diff --git a/DEPS b/DEPS
index e5f89bff..46909c1 100644
--- a/DEPS
+++ b/DEPS
@@ -160,11 +160,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': 'ba57aa2b4a6064237a9a3e59a3eb9d0df8318a10',
+  'skia_revision': 'ea07123fedc7ecfe6bc74fd94ad6c2cec34c211a',
   # 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': '40ba72e3195abfa78729d496ed5cc951d5941174',
+  'v8_revision': '2334bc12561ce4921dad83678b344df2abf1c775',
   # 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.
@@ -172,11 +172,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': '3c6b2e1613c844f7265895de5611c0528641d501',
+  'angle_revision': '87b106a0944e5bc83bbee8de95a49c4be3de3825',
   # 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': 'd6dc4b7e45334f493f49cf70114e939f7fc25fde',
+  'swiftshader_revision': '793707262d8d92d6a96632d725dd6525227abf27',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -211,7 +211,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '734d60f63cfa27f9b337ddbb80adb9edd60475bf',
+  'freetype_revision': '7d1d3b9a0e9310376a559ad2eac8a9dc4c60ce59',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling HarfBuzz
   # and whatever else without interference from each other.
@@ -223,7 +223,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '1078fdda6aea72aac670e606809de1bbd77c4744',
+  'catapult_revision': '83d2edf28b9db0d0d8a3dd59d997e6832b83b7e0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -295,11 +295,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '25cc723823e3aa9bf6d7edfcd308bb82ab05b0fc',
+  'dawn_revision': 'f19c328b5ba5fb3aaf02e50b5fc11dab93b098b7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': 'c8c02a5f788cddb615e5f0fb2636dea67cad2396',
+  'quiche_revision': '3de7170b31c5a4a47498ead0e30271e4d00454b2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -840,7 +840,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '81a5ea6cf04a9593b4242ed56313ba121255b5b1',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '0521d7ebd8730904da0ef0ca47042a4a46cb5a1a',
       'condition': 'checkout_linux',
   },
 
@@ -865,7 +865,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '31f187e5c0d7d13d71348dd9bd6a891bf3643ba6',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '0e5fff1a88619bd1eb46df2011075a6e8ca16310',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1406,7 +1406,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'abaae129d9a0c6e1e092067e0b105475df43352e',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'a2dae38ee7729ec1d6fcb7d22b7a597c627ad81a',
+    Var('webrtc_git') + '/src.git' + '@' + '4869bd6309bbe2e9e038451e9549f5a9d12e4a38',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1447,7 +1447,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@271fe862460bdbe49684afa108f0bb710fcd4130',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@4857c934a09c2c4e035ed5caf03120beafbe301c',
     'condition': 'checkout_src_internal',
   },
 
@@ -1455,7 +1455,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/android_deps/libs/com_google_android_play_core_verification',
-              'version': '4hAx_nDBLvvQHfRMqLEB4ot_jERn_Wpuwxuap5Ii4PsC',
+              'version': '0z_scbnHbcGHs7fem6R48_Cfgf7n-46yI7DEXkpkZcwC',
           },
       ],
       'condition': 'checkout_android',
diff --git a/android_webview/java/src/org/chromium/android_webview/AwAutofillProvider.java b/android_webview/java/src/org/chromium/android_webview/AwAutofillProvider.java
index c1ee883b..75a18a9 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwAutofillProvider.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwAutofillProvider.java
@@ -306,6 +306,7 @@
         }
         mAutofillManager.notifyNewSessionStarted();
         Rect absBound = transformToWindowBounds(new RectF(x, y, x + width, y + height));
+        if (mRequest != null) notifyViewExitBeforeDestoryRequest();
         mRequest = new AutofillRequest(formData, new FocusField((short) focus, absBound));
         int virtualId = mRequest.getVirtualId((short) focus);
         mAutofillManager.notifyVirtualViewEntered(mContainerView, virtualId, absBound);
@@ -383,6 +384,15 @@
                 focusOnForm, focusField, x, y, width, height, false /*causedByValueChange*/);
     }
 
+    private void notifyViewExitBeforeDestoryRequest() {
+        if (mRequest == null) return;
+        FocusField focusField = mRequest.getFocusField();
+        if (focusField == null) return;
+        mAutofillManager.notifyVirtualViewExited(
+                mContainerView, mRequest.getVirtualId(focusField.fieldIndex));
+        mRequest.setFocusField(null);
+    }
+
     private void onFocusChangedImpl(boolean focusOnForm, int focusField, float x, float y,
             float width, float height, boolean causedByValueChange) {
         // Check focusField inside short value?
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwAutofillTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwAutofillTest.java
index e4f4dc5e..a356c15 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwAutofillTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwAutofillTest.java
@@ -1100,11 +1100,10 @@
                             AUTOFILL_VIEW_ENTERED, AUTOFILL_VALUE_CHANGED});
             // Move to form2, cancel() should be called again.
             executeJavaScriptAndWaitForResult("document.getElementById('text2').select();");
-            cnt += waitForCallbackAndVerifyTypes(cnt, new Integer[] {AUTOFILL_VIEW_EXITED});
             dispatchDownAndUpKeyEvents(KeyEvent.KEYCODE_A);
             waitForCallbackAndVerifyTypes(cnt,
-                    new Integer[] {AUTOFILL_CANCEL, AUTOFILL_VIEW_ENTERED, AUTOFILL_VIEW_EXITED,
-                            AUTOFILL_VIEW_ENTERED, AUTOFILL_VALUE_CHANGED});
+                    new Integer[] {AUTOFILL_VIEW_EXITED, AUTOFILL_CANCEL, AUTOFILL_VIEW_ENTERED,
+                            AUTOFILL_VIEW_EXITED, AUTOFILL_VIEW_ENTERED, AUTOFILL_VALUE_CHANGED});
         } finally {
             webServer.shutdown();
         }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 2852b14..eb682c4 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1999,7 +1999,6 @@
     "//components/sync_preferences:test_support",
     "//components/user_manager",
     "//components/user_manager:test_support",
-    "//device/base",
     "//device/bluetooth",
     "//device/bluetooth:mocks",
     "//extensions/common:common_constants",
diff --git a/ash/DEPS b/ash/DEPS
index af5500c..672b469 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -1,5 +1,4 @@
 include_rules = [
-  "+device/base/features.h",
   "+device/bluetooth",
   "+cc/debug",
   "+cc/output",
diff --git a/ash/events/select_to_speak_event_handler.cc b/ash/events/select_to_speak_event_handler.cc
index 35e4561..47b7c65 100644
--- a/ash/events/select_to_speak_event_handler.cc
+++ b/ash/events/select_to_speak_event_handler.cc
@@ -42,7 +42,7 @@
     // up event by not resetting the state to INACTIVE.
     if (state_ != MOUSE_RELEASED)
       state_ = INACTIVE;
-    touch_id_ = ui::PointerDetails::kUnknownPointerId;
+    touch_id_ = ui::kPointerIdUnknown;
     touch_type_ = ui::EventPointerType::POINTER_TYPE_UNKNOWN;
   }
 }
@@ -158,7 +158,7 @@
   // On a touch-down event, if selection was requested, we begin capturing
   // touch events.
   if (event->type() == ui::ET_TOUCH_PRESSED && state_ == SELECTION_REQUESTED &&
-      touch_id_ == ui::PointerDetails::kUnknownPointerId) {
+      touch_id_ == ui::kPointerIdUnknown) {
     state_ = CAPTURING_TOUCH_ONLY;
     touch_id_ = event->pointer_details().id;
     touch_type_ = event->pointer_details().pointer_type;
@@ -177,7 +177,7 @@
   if (event->type() == ui::ET_TOUCH_RELEASED &&
       state_ == CAPTURING_TOUCH_ONLY) {
     state_ = INACTIVE;
-    touch_id_ = ui::PointerDetails::kUnknownPointerId;
+    touch_id_ = ui::kPointerIdUnknown;
     touch_type_ = ui::EventPointerType::POINTER_TYPE_UNKNOWN;
   }
 
diff --git a/ash/events/select_to_speak_event_handler.h b/ash/events/select_to_speak_event_handler.h
index b194e4b..9a9204ffb 100644
--- a/ash/events/select_to_speak_event_handler.h
+++ b/ash/events/select_to_speak_event_handler.h
@@ -97,7 +97,7 @@
 
   State state_ = INACTIVE;
 
-  ui::PointerId touch_id_ = ui::PointerDetails::kUnknownPointerId;
+  ui::PointerId touch_id_ = ui::kPointerIdUnknown;
 
   ui::EventPointerType touch_type_ = ui::EventPointerType::POINTER_TYPE_UNKNOWN;
 
diff --git a/ash/keyboard/ui/container_floating_behavior.cc b/ash/keyboard/ui/container_floating_behavior.cc
index 590195d..9815a145 100644
--- a/ash/keyboard/ui/container_floating_behavior.cc
+++ b/ash/keyboard/ui/container_floating_behavior.cc
@@ -188,7 +188,7 @@
   if (keyboard_bounds_in_screen.height() <= 0)
     return false;
 
-  ui::PointerId pointer_id = ui::MouseEvent::kMousePointerId;
+  ui::PointerId pointer_id = ui::kPointerIdMouse;
   if (event.IsTouchEvent()) {
     const ui::TouchEvent* te = event.AsTouchEvent();
     pointer_id = te->pointer_details().id;
diff --git a/ash/system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc b/ash/system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc
index 1936b7a..51f65d9d 100644
--- a/ash/system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc
+++ b/ash/system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc
@@ -12,9 +12,8 @@
 #include "base/bind_helpers.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
+#include "chromeos/constants/chromeos_switches.h"
 #include "dbus/object_path.h"
-#include "device/base/features.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
 #include "device/bluetooth/dbus/fake_bluetooth_adapter_client.h"
 #include "device/bluetooth/dbus/fake_bluetooth_device_client.h"
@@ -260,9 +259,8 @@
 // Tests the Bluetooth device list when UnfilteredBluetoothDevices feature is
 // enabled.
 TEST_F(TrayBluetoothHelperLegacyTest, UnfilteredBluetoothDevices) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitFromCommandLine(device::kUnfilteredBluetoothDevices.name,
-                                   "");
+  base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+  cmd_line->AppendSwitch(chromeos::switches::kUnfilteredBluetoothDevices);
 
   // Set Bluetooth discovery simulation delay to 0 so the test doesn't have to
   // wait or use timers.
diff --git a/base/task/post_job.cc b/base/task/post_job.cc
index df0a981..e57b32e 100644
--- a/base/task/post_job.cc
+++ b/base/task/post_job.cc
@@ -5,16 +5,49 @@
 #include "base/task/post_job.h"
 
 #include "base/task/thread_pool/job_task_source.h"
+#include "base/task/thread_pool/pooled_task_runner_delegate.h"
 
 namespace base {
 namespace experimental {
 
-JobDelegate::JobDelegate(internal::JobTaskSource* task_source)
-    : task_source_(task_source) {}
+JobDelegate::JobDelegate(
+    internal::JobTaskSource* task_source,
+    internal::PooledTaskRunnerDelegate* pooled_task_runner_delegate)
+    : task_source_(task_source),
+      pooled_task_runner_delegate_(pooled_task_runner_delegate) {
+  DCHECK(task_source_);
+  DCHECK(pooled_task_runner_delegate_);
+#if DCHECK_IS_ON()
+  recorded_increase_version_ = task_source_->GetConcurrencyIncreaseVersion();
+  // Record max concurrency before running the worker task.
+  recorded_max_concurrency_ = task_source_->GetMaxConcurrency();
+#endif  // DCHECK_IS_ON()
+}
+
+JobDelegate::~JobDelegate() {
+#if DCHECK_IS_ON()
+  // When ShouldYield() returns false, the worker task is expected to do
+  // work before returning.
+  size_t expected_max_concurrency = recorded_max_concurrency_;
+  if (!last_should_yield_ && expected_max_concurrency > 0)
+    --expected_max_concurrency;
+  AssertExpectedConcurrency(expected_max_concurrency);
+#endif  // DCHECK_IS_ON()
+}
 
 bool JobDelegate::ShouldYield() {
-  // TODO(crbug.com/839091): Implement this.
-  return false;
+#if DCHECK_IS_ON()
+  // ShouldYield() shouldn't be called again after returning true.
+  DCHECK(!last_should_yield_);
+  AssertExpectedConcurrency(recorded_max_concurrency_);
+#endif  // DCHECK_IS_ON()
+  const bool should_yield =
+      pooled_task_runner_delegate_->ShouldYield(task_source_);
+
+#if DCHECK_IS_ON()
+  last_should_yield_ = should_yield;
+#endif  // DCHECK_IS_ON()
+  return should_yield;
 }
 
 void JobDelegate::YieldIfNeeded() {
@@ -25,5 +58,45 @@
   task_source_->NotifyConcurrencyIncrease();
 }
 
+void JobDelegate::AssertExpectedConcurrency(size_t expected_max_concurrency) {
+  // In dcheck builds, verify that max concurrency falls in one of the following
+  // cases:
+  // 1) max concurrency behaves normally and is below or equals the expected
+  //    value.
+  // 2) max concurrency increased above the expected value, which implies
+  //    there are new work items that the associated worker task didn't see and
+  //    NotifyConcurrencyIncrease() should be called to adjust the number of
+  //    worker.
+  //   a) NotifyConcurrencyIncrease() was already called and the recorded
+  //      concurrency version is out of date, i.e. less than the actual version.
+  //   b) NotifyConcurrencyIncrease() has not yet been called, in which case the
+  //      function waits for an imminent increase of the concurrency version.
+  // This prevent ill-formed GetMaxConcurrency() implementations that:
+  // - Don't decrease with the number of remaining work items.
+  // - Don't return an up-to-date value.
+#if DCHECK_IS_ON()
+  // Case 1:
+  const size_t max_concurrency = task_source_->GetMaxConcurrency();
+  if (max_concurrency <= expected_max_concurrency)
+    return;
+
+  // Case 2a:
+  const size_t actual_version = task_source_->GetConcurrencyIncreaseVersion();
+  DCHECK_LE(recorded_increase_version_, actual_version);
+  if (recorded_increase_version_ < actual_version)
+    return;
+
+  // Case 2b:
+  const bool updated = task_source_->WaitForConcurrencyIncreaseUpdate(
+      recorded_increase_version_);
+  DCHECK(updated)
+      << "Value returned by |max_concurrency_callback| is expected to "
+         "decrease, unless NotifyConcurrencyIncrease() is called.";
+
+  recorded_increase_version_ = task_source_->GetConcurrencyIncreaseVersion();
+  recorded_max_concurrency_ = task_source_->GetMaxConcurrency();
+#endif  // DCHECK_IS_ON()
+}
+
 }  // namespace experimental
 }  // namespace base
\ No newline at end of file
diff --git a/base/task/post_job.h b/base/task/post_job.h
index 94ab75c..de6b2d6 100644
--- a/base/task/post_job.h
+++ b/base/task/post_job.h
@@ -6,12 +6,14 @@
 #define BASE_TASK_POST_JOB_H_
 
 #include "base/base_export.h"
+#include "base/logging.h"
 #include "base/macros.h"
 #include "base/time/time.h"
 
 namespace base {
 namespace internal {
 class JobTaskSource;
+class PooledTaskRunnerDelegate;
 }
 namespace experimental {
 
@@ -19,7 +21,13 @@
 // communicate with the scheduler.
 class BASE_EXPORT JobDelegate {
  public:
-  explicit JobDelegate(internal::JobTaskSource* task_source);
+  // A JobDelegate is instantiated for each worker task that is run.
+  // |task_source| is the task source whose worker task is running with this
+  // delegate and |pooled_task_runner_delegate| provides communication with the
+  // thread pool.
+  JobDelegate(internal::JobTaskSource* task_source,
+              internal::PooledTaskRunnerDelegate* pooled_task_runner_delegate);
+  ~JobDelegate();
 
   // Returns true if this thread should return from the worker task on the
   // current thread ASAP. Workers should periodically invoke ShouldYield (or
@@ -38,7 +46,25 @@
   void NotifyConcurrencyIncrease();
 
  private:
+  // Verifies that either max concurrency is lower or equal to
+  // |expected_max_concurrency|, or there is an increase version update
+  // triggered by NotifyConcurrencyIncrease().
+  void AssertExpectedConcurrency(size_t expected_max_concurrency);
+
   internal::JobTaskSource* const task_source_;
+  internal::PooledTaskRunnerDelegate* const pooled_task_runner_delegate_;
+
+#if DCHECK_IS_ON()
+  // Used in AssertExpectedConcurrency(), see that method's impl for details.
+  // Value of max concurrency recorded before running the worker task.
+  size_t recorded_max_concurrency_;
+  // Value of the increase version recorded before running the worker task.
+  size_t recorded_increase_version_;
+  // Value returned by the last call to ShouldYield().
+  bool last_should_yield_ = false;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(JobDelegate);
 };
 
 }  // namespace experimental
diff --git a/base/task/task_traits.h b/base/task/task_traits.h
index 16efae6..c550716 100644
--- a/base/task/task_traits.h
+++ b/base/task/task_traits.h
@@ -255,7 +255,21 @@
         may_block_(trait_helpers::HasTrait<MayBlock, ArgTypes...>()),
         with_base_sync_primitives_(
             trait_helpers::HasTrait<WithBaseSyncPrimitives, ArgTypes...>()),
-        use_thread_pool_(trait_helpers::HasTrait<ThreadPool, ArgTypes...>()) {}
+        use_thread_pool_(trait_helpers::HasTrait<ThreadPool, ArgTypes...>()) {
+#if !defined(OS_CHROMEOS)
+    // TODO(https://crbug.com/968047): Enable on Chrome OS in a separate CL.
+    // Initially disabled to keep the rule enforced on other platforms if the
+    // CL that enables it on Chrome OS is reverted.
+    constexpr bool has_thread_pool =
+        trait_helpers::HasTrait<ThreadPool, ArgTypes...>();
+    constexpr bool has_extension =
+        !trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value;
+    static_assert(
+        has_thread_pool ^ has_extension,
+        "Traits must explicitly specify a destination (e.g. ThreadPool or a "
+        "named thread like BrowserThread)");
+#endif
+  }
 
   constexpr TaskTraits(const TaskTraits& other) = default;
   TaskTraits& operator=(const TaskTraits& other) = default;
diff --git a/base/task/thread_pool/job_task_source.cc b/base/task/thread_pool/job_task_source.cc
index 25ff987..c6e5f97 100644
--- a/base/task/thread_pool/job_task_source.cc
+++ b/base/task/thread_pool/job_task_source.cc
@@ -11,7 +11,10 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/task/task_features.h"
+#include "base/task/thread_pool/pooled_task_runner_delegate.h"
+#include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
+#include "base/time/time_override.h"
 
 namespace base {
 namespace internal {
@@ -20,7 +23,8 @@
     const Location& from_here,
     const TaskTraits& traits,
     RepeatingCallback<void(experimental::JobDelegate*)> worker_task,
-    RepeatingCallback<size_t()> max_concurrency_callback)
+    RepeatingCallback<size_t()> max_concurrency_callback,
+    PooledTaskRunnerDelegate* delegate)
     : TaskSource(traits, nullptr, TaskSourceExecutionMode::kJob),
       from_here_(from_here),
       max_concurrency_callback_(std::move(max_concurrency_callback)),
@@ -29,14 +33,15 @@
              const RepeatingCallback<void(experimental::JobDelegate*)>&
                  worker_task) {
             // Each worker task has its own delegate with associated state.
-            // TODO(crbug.com/839091): Implement assertions on max concurrency
-            // increase in the delegate.
-            experimental::JobDelegate job_delegate{self};
+            experimental::JobDelegate job_delegate{self, self->delegate_};
             worker_task.Run(&job_delegate);
           },
           base::Unretained(this),
           std::move(worker_task))),
-      queue_time_(TimeTicks::Now()) {}
+      queue_time_(TimeTicks::Now()),
+      delegate_(delegate) {
+  DCHECK(delegate_);
+}
 
 JobTaskSource::~JobTaskSource() {
 #if DCHECK_IS_ON()
@@ -100,16 +105,52 @@
 }
 
 void JobTaskSource::NotifyConcurrencyIncrease() {
-  // TODO(839091): Implement this.
+#if DCHECK_IS_ON()
+  {
+    AutoLock auto_lock(version_lock_);
+    ++increase_version_;
+    version_condition_.Broadcast();
+  }
+#endif  // DCHECK_IS_ON()
+  // Make sure the task source is in the queue if not already.
+  // Caveat: it's possible but unlikely that the task source has already reached
+  // its intended concurrency and doesn't need to be enqueued if there
+  // previously were too many worker. For simplicity, the task source is always
+  // enqueued and will get discarded if already saturated when it is popped from
+  // the priority queue.
+  delegate_->EnqueueJobTaskSource(this);
 }
 
 size_t JobTaskSource::GetMaxConcurrency() const {
   return max_concurrency_callback_.Run();
 }
 
+#if DCHECK_IS_ON()
+
+size_t JobTaskSource::GetConcurrencyIncreaseVersion() const {
+  AutoLock auto_lock(version_lock_);
+  return increase_version_;
+}
+
+bool JobTaskSource::WaitForConcurrencyIncreaseUpdate(size_t recorded_version) {
+  AutoLock auto_lock(version_lock_);
+  constexpr TimeDelta timeout = TimeDelta::FromSeconds(1);
+  const base::TimeTicks start_time = subtle::TimeTicksNowIgnoringOverride();
+  do {
+    DCHECK_LE(recorded_version, increase_version_);
+    if (recorded_version != increase_version_)
+      return true;
+    // Waiting is acceptable because it is in DCHECK-only code.
+    ScopedAllowBaseSyncPrimitivesOutsideBlockingScope
+        allow_base_sync_primitives;
+    version_condition_.TimedWait(timeout);
+  } while (subtle::TimeTicksNowIgnoringOverride() - start_time < timeout);
+  return false;
+}
+
+#endif  // DCHECK_IS_ON()
+
 Optional<Task> JobTaskSource::TakeTask(TaskSource::Transaction* transaction) {
-  // JobTaskSource members are not lock-protected so no need to acquire a lock
-  // if |transaction| is nullptr.
   DCHECK_GT(worker_count_.load(std::memory_order_relaxed), 0U);
   DCHECK(worker_task_);
   return base::make_optional<Task>(from_here_, worker_task_, TimeDelta());
diff --git a/base/task/thread_pool/job_task_source.h b/base/task/thread_pool/job_task_source.h
index 58000b0..ddaca19 100644
--- a/base/task/thread_pool/job_task_source.h
+++ b/base/task/thread_pool/job_task_source.h
@@ -14,6 +14,8 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
 #include "base/task/post_job.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool/sequence_sort_key.h"
@@ -23,6 +25,8 @@
 namespace base {
 namespace internal {
 
+class PooledTaskRunnerDelegate;
+
 // A JobTaskSource generates many Tasks from a single RepeatingClosure.
 //
 // Derived classes control the intended concurrency with GetMaxConcurrency().
@@ -31,7 +35,8 @@
   JobTaskSource(const Location& from_here,
                 const TaskTraits& traits,
                 RepeatingCallback<void(experimental::JobDelegate*)> worker_task,
-                RepeatingCallback<size_t()> max_concurrency_callback);
+                RepeatingCallback<size_t()> max_concurrency_callback,
+                PooledTaskRunnerDelegate* delegate);
 
   // Notifies this task source that max concurrency was increased, and the
   // number of worker should be adjusted.
@@ -41,17 +46,23 @@
   ExecutionEnvironment GetExecutionEnvironment() override;
   size_t GetRemainingConcurrency() const override;
 
+  // Returns the maximum number of tasks from this TaskSource that can run
+  // concurrently.
+  size_t GetMaxConcurrency() const;
+
+#if DCHECK_IS_ON()
+  size_t GetConcurrencyIncreaseVersion() const;
+  // Returns true if the concurrency version was updated above
+  // |recorded_version|, or false on timeout.
+  bool WaitForConcurrencyIncreaseUpdate(size_t recorded_version);
+#endif  // DCHECK_IS_ON()
+
  private:
   static constexpr size_t kInvalidWorkerCount =
       std::numeric_limits<size_t>::max();
 
   ~JobTaskSource() override;
 
-  // Returns the maximum number of tasks from this TaskSource that can run
-  // concurrently. The implementation can only return values lower than or equal
-  // to previously returned values.
-  size_t GetMaxConcurrency() const;
-
   // TaskSource:
   RunStatus WillRunTask() override;
   Optional<Task> TakeTask(TaskSource::Transaction* transaction) override;
@@ -67,6 +78,16 @@
   base::RepeatingCallback<size_t()> max_concurrency_callback_;
   base::RepeatingClosure worker_task_;
   const TimeTicks queue_time_;
+  PooledTaskRunnerDelegate* delegate_;
+
+#if DCHECK_IS_ON()
+  // Synchronizes accesses to |increase_version_|.
+  mutable Lock version_lock_;
+  // Signaled whenever increase_version_ is updated.
+  ConditionVariable version_condition_{&version_lock_};
+  // Incremented every time max concurrency is increased.
+  size_t increase_version_ GUARDED_BY(version_lock_) = 0;
+#endif  // DCHECK_IS_ON()
 
   DISALLOW_COPY_AND_ASSIGN(JobTaskSource);
 };
diff --git a/base/task/thread_pool/job_task_source_unittest.cc b/base/task/thread_pool/job_task_source_unittest.cc
index 0483011..8d911a5 100644
--- a/base/task/thread_pool/job_task_source_unittest.cc
+++ b/base/task/thread_pool/job_task_source_unittest.cc
@@ -8,20 +8,45 @@
 
 #include "base/bind_helpers.h"
 #include "base/memory/ptr_util.h"
+#include "base/task/thread_pool/pooled_task_runner_delegate.h"
 #include "base/task/thread_pool/test_utils.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/gtest_util.h"
+#include "base/test/test_timeouts.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::testing::_;
+using ::testing::Return;
+
 namespace base {
 namespace internal {
 
+class MockPooledTaskRunnerDelegate : public PooledTaskRunnerDelegate {
+ public:
+  MOCK_METHOD2(PostTaskWithSequence,
+               bool(Task task, scoped_refptr<Sequence> sequence));
+  MOCK_CONST_METHOD1(ShouldYield, bool(TaskSource* task_source));
+  MOCK_METHOD1(EnqueueJobTaskSource,
+               bool(scoped_refptr<JobTaskSource> task_source));
+  MOCK_CONST_METHOD1(IsRunningPoolWithTraits, bool(const TaskTraits& traits));
+  MOCK_METHOD2(UpdatePriority,
+               void(scoped_refptr<TaskSource> task_source,
+                    TaskPriority priority));
+};
+
+class ThreadPoolJobTaskSourceTest : public testing::Test {
+ protected:
+  testing::StrictMock<MockPooledTaskRunnerDelegate>
+      pooled_task_runner_delegate_;
+};
+
 // Verifies the normal flow of running 2 tasks one after the other.
-TEST(ThreadPoolJobTaskSourceTest, RunTasks) {
+TEST_F(ThreadPoolJobTaskSourceTest, RunTasks) {
   auto job_task = base::MakeRefCounted<test::MockJobTask>(
       DoNothing(), /* num_tasks_to_run */ 2);
   scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
-      FROM_HERE, {ThreadPool(), TaskPriority::BEST_EFFORT});
+      FROM_HERE, ThreadPool(), &pooled_task_runner_delegate_);
   auto registered_task_source =
       RegisteredTaskSource::CreateForTesting(task_source);
 
@@ -55,11 +80,11 @@
 
 // Verifies that a job task source doesn't allow any new RunStatus after Clear()
 // is called.
-TEST(ThreadPoolJobTaskSourceTest, Clear) {
+TEST_F(ThreadPoolJobTaskSourceTest, Clear) {
   auto job_task = base::MakeRefCounted<test::MockJobTask>(
       DoNothing(), /* num_tasks_to_run */ 5);
   scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
-      FROM_HERE, {ThreadPool(), TaskPriority::BEST_EFFORT});
+      FROM_HERE, ThreadPool(), &pooled_task_runner_delegate_);
 
   EXPECT_EQ(5U, task_source->GetRemainingConcurrency());
   auto registered_task_source_a =
@@ -116,11 +141,11 @@
 }
 
 // Verifies that multiple tasks can run in parallel up to |max_concurrency|.
-TEST(ThreadPoolJobTaskSourceTest, RunTasksInParallel) {
+TEST_F(ThreadPoolJobTaskSourceTest, RunTasksInParallel) {
   auto job_task = base::MakeRefCounted<test::MockJobTask>(
       DoNothing(), /* num_tasks_to_run */ 2);
   scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
-      FROM_HERE, {ThreadPool(), TaskPriority::BEST_EFFORT});
+      FROM_HERE, ThreadPool(), &pooled_task_runner_delegate_);
 
   auto registered_task_source_a =
       RegisteredTaskSource::CreateForTesting(task_source);
@@ -158,12 +183,138 @@
   EXPECT_FALSE(registered_task_source_c.DidProcessTask());
 }
 
-TEST(ThreadPoolJobTaskSourceTest, InvalidTakeTask) {
+// Verifies that a call to NotifyConcurrencyIncrease() calls the delegate
+// and allows to run additional tasks.
+TEST_F(ThreadPoolJobTaskSourceTest, NotifyConcurrencyIncrease) {
+  auto job_task = base::MakeRefCounted<test::MockJobTask>(
+      DoNothing(), /* num_tasks_to_run */ 1);
+  scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
+      FROM_HERE, ThreadPool(), &pooled_task_runner_delegate_);
+
+  auto registered_task_source_a =
+      RegisteredTaskSource::CreateForTesting(task_source);
+  EXPECT_EQ(registered_task_source_a.WillRunTask(),
+            TaskSource::RunStatus::kAllowedSaturated);
+  auto task_a = registered_task_source_a.TakeTask();
+  EXPECT_EQ(RegisteredTaskSource::CreateForTesting(task_source).WillRunTask(),
+            TaskSource::RunStatus::kDisallowed);
+
+  job_task->SetNumTasksToRun(2);
+  EXPECT_CALL(pooled_task_runner_delegate_, EnqueueJobTaskSource(_)).Times(1);
+  task_source->NotifyConcurrencyIncrease();
+
+  auto registered_task_source_b =
+      RegisteredTaskSource::CreateForTesting(task_source);
+  // WillRunTask() should return a valid RunStatus because max concurrency was
+  // increased to 2.
+  EXPECT_EQ(registered_task_source_b.WillRunTask(),
+            TaskSource::RunStatus::kAllowedSaturated);
+  auto task_b = registered_task_source_b.TakeTask();
+  EXPECT_EQ(RegisteredTaskSource::CreateForTesting(task_source).WillRunTask(),
+            TaskSource::RunStatus::kDisallowed);
+
+  std::move(task_a->task).Run();
+  EXPECT_FALSE(registered_task_source_a.DidProcessTask());
+
+  std::move(task_b->task).Run();
+  EXPECT_FALSE(registered_task_source_b.DidProcessTask());
+}
+
+// Verifies that ShouldYield() calls the delegate.
+TEST_F(ThreadPoolJobTaskSourceTest, ShouldYield) {
+  auto job_task = base::MakeRefCounted<test::MockJobTask>(
+      BindLambdaForTesting([](experimental::JobDelegate* delegate) {
+        // As set up below, the mock will return false once and true the second
+        // time.
+        EXPECT_FALSE(delegate->ShouldYield());
+        EXPECT_TRUE(delegate->ShouldYield());
+      }),
+      /* num_tasks_to_run */ 1);
+  scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
+      FROM_HERE, ThreadPool(), &pooled_task_runner_delegate_);
+
+  auto registered_task_source =
+      RegisteredTaskSource::CreateForTesting(task_source);
+  ASSERT_EQ(registered_task_source.WillRunTask(),
+            TaskSource::RunStatus::kAllowedSaturated);
+
+  auto task = registered_task_source.TakeTask();
+
+  EXPECT_CALL(pooled_task_runner_delegate_, ShouldYield(_))
+      .Times(2)
+      .WillOnce(Return(false))
+      .WillOnce(Return(true));
+
+  std::move(task->task).Run();
+  EXPECT_FALSE(registered_task_source.DidProcessTask());
+}
+
+// Verifies that max concurrency is allowed to stagnate when ShouldYield returns
+// true.
+TEST_F(ThreadPoolJobTaskSourceTest, MaxConcurrencyStagnateIfShouldYield) {
+  scoped_refptr<JobTaskSource> task_source =
+      base::MakeRefCounted<JobTaskSource>(
+          FROM_HERE, ThreadPool(),
+          BindRepeating([](experimental::JobDelegate* delegate) {
+            // As set up below, the mock will return true once.
+            ASSERT_TRUE(delegate->ShouldYield());
+          }),
+          BindRepeating([]() -> size_t {
+            return 1;  // max concurrency is always 1.
+          }),
+          &pooled_task_runner_delegate_);
+
+  EXPECT_CALL(pooled_task_runner_delegate_, ShouldYield(_))
+      .WillOnce(Return(true));
+
+  auto registered_task_source =
+      RegisteredTaskSource::CreateForTesting(task_source);
+  ASSERT_EQ(registered_task_source.WillRunTask(),
+            TaskSource::RunStatus::kAllowedSaturated);
+  auto task = registered_task_source.TakeTask();
+
+  // Running the task should not fail even though max concurrency remained at 1,
+  // since ShouldYield() returned true.
+  std::move(task->task).Run();
+  registered_task_source.DidProcessTask();
+}
+
+// Verifies that a missing call to NotifyConcurrencyIncrease() causes a DCHECK
+// death after a timeout.
+TEST_F(ThreadPoolJobTaskSourceTest, InvalidConcurrency) {
+  testing::FLAGS_gtest_death_test_style = "threadsafe";
+
+  scoped_refptr<test::MockJobTask> job_task;
+  job_task = base::MakeRefCounted<test::MockJobTask>(
+      BindLambdaForTesting([&](experimental::JobDelegate* delegate) {
+        EXPECT_FALSE(delegate->ShouldYield());
+        job_task->SetNumTasksToRun(2);
+        EXPECT_FALSE(delegate->ShouldYield());
+
+        // After returning, a DCHECK should trigger because we never called
+        // NotifyConcurrencyIncrease().
+      }),
+      /* num_tasks_to_run */ 1);
+  scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
+      FROM_HERE, ThreadPool(), &pooled_task_runner_delegate_);
+
+  auto registered_task_source =
+      RegisteredTaskSource::CreateForTesting(task_source);
+  ASSERT_EQ(registered_task_source.WillRunTask(),
+            TaskSource::RunStatus::kAllowedSaturated);
+  auto task = registered_task_source.TakeTask();
+
+  EXPECT_DCHECK_DEATH(std::move(task->task).Run());
+
+  registered_task_source.DidProcessTask();
+}
+
+TEST_F(ThreadPoolJobTaskSourceTest, InvalidTakeTask) {
   auto job_task =
       base::MakeRefCounted<test::MockJobTask>(DoNothing(),
                                               /* num_tasks_to_run */ 1);
   scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
-      FROM_HERE, {ThreadPool(), TaskPriority::BEST_EFFORT});
+      FROM_HERE, ThreadPool(), &pooled_task_runner_delegate_);
 
   auto registered_task_source_a =
       RegisteredTaskSource::CreateForTesting(task_source);
@@ -182,12 +333,12 @@
   registered_task_source_a.DidProcessTask();
 }
 
-TEST(ThreadPoolJobTaskSourceTest, InvalidDidProcessTask) {
+TEST_F(ThreadPoolJobTaskSourceTest, InvalidDidProcessTask) {
   auto job_task =
       base::MakeRefCounted<test::MockJobTask>(DoNothing(),
                                               /* num_tasks_to_run */ 1);
   scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
-      FROM_HERE, {ThreadPool(), TaskPriority::BEST_EFFORT});
+      FROM_HERE, ThreadPool(), &pooled_task_runner_delegate_);
 
   auto registered_task_source =
       RegisteredTaskSource::CreateForTesting(task_source);
diff --git a/base/task/thread_pool/pooled_task_runner_delegate.h b/base/task/thread_pool/pooled_task_runner_delegate.h
index 46dbe39..f6687bd 100644
--- a/base/task/thread_pool/pooled_task_runner_delegate.h
+++ b/base/task/thread_pool/pooled_task_runner_delegate.h
@@ -27,6 +27,11 @@
   // outlives the ThreadPoolInstance that created it.
   static bool Exists();
 
+  // Returns true if |task_source| currently running must return ASAP.
+  // Thread-safe but may return an outdated result (if a task unnecessarily
+  // yields due to this, it will simply be re-scheduled).
+  virtual bool ShouldYield(TaskSource* task_source) const = 0;
+
   // Invoked when a |task| is posted to the PooledParallelTaskRunner or
   // PooledSequencedTaskRunner. The implementation must post |task| to
   // |sequence| within the appropriate priority queue, depending on |sequence|
@@ -36,7 +41,8 @@
 
   // Invoked when a task is posted as a Job. The implementation must add
   // |task_source| to the appropriate priority queue, depending on |task_source|
-  // traits. Returns true if task source was successfully enqueued.
+  // traits, if it's not there already. Returns true if task source was
+  // successfully enqueued or was already enqueued.
   virtual bool EnqueueJobTaskSource(
       scoped_refptr<JobTaskSource> task_source) = 0;
 
diff --git a/base/task/thread_pool/task_source.cc b/base/task/thread_pool/task_source.cc
index 1cf36d5..271988c 100644
--- a/base/task/thread_pool/task_source.cc
+++ b/base/task/thread_pool/task_source.cc
@@ -40,6 +40,8 @@
   if (FeatureList::IsEnabled(kAllTasksUserBlocking))
     return;
   task_source_->traits_.UpdatePriority(priority);
+  task_source_->priority_racy_.store(task_source_->traits_.priority(),
+                                     std::memory_order_relaxed);
 }
 
 void TaskSource::SetHeapHandle(const HeapHandle& handle) {
@@ -54,6 +56,7 @@
                        TaskRunner* task_runner,
                        TaskSourceExecutionMode execution_mode)
     : traits_(traits),
+      priority_racy_(traits.priority()),
       task_runner_(task_runner),
       execution_mode_(execution_mode) {
   DCHECK(task_runner_ ||
diff --git a/base/task/thread_pool/task_source.h b/base/task/thread_pool/task_source.h
index df8bebe..e56b42e 100644
--- a/base/task/thread_pool/task_source.h
+++ b/base/task/thread_pool/task_source.h
@@ -160,6 +160,14 @@
   TaskShutdownBehavior shutdown_behavior() const {
     return traits_.shutdown_behavior();
   }
+  // Returns a racy priority of the TaskSource. Can be accessed without a
+  // Transaction but may return an outdated result.
+  TaskPriority priority_racy() const {
+    return priority_racy_.load(std::memory_order_relaxed);
+  }
+  // Returns the thread policy of the TaskSource. Can be accessed without a
+  // Transaction because it is never mutated.
+  ThreadPolicy thread_policy() const { return traits_.thread_policy(); }
 
   // A reference to TaskRunner is only retained between PushTask() and when
   // DidProcessTask() returns false, guaranteeing it is safe to dereference this
@@ -193,6 +201,9 @@
   // The TaskTraits of all Tasks in the TaskSource.
   TaskTraits traits_;
 
+  // The cached priority for atomic access.
+  std::atomic<TaskPriority> priority_racy_;
+
   // Synchronizes access to all members.
   mutable CheckedLock lock_{UniversalPredecessor()};
 
diff --git a/base/task/thread_pool/test_utils.cc b/base/task/thread_pool/test_utils.cc
index 3d7a2bb..02ae793 100644
--- a/base/task/thread_pool/test_utils.cc
+++ b/base/task/thread_pool/test_utils.cc
@@ -56,8 +56,8 @@
     return false;
 
   auto job_task = base::MakeRefCounted<MockJobTask>(std::move(closure));
-  scoped_refptr<JobTaskSource> task_source =
-      job_task->GetJobTaskSource(from_here, traits_);
+  scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
+      from_here, traits_, pooled_task_runner_delegate_);
   return pooled_task_runner_delegate_->EnqueueJobTaskSource(
       std::move(task_source));
 }
@@ -217,6 +217,10 @@
   }
 }
 
+bool MockPooledTaskRunnerDelegate::ShouldYield(TaskSource* task_source) const {
+  return thread_group_->ShouldYield(task_source->priority_racy());
+}
+
 bool MockPooledTaskRunnerDelegate::EnqueueJobTaskSource(
     scoped_refptr<JobTaskSource> task_source) {
   // |thread_group_| must be initialized with SetThreadGroup() before
@@ -277,8 +281,6 @@
 }
 
 void MockJobTask::Run(experimental::JobDelegate* delegate) {
-  if (delegate->ShouldYield())
-    return;
   worker_task_.Run(delegate);
   size_t before = remaining_num_tasks_to_run_.fetch_sub(1);
   DCHECK_GT(before, 0U);
@@ -286,10 +288,12 @@
 
 scoped_refptr<JobTaskSource> MockJobTask::GetJobTaskSource(
     const Location& from_here,
-    const TaskTraits& traits) {
+    const TaskTraits& traits,
+    PooledTaskRunnerDelegate* delegate) {
   return MakeRefCounted<JobTaskSource>(
       from_here, traits, base::BindRepeating(&test::MockJobTask::Run, this),
-      base::BindRepeating(&test::MockJobTask::GetMaxConcurrency, this));
+      base::BindRepeating(&test::MockJobTask::GetMaxConcurrency, this),
+      delegate);
 }
 
 RegisteredTaskSource QueueAndRunTaskSource(
diff --git a/base/task/thread_pool/test_utils.h b/base/task/thread_pool/test_utils.h
index 554c205..cc378fc 100644
--- a/base/task/thread_pool/test_utils.h
+++ b/base/task/thread_pool/test_utils.h
@@ -62,6 +62,7 @@
   bool PostTaskWithSequence(Task task,
                             scoped_refptr<Sequence> sequence) override;
   bool EnqueueJobTaskSource(scoped_refptr<JobTaskSource> task_source) override;
+  bool ShouldYield(TaskSource* task_source) const override;
   bool IsRunningPoolWithTraits(const TaskTraits& traits) const override;
   void UpdatePriority(scoped_refptr<TaskSource> task_source,
                       TaskPriority priority) override;
@@ -81,8 +82,6 @@
 class MockJobTask : public base::RefCountedThreadSafe<MockJobTask> {
  public:
   // Gives |worker_task| to requesting workers |num_tasks_to_run| times.
-  // ShouldYield() is automatically called on JobDelegate before running
-  // |worker_task| so that DoNothing() may be passed.
   MockJobTask(
       base::RepeatingCallback<void(experimental::JobDelegate*)> worker_task,
       size_t num_tasks_to_run);
@@ -99,8 +98,10 @@
   size_t GetMaxConcurrency() const;
   void Run(experimental::JobDelegate* delegate);
 
-  scoped_refptr<JobTaskSource> GetJobTaskSource(const Location& from_here,
-                                                const TaskTraits& traits);
+  scoped_refptr<JobTaskSource> GetJobTaskSource(
+      const Location& from_here,
+      const TaskTraits& traits,
+      PooledTaskRunnerDelegate* delegate);
 
  private:
   friend class base::RefCountedThreadSafe<MockJobTask>;
diff --git a/base/task/thread_pool/thread_group_impl_unittest.cc b/base/task/thread_pool/thread_group_impl_unittest.cc
index 771b3ff..50baf9ab 100644
--- a/base/task/thread_pool/thread_group_impl_unittest.cc
+++ b/base/task/thread_pool/thread_group_impl_unittest.cc
@@ -311,7 +311,8 @@
       }),
       /* num_tasks_to_run */ kMaxTasks);
   scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
-      FROM_HERE, {ThreadPool(), TaskPriority::USER_VISIBLE});
+      FROM_HERE, {ThreadPool(), TaskPriority::USER_VISIBLE},
+      &mock_pooled_task_runner_delegate_);
 
   auto registered_task_source = task_tracker_.RegisterTaskSource(task_source);
   ASSERT_TRUE(registered_task_source);
diff --git a/base/task/thread_pool/thread_group_unittest.cc b/base/task/thread_pool/thread_group_unittest.cc
index 4bb81dc..cb4481c3 100644
--- a/base/task/thread_pool/thread_group_unittest.cc
+++ b/base/task/thread_pool/thread_group_unittest.cc
@@ -596,8 +596,8 @@
         test::WaitWithoutBlockingObserver(&threads_continue);
       }),
       /* num_tasks_to_run */ kMaxTasks);
-  scoped_refptr<JobTaskSource> task_source =
-      job_task->GetJobTaskSource(FROM_HERE, {ThreadPool()});
+  scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
+      FROM_HERE, ThreadPool(), &mock_pooled_task_runner_delegate_);
 
   auto registered_task_source =
       task_tracker_.RegisterTaskSource(std::move(task_source));
@@ -614,6 +614,95 @@
   task_tracker_.FlushForTesting();
 }
 
+// Verify that tasks from a JobTaskSource run at the intended concurrency.
+TEST_P(ThreadGroupTest, ScheduleJobTaskSourceMultipleTime) {
+  StartThreadGroup();
+
+  WaitableEvent thread_running;
+  WaitableEvent thread_continue;
+  auto job_task = base::MakeRefCounted<test::MockJobTask>(
+      BindLambdaForTesting(
+          [&thread_running, &thread_continue](experimental::JobDelegate*) {
+            DCHECK(!thread_running.IsSignaled());
+            thread_running.Signal();
+            test::WaitWithoutBlockingObserver(&thread_continue);
+          }),
+      /* num_tasks_to_run */ 1);
+  scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
+      FROM_HERE, ThreadPool(), &mock_pooled_task_runner_delegate_);
+
+  thread_group_->PushTaskSourceAndWakeUpWorkers(
+      TransactionWithRegisteredTaskSource::FromTaskSource(
+          task_tracker_.RegisterTaskSource(task_source)));
+
+  // Enqueuing the task source again shouldn't affect the number of time it's
+  // run.
+  thread_group_->PushTaskSourceAndWakeUpWorkers(
+      TransactionWithRegisteredTaskSource::FromTaskSource(
+          task_tracker_.RegisterTaskSource(task_source)));
+
+  thread_running.Wait();
+  thread_continue.Signal();
+
+  // Once the worker task ran, enqueuing the task source has no effect.
+  thread_group_->PushTaskSourceAndWakeUpWorkers(
+      TransactionWithRegisteredTaskSource::FromTaskSource(
+          task_tracker_.RegisterTaskSource(task_source)));
+
+  // Flush the task tracker to be sure that no local variables are accessed by
+  // tasks after the end of the scope.
+  task_tracker_.FlushForTesting();
+}
+
+// Verify that calling JobTaskSource::NotifyConcurrencyIncrease() (re-)schedule
+// tasks with the intended concurrency.
+TEST_P(ThreadGroupTest, JobTaskSourceConcurrencyIncrease) {
+  StartThreadGroup();
+
+  WaitableEvent threads_running_a;
+  WaitableEvent threads_continue;
+
+  // Initially schedule half the tasks.
+  RepeatingClosure threads_running_barrier = BarrierClosure(
+      kMaxTasks / 2,
+      BindOnce(&WaitableEvent::Signal, Unretained(&threads_running_a)));
+
+  auto job_state = base::MakeRefCounted<test::MockJobTask>(
+      BindLambdaForTesting([&threads_running_barrier,
+                            &threads_continue](experimental::JobDelegate*) {
+        threads_running_barrier.Run();
+        test::WaitWithoutBlockingObserver(&threads_continue);
+      }),
+      /* num_tasks_to_run */ kMaxTasks / 2);
+  auto task_source = job_state->GetJobTaskSource(
+      FROM_HERE, ThreadPool(), &mock_pooled_task_runner_delegate_);
+
+  auto registered_task_source = task_tracker_.RegisterTaskSource(task_source);
+  EXPECT_TRUE(registered_task_source);
+  thread_group_->PushTaskSourceAndWakeUpWorkers(
+      TransactionWithRegisteredTaskSource::FromTaskSource(
+          std::move(registered_task_source)));
+
+  threads_running_a.Wait();
+  // Reset |threads_running_barrier| for the remaining tasks.
+  WaitableEvent threads_running_b;
+  threads_running_barrier = BarrierClosure(
+      kMaxTasks / 2,
+      BindOnce(&WaitableEvent::Signal, Unretained(&threads_running_b)));
+  job_state->SetNumTasksToRun(kMaxTasks);
+
+  // Unblocks tasks to let them racily wait for NotifyConcurrencyIncrease() to
+  // be called.
+  threads_continue.Signal();
+  task_source->NotifyConcurrencyIncrease();
+  // Wait for the remaining tasks. This should not block forever.
+  threads_running_b.Wait();
+
+  // Flush the task tracker to be sure that no local variables are accessed by
+  // tasks after the end of the scope.
+  task_tracker_.FlushForTesting();
+}
+
 // Verify that a JobTaskSource that becomes empty while in the queue eventually
 // gets discarded.
 TEST_P(ThreadGroupTest, ScheduleEmptyJobTaskSource) {
@@ -624,8 +713,8 @@
   auto job_task = base::MakeRefCounted<test::MockJobTask>(
       BindRepeating([](experimental::JobDelegate*) { ShouldNotRun(); }),
       /* num_tasks_to_run */ 1);
-  scoped_refptr<JobTaskSource> task_source =
-      job_task->GetJobTaskSource(FROM_HERE, {ThreadPool()});
+  scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
+      FROM_HERE, ThreadPool(), &mock_pooled_task_runner_delegate_);
 
   auto registered_task_source =
       task_tracker_.RegisterTaskSource(std::move(task_source));
@@ -674,7 +763,8 @@
       }),
       /* num_tasks_to_run */ kMaxTasks);
   scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
-      FROM_HERE, {ThreadPool(), TaskPriority::BEST_EFFORT});
+      FROM_HERE, {ThreadPool(), TaskPriority::BEST_EFFORT},
+      &mock_pooled_task_runner_delegate_);
 
   auto registered_task_source = task_tracker_.RegisterTaskSource(task_source);
   EXPECT_TRUE(registered_task_source);
diff --git a/base/task/thread_pool/thread_pool_impl.cc b/base/task/thread_pool/thread_pool_impl.cc
index 6953de1..fcb783c 100644
--- a/base/task/thread_pool/thread_pool_impl.cc
+++ b/base/task/thread_pool/thread_pool_impl.cc
@@ -18,6 +18,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
+#include "base/task/scoped_set_task_priority_for_current_thread.h"
 #include "base/task/task_features.h"
 #include "base/task/thread_pool/pooled_parallel_task_runner.h"
 #include "base/task/thread_pool/pooled_sequenced_task_runner.h"
@@ -396,6 +397,19 @@
   return true;
 }
 
+bool ThreadPoolImpl::ShouldYield(TaskSource* task_source) const {
+  const TaskPriority priority = task_source->priority_racy();
+  auto* const thread_group = GetThreadGroupForTraits(
+      {ThreadPool(), priority, task_source->thread_policy()});
+  // A task whose priority changed and is now running in the wrong thread group
+  // should yield so it's rescheduled in the right one.
+  if (!thread_group->IsBoundToCurrentThread())
+    return true;
+  return GetThreadGroupForTraits(
+             {ThreadPool(), priority, task_source->thread_policy()})
+      ->ShouldYield(priority);
+}
+
 bool ThreadPoolImpl::EnqueueJobTaskSource(
     scoped_refptr<JobTaskSource> task_source) {
   auto registered_task_source =
diff --git a/base/task/thread_pool/thread_pool_impl.h b/base/task/thread_pool/thread_pool_impl.h
index 75d72f05..d66f0a6 100644
--- a/base/task/thread_pool/thread_pool_impl.h
+++ b/base/task/thread_pool/thread_pool_impl.h
@@ -103,6 +103,8 @@
 
   // PooledTaskRunnerDelegate:
   bool EnqueueJobTaskSource(scoped_refptr<JobTaskSource> task_source) override;
+  void UpdatePriority(scoped_refptr<TaskSource> task_source,
+                      TaskPriority priority) override;
 
   // Returns the TimeTicks of the next task scheduled on ThreadPool (Now() if
   // immediate, nullopt if none). This is thread-safe, i.e., it's safe if tasks
@@ -139,8 +141,7 @@
   bool PostTaskWithSequence(Task task,
                             scoped_refptr<Sequence> sequence) override;
   bool IsRunningPoolWithTraits(const TaskTraits& traits) const override;
-  void UpdatePriority(scoped_refptr<TaskSource> task_source,
-                      TaskPriority priority) override;
+  bool ShouldYield(TaskSource* task_source) const override;
 
   const std::unique_ptr<TaskTrackerImpl> task_tracker_;
   std::unique_ptr<Thread> service_thread_;
diff --git a/base/task/thread_pool/thread_pool_impl_unittest.cc b/base/task/thread_pool/thread_pool_impl_unittest.cc
index 337eb73d..7e1cb29 100644
--- a/base/task/thread_pool/thread_pool_impl_unittest.cc
+++ b/base/task/thread_pool/thread_pool_impl_unittest.cc
@@ -1087,12 +1087,47 @@
       }),
       /* num_tasks_to_run */ 1);
   scoped_refptr<JobTaskSource> task_source =
-      job_task->GetJobTaskSource(FROM_HERE, {ThreadPool()});
+      job_task->GetJobTaskSource(FROM_HERE, ThreadPool(), thread_pool_.get());
 
   thread_pool_->EnqueueJobTaskSource(task_source);
   threads_running.Wait();
 }
 
+// Verify that calling ShouldYield() returns true for a job task source that
+// needs to change thread group because of a priority update.
+TEST_P(ThreadPoolImplTest, ThreadGroupChangeShouldYield) {
+  StartThreadPool();
+
+  WaitableEvent threads_running;
+  WaitableEvent threads_continue;
+
+  auto job_task = base::MakeRefCounted<test::MockJobTask>(
+      BindLambdaForTesting([&threads_running, &threads_continue](
+                               experimental::JobDelegate* delegate) {
+        EXPECT_FALSE(delegate->ShouldYield());
+
+        threads_running.Signal();
+        test::WaitWithoutBlockingObserver(&threads_continue);
+
+        // The task source needs to yield if background thread groups exist.
+        EXPECT_EQ(delegate->ShouldYield(),
+                  CanUseBackgroundPriorityForWorkerThread());
+      }),
+      /* num_tasks_to_run */ 1);
+  scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
+      FROM_HERE, {ThreadPool(), TaskPriority::USER_VISIBLE},
+      thread_pool_.get());
+
+  thread_pool_->EnqueueJobTaskSource(task_source);
+  threads_running.Wait();
+  thread_pool_->UpdatePriority(task_source, TaskPriority::BEST_EFFORT);
+  threads_continue.Signal();
+
+  // Flush the task tracker to be sure that no local variables are accessed by
+  // tasks after the end of the scope.
+  thread_pool_->FlushForTesting();
+}
+
 namespace {
 
 class MustBeDestroyed {
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index 09e7d73..98e316ac 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -275,6 +275,7 @@
 }
 
 namespace internal {
+class JobTaskSource;
 class TaskTracker;
 }
 
@@ -450,6 +451,7 @@
   friend class audio::OutputDevice;
   friend class base::sequence_manager::internal::TaskQueueImpl;
   friend class base::FileDescriptorWatcher;
+  friend class base::internal::JobTaskSource;
   friend class base::MessageLoopImpl;
   friend class base::ScopedAllowThreadRecallForStackSamplingProfiler;
   friend class base::StackSamplingProfiler;
diff --git a/build/android/pylib/constants/__init__.py b/build/android/pylib/constants/__init__.py
index f0ddd56..aff42e3 100644
--- a/build/android/pylib/constants/__init__.py
+++ b/build/android/pylib/constants/__init__.py
@@ -71,6 +71,14 @@
     chrome.PackageInfo('com.google.android.webview',
                        'com.android.cts.webkit.WebViewStartupCtsActivity',
                        'webview-command-line', None),
+    'android_system_webview_shell':
+    chrome.PackageInfo('org.chromium.webview_shell',
+                       'org.chromium.webview_shell.WebViewBrowserActivity',
+                       'webview-command-line', None),
+    'android_webview_ui_test':
+    chrome.PackageInfo('org.chromium.webview_ui_test',
+                       'org.chromium.webview_ui_test.WebViewUiTestActivity',
+                       'webview-command-line', None),
 })
 
 
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index d4ac148..566f893 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8904093899187505616
\ No newline at end of file
+8904069313901062272
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 214795eb..b6bd538 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8904100144621675968
\ No newline at end of file
+8904068489467873856
\ No newline at end of file
diff --git a/build/toolchain/win/tool_wrapper.py b/build/toolchain/win/tool_wrapper.py
index 1ad3712..3834cf2 100644
--- a/build/toolchain/win/tool_wrapper.py
+++ b/build/toolchain/win/tool_wrapper.py
@@ -18,34 +18,48 @@
 import sys
 
 # Embedded vpython spec to provide `win32file` when this is invoked with
-# vpython.
+# vpython on windows.
 #
 # [VPYTHON:BEGIN]
 # wheel: <
 #   name: "infra/python/wheels/pypiwin32/${vpython_platform}"
 #   version: "version:219"
+#   match_tag: < platform: "win32" >
+#   match_tag: < platform: "win_amd64" >
 # >
 # [VPYTHON:END]
 
-try:
-  # First, try the normal way. This will work for python installations which
-  # have win32file already, or for vpython invocations of this script.
-  import win32file
-except ImportError:
-  # Otherwise, do a hack to locate the depot_tools specific version of
-  # win32file.
-  #
-  # tool_wrapper.py doesn't get invoked through python.bat so the Python bin
-  # directory doesn't get added to the path. The Python module search logic
-  # handles this fine and finds win32file.pyd. However the Windows module
-  # search logic then looks for pywintypes27.dll and other DLLs in the path and
-  # if it finds versions with a different bitness first then win32file.pyd will
-  # fail to load with a cryptic error:
-  #     ImportError: DLL load failed: %1 is not a valid Win32 application.
-  if sys.platform == 'win32':
-    os.environ['PATH'] = os.path.dirname(sys.executable) + \
-                         os.pathsep + os.environ['PATH']
-    import win32file    # pylint: disable=import-error
+if sys.platform == "win32":
+  try:
+    # First, try the normal way. This will work for python installations which
+    # have win32file already, or for vpython invocations of this script.
+    import win32file
+  except ImportError:
+    # Otherwise, do a hack to locate the depot_tools specific version of
+    # win32file.
+    #
+    # tool_wrapper.py doesn't get invoked through python.bat so the Python bin
+    # directory doesn't get added to the path. The Python module search logic
+    # handles this fine and finds win32file.pyd. However the Windows module
+    # search logic then looks for pywintypes27.dll and other DLLs in the path
+    # and if it finds versions with a different bitness first then win32file.pyd
+    # will fail to load with a cryptic error:
+    #     ImportError: DLL load failed: %1 is not a valid Win32 application.
+    if sys.platform == 'win32':
+      os.environ['PATH'] = os.path.dirname(sys.executable) + \
+                           os.pathsep + os.environ['PATH']
+      import win32file    # pylint: disable=import-error
+
+  def superflush(pe_name):
+    # Flush the file buffers to try to work around a Windows 10 kernel bug,
+    # https://crbug.com/644525
+    output_handle = win32file.CreateFile(pe_name, win32file.GENERIC_WRITE,
+                                    0, None, win32file.OPEN_EXISTING, 0, 0)
+    win32file.FlushFileBuffers(output_handle)
+    output_handle.Close()
+else:
+  def superflush(pe_name):
+    return None
 
 BASE_DIR = os.path.dirname(os.path.abspath(__file__))
 
@@ -176,12 +190,7 @@
         print(line)
     result = link.wait()
     if result == 0 and sys.platform == 'win32':
-      # Flush the file buffers to try to work around a Windows 10 kernel bug,
-      # https://crbug.com/644525
-      output_handle = win32file.CreateFile(pe_name, win32file.GENERIC_WRITE,
-                                      0, None, win32file.OPEN_EXISTING, 0, 0)
-      win32file.FlushFileBuffers(output_handle)
-      output_handle.Close()
+      superflush(pe_name)
     return result
 
   def ExecAsmWrapper(self, arch, *args):
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index 15db8ec..cb06e83 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -837,84 +837,6 @@
   }
 }
 
-void LayerTreeTest::SetupViewport(scoped_refptr<Layer> outer_scroll_layer,
-                                  const gfx::Size& outer_bounds) {
-  Layer* root = layer_tree_host()->root_layer();
-  DCHECK(root);
-  scoped_refptr<Layer> inner_viewport_container_layer = Layer::Create();
-  scoped_refptr<Layer> overscroll_elasticity_layer = Layer::Create();
-  scoped_refptr<Layer> inner_viewport_scroll_layer = Layer::Create();
-  scoped_refptr<Layer> outer_viewport_container_layer = Layer::Create();
-  scoped_refptr<Layer> page_scale_layer = Layer::Create();
-
-  inner_viewport_scroll_layer->SetElementId(
-      LayerIdToElementIdForTesting(inner_viewport_scroll_layer->id()));
-  outer_scroll_layer->SetElementId(
-      LayerIdToElementIdForTesting(outer_scroll_layer->id()));
-  overscroll_elasticity_layer->SetElementId(
-      LayerIdToElementIdForTesting(overscroll_elasticity_layer->id()));
-
-  inner_viewport_container_layer->SetBounds(root->bounds());
-  inner_viewport_scroll_layer->SetScrollable(root->bounds());
-  inner_viewport_scroll_layer->SetHitTestable(true);
-  inner_viewport_scroll_layer->SetBounds(outer_bounds);
-  outer_viewport_container_layer->SetBounds(outer_bounds);
-  outer_scroll_layer->SetScrollable(outer_bounds);
-  outer_scroll_layer->SetHitTestable(true);
-
-  root->AddChild(inner_viewport_container_layer);
-  if (layer_tree_host()->IsUsingLayerLists()) {
-    root->AddChild(overscroll_elasticity_layer);
-    root->AddChild(page_scale_layer);
-    root->AddChild(inner_viewport_scroll_layer);
-    root->AddChild(outer_viewport_container_layer);
-    root->AddChild(outer_scroll_layer);
-
-    CopyProperties(root, inner_viewport_container_layer.get());
-    CopyProperties(inner_viewport_container_layer.get(),
-                   overscroll_elasticity_layer.get());
-    CreateTransformNode(overscroll_elasticity_layer.get());
-    CopyProperties(overscroll_elasticity_layer.get(), page_scale_layer.get());
-    CreateTransformNode(page_scale_layer.get());
-    CopyProperties(page_scale_layer.get(), inner_viewport_scroll_layer.get());
-    CopyProperties(inner_viewport_scroll_layer.get(),
-                   outer_viewport_container_layer.get());
-    CopyProperties(outer_viewport_container_layer.get(),
-                   outer_scroll_layer.get());
-    // TODO(wangxianzhu): Create other property nodes when they are needed by
-    // tests newly converted to layer list mode.
-  } else {
-    inner_viewport_container_layer->AddChild(overscroll_elasticity_layer);
-    overscroll_elasticity_layer->AddChild(page_scale_layer);
-    page_scale_layer->AddChild(inner_viewport_scroll_layer);
-    inner_viewport_scroll_layer->AddChild(outer_viewport_container_layer);
-    outer_viewport_container_layer->AddChild(outer_scroll_layer);
-
-    inner_viewport_scroll_layer->SetIsContainerForFixedPositionLayers(true);
-    outer_scroll_layer->SetIsContainerForFixedPositionLayers(true);
-    layer_tree_host()->property_trees()->needs_rebuild = true;
-  }
-
-  ViewportLayers viewport_layers;
-  viewport_layers.overscroll_elasticity_element_id =
-      overscroll_elasticity_layer->element_id();
-  viewport_layers.page_scale = page_scale_layer;
-  viewport_layers.inner_viewport_container = inner_viewport_container_layer;
-  viewport_layers.outer_viewport_container = outer_viewport_container_layer;
-  viewport_layers.inner_viewport_scroll = inner_viewport_scroll_layer;
-  viewport_layers.outer_viewport_scroll = outer_scroll_layer;
-  layer_tree_host()->RegisterViewportLayers(viewport_layers);
-}
-
-void LayerTreeTest::SetupViewport(const gfx::Size& outer_bounds,
-                                  const gfx::Size& scroll_bounds) {
-  scoped_refptr<Layer> outer_viewport_scroll_layer = Layer::Create();
-  outer_viewport_scroll_layer->SetBounds(scroll_bounds);
-  outer_viewport_scroll_layer->SetIsDrawable(true);
-  outer_viewport_scroll_layer->SetHitTestable(true);
-  SetupViewport(outer_viewport_scroll_layer, outer_bounds);
-}
-
 void LayerTreeTest::SetupTree() {
   if (!layer_tree_host()->root_layer()) {
     layer_tree_host()->SetRootLayer(Layer::Create());
diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h
index 743351a..522fd40 100644
--- a/cc/test/layer_tree_test.h
+++ b/cc/test/layer_tree_test.h
@@ -107,17 +107,6 @@
 
   AnimationHost* animation_host() const { return animation_host_.get(); }
 
-  // Creates viewport layers and (in layer list mode) paint properties.
-  // Convenient overload of the method below that creates a scrolling layer as
-  // the outer viewport scroll layer.
-  void SetupViewport(const gfx::Size& outer_bounds,
-                     const gfx::Size& scroll_bounds);
-
-  // Creates viewport layers and (in layer list mode) paint properties.
-  // Uses the given scroll layer as the content "outer viewport scroll layer".
-  void SetupViewport(scoped_refptr<Layer> outer_scroll_layer,
-                     const gfx::Size& outer_bounds);
-
   void SetUseLayerLists() { settings_.use_layer_lists = true; }
 
  protected:
diff --git a/cc/test/property_tree_test_utils.cc b/cc/test/property_tree_test_utils.cc
index 3601d7f5..5912279 100644
--- a/cc/test/property_tree_test_utils.cc
+++ b/cc/test/property_tree_test_utils.cc
@@ -183,4 +183,83 @@
   return CreateScrollNodeInternal(layer, parent_id);
 }
 
+void SetupViewport(Layer* root,
+                   scoped_refptr<Layer> outer_scroll_layer,
+                   const gfx::Size& outer_bounds) {
+  DCHECK(root);
+  scoped_refptr<Layer> inner_viewport_container_layer = Layer::Create();
+  scoped_refptr<Layer> overscroll_elasticity_layer = Layer::Create();
+  scoped_refptr<Layer> inner_viewport_scroll_layer = Layer::Create();
+  scoped_refptr<Layer> outer_viewport_container_layer = Layer::Create();
+  scoped_refptr<Layer> page_scale_layer = Layer::Create();
+
+  inner_viewport_scroll_layer->SetElementId(
+      LayerIdToElementIdForTesting(inner_viewport_scroll_layer->id()));
+  outer_scroll_layer->SetElementId(
+      LayerIdToElementIdForTesting(outer_scroll_layer->id()));
+  overscroll_elasticity_layer->SetElementId(
+      LayerIdToElementIdForTesting(overscroll_elasticity_layer->id()));
+
+  inner_viewport_container_layer->SetBounds(root->bounds());
+  inner_viewport_scroll_layer->SetScrollable(root->bounds());
+  inner_viewport_scroll_layer->SetHitTestable(true);
+  inner_viewport_scroll_layer->SetBounds(outer_bounds);
+  outer_viewport_container_layer->SetBounds(outer_bounds);
+  outer_scroll_layer->SetScrollable(outer_bounds);
+  outer_scroll_layer->SetHitTestable(true);
+
+  root->AddChild(inner_viewport_container_layer);
+  if (root->layer_tree_host()->IsUsingLayerLists()) {
+    root->AddChild(overscroll_elasticity_layer);
+    root->AddChild(page_scale_layer);
+    root->AddChild(inner_viewport_scroll_layer);
+    root->AddChild(outer_viewport_container_layer);
+    root->AddChild(outer_scroll_layer);
+
+    CopyProperties(root, inner_viewport_container_layer.get());
+    CopyProperties(inner_viewport_container_layer.get(),
+                   overscroll_elasticity_layer.get());
+    CreateTransformNode(overscroll_elasticity_layer.get());
+    CopyProperties(overscroll_elasticity_layer.get(), page_scale_layer.get());
+    CreateTransformNode(page_scale_layer.get());
+    CopyProperties(page_scale_layer.get(), inner_viewport_scroll_layer.get());
+    CopyProperties(inner_viewport_scroll_layer.get(),
+                   outer_viewport_container_layer.get());
+    CopyProperties(outer_viewport_container_layer.get(),
+                   outer_scroll_layer.get());
+    // TODO(wangxianzhu): Create other property nodes when they are needed by
+    // tests newly converted to layer list mode.
+  } else {
+    inner_viewport_container_layer->AddChild(overscroll_elasticity_layer);
+    overscroll_elasticity_layer->AddChild(page_scale_layer);
+    page_scale_layer->AddChild(inner_viewport_scroll_layer);
+    inner_viewport_scroll_layer->AddChild(outer_viewport_container_layer);
+    outer_viewport_container_layer->AddChild(outer_scroll_layer);
+
+    inner_viewport_scroll_layer->SetIsContainerForFixedPositionLayers(true);
+    outer_scroll_layer->SetIsContainerForFixedPositionLayers(true);
+    root->layer_tree_host()->property_trees()->needs_rebuild = true;
+  }
+
+  ViewportLayers viewport_layers;
+  viewport_layers.overscroll_elasticity_element_id =
+      overscroll_elasticity_layer->element_id();
+  viewport_layers.page_scale = page_scale_layer;
+  viewport_layers.inner_viewport_container = inner_viewport_container_layer;
+  viewport_layers.outer_viewport_container = outer_viewport_container_layer;
+  viewport_layers.inner_viewport_scroll = inner_viewport_scroll_layer;
+  viewport_layers.outer_viewport_scroll = outer_scroll_layer;
+  root->layer_tree_host()->RegisterViewportLayers(viewport_layers);
+}
+
+void SetupViewport(Layer* root,
+                   const gfx::Size& outer_bounds,
+                   const gfx::Size& scroll_bounds) {
+  scoped_refptr<Layer> outer_viewport_scroll_layer = Layer::Create();
+  outer_viewport_scroll_layer->SetBounds(scroll_bounds);
+  outer_viewport_scroll_layer->SetIsDrawable(true);
+  outer_viewport_scroll_layer->SetHitTestable(true);
+  SetupViewport(root, outer_viewport_scroll_layer, outer_bounds);
+}
+
 }  // namespace cc
diff --git a/cc/test/property_tree_test_utils.h b/cc/test/property_tree_test_utils.h
index 3ef5716..d414835 100644
--- a/cc/test/property_tree_test_utils.h
+++ b/cc/test/property_tree_test_utils.h
@@ -67,6 +67,19 @@
   return GetPropertyTrees(layer)->scroll_tree.Node(layer->scroll_tree_index());
 }
 
+// Creates viewport layers and (in layer list mode) paint properties.
+// Convenient overload of the method below that creates a scrolling layer as
+// the outer viewport scroll layer.
+void SetupViewport(Layer* root,
+                   const gfx::Size& outer_bounds,
+                   const gfx::Size& scroll_bounds);
+
+// Creates viewport layers and (in layer list mode) paint properties.
+// Uses the given scroll layer as the content "outer viewport scroll layer".
+void SetupViewport(Layer* root,
+                   scoped_refptr<Layer> outer_scroll_layer,
+                   const gfx::Size& outer_bounds);
+
 // TODO(wangxianzhu): Add functions to create property nodes not based on
 // layers when needed.
 
diff --git a/cc/trees/effect_node.cc b/cc/trees/effect_node.cc
index 84743295..339be0b 100644
--- a/cc/trees/effect_node.cc
+++ b/cc/trees/effect_node.cc
@@ -20,7 +20,7 @@
       cache_render_surface(false),
       has_copy_request(false),
       hidden_by_backface_visibility(false),
-      double_sided(false),
+      double_sided(true),
       trilinear_filtering(false),
       is_drawn(true),
       subtree_hidden(false),
diff --git a/cc/trees/effect_node.h b/cc/trees/effect_node.h
index eaddb83f..0f9d220 100644
--- a/cc/trees/effect_node.h
+++ b/cc/trees/effect_node.h
@@ -93,6 +93,8 @@
   bool cache_render_surface : 1;
   bool has_copy_request : 1;
   bool hidden_by_backface_visibility : 1;
+  // Whether the contents should continue to be visible when rotated such that
+  // its back face is facing toward the camera. It's true by default.
   bool double_sided : 1;
   bool trilinear_filtering : 1;
   bool is_drawn : 1;
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 17c3b10d..ee47dc4e 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -3110,7 +3110,7 @@
                                        2 * root_layer->bounds().height()));
     scroll_layer_->SetScrollOffset(gfx::ScrollOffset());
 
-    SetupViewport(scroll_layer_, root_layer->bounds());
+    SetupViewport(root_layer, scroll_layer_, root_layer->bounds());
 
     layer_tree_host()->SetPageScaleFactorAndLimits(1.f, 0.5f, 2.f);
     client_.set_bounds(root_layer->bounds());
diff --git a/cc/trees/layer_tree_host_unittest_masks.cc b/cc/trees/layer_tree_host_unittest_masks.cc
index 9847b71..7e3dde74 100644
--- a/cc/trees/layer_tree_host_unittest_masks.cc
+++ b/cc/trees/layer_tree_host_unittest_masks.cc
@@ -57,7 +57,7 @@
     LayerTreeTest::SetupTree();
     scoped_refptr<Layer> outer_viewport_scroll_layer = Layer::Create();
     outer_viewport_scroll_layer->SetBounds(layer_size);
-    SetupViewport(outer_viewport_scroll_layer, gfx::Size(50, 50));
+    SetupViewport(root.get(), outer_viewport_scroll_layer, gfx::Size(50, 50));
     layer_tree_host()->outer_viewport_container_layer()->SetMasksToBounds(true);
     outer_viewport_scroll_layer->AddChild(content_layer);
 
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index 5759c6b..ea542533f 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -83,7 +83,7 @@
     gfx::Size scroll_layer_bounds(root_layer->bounds().width() + 100,
                                   root_layer->bounds().height() + 100);
 
-    SetupViewport(root_layer->bounds(), scroll_layer_bounds);
+    SetupViewport(root_layer, root_layer->bounds(), scroll_layer_bounds);
   }
 };
 
@@ -571,7 +571,7 @@
     root_scroll_layer_->SetPosition(gfx::PointF());
     root_scroll_layer_->SetIsDrawable(true);
 
-    SetupViewport(root_scroll_layer_, root_layer->bounds());
+    SetupViewport(root_layer, root_scroll_layer_, root_layer->bounds());
 
     child_layer_ = FakePictureLayer::Create(&fake_content_layer_client_);
     child_layer_->set_did_scroll_callback(
@@ -1321,7 +1321,7 @@
     Layer* root_layer = layer_tree_host()->root_layer();
     root_layer->SetBounds(gfx::Size(10, 10));
 
-    SetupViewport(root_layer->bounds(), root_layer->bounds());
+    SetupViewport(root_layer, root_layer->bounds(), root_layer->bounds());
 
     Layer* outer_scroll_layer =
         layer_tree_host()->outer_viewport_scroll_layer();
diff --git a/cc/trees/transform_node.cc b/cc/trees/transform_node.cc
index ede265c..3a791c4 100644
--- a/cc/trees/transform_node.cc
+++ b/cc/trees/transform_node.cc
@@ -25,7 +25,7 @@
       has_potential_animation(false),
       is_currently_animating(false),
       to_screen_is_potentially_animated(false),
-      flattens_inherited_transform(false),
+      flattens_inherited_transform(true),
       node_and_ancestors_are_flat(true),
       node_and_ancestors_have_only_integer_translation(true),
       scrolls(false),
diff --git a/cc/trees/transform_node.h b/cc/trees/transform_node.h
index df1b8a9..9a7eb49 100644
--- a/cc/trees/transform_node.h
+++ b/cc/trees/transform_node.h
@@ -81,7 +81,7 @@
   bool to_screen_is_potentially_animated : 1;
 
   // Flattening, when needed, is only applied to a node's inherited transform,
-  // never to its local transform.
+  // never to its local transform. It's true by default.
   bool flattens_inherited_transform : 1;
 
   // This is true if the to_parent transform at every node on the path to the
diff --git a/chrome/VERSION b/chrome/VERSION
index 05e95ed1..61b46db0 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=78
 MINOR=0
-BUILD=3893
+BUILD=3894
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 6b6136a..0000d3da 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -2585,6 +2585,7 @@
     "java/src/org/chromium/chrome/browser/password_manager/Credential.java",
     "java/src/org/chromium/chrome/browser/password_manager/CredentialLeakDialogBridge.java",
     "java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogBridge.java",
+    "java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncher.java",
     "java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogBridge.java",
     "java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationPopupBridge.java",
     "java/src/org/chromium/chrome/browser/payments/CanMakePaymentQuery.java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index cabdd6e..2760cf0 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1169,6 +1169,7 @@
   "java/src/org/chromium/chrome/browser/password_manager/CredentialLeakDialogBridge.java",
   "java/src/org/chromium/chrome/browser/password_manager/GooglePasswordManagerUIProvider.java",
   "java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogBridge.java",
+  "java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncher.java",
   "java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogBridge.java",
   "java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogCoordinator.java",
   "java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogCustomView.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index a4c953d..25eca5d 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -149,6 +149,7 @@
   "junit/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtilsTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/SpannableAutocompleteEditTextModelUnitTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/UrlBarDataTest.java",
+  "junit/src/org/chromium/chrome/browser/omnibox/UrlBarUnitTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderUnitTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManagerTest.java",
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 652e4d05..f1277e64 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -72,6 +72,7 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantPeekHeightCoordinator.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantSnackbar.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTagsForTesting.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTextUtils.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryImpl.java",
@@ -121,6 +122,8 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestContactDetailsSection.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestCoordinator.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestDelegate.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestLoginChoice.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestLoginSection.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestModel.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestNativeDelegate.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestPaymentMethodSection.java",
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_login.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_login.xml
new file mode 100644
index 0000000..f3ac4d60
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_login.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/login_full"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:gravity="center_vertical">
+  <TextView
+      android:id="@+id/username"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:textAppearance="@style/TextAppearance.BlackBody"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_payment_request_choice_list.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_payment_request_choice_list.xml
deleted file mode 100644
index 0871df1..0000000
--- a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_payment_request_choice_list.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-
-<!-- Common layout file for all autofill assistant payment request choice lists. Will be filled
- in code, see |AssistantPaymentRequestSection.java|.-->
-<org.chromium.chrome.browser.autofill_assistant.payment.AssistantChoiceList
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/section_choice_list"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:background="@color/payments_section_edit_background"
-    android:paddingStart="@dimen/autofill_assistant_bottombar_horizontal_spacing"
-    android:paddingTop="8dp"
-    android:paddingEnd="@dimen/autofill_assistant_payment_request_choice_list_padding_end"
-    android:paddingBottom="8dp"
-    app:column_spacing="8dp">
-</org.chromium.chrome.browser.autofill_assistant.payment.AssistantChoiceList>
\ No newline at end of file
diff --git a/chrome/android/features/autofill_assistant/java/res/values-v17/dimens.xml b/chrome/android/features/autofill_assistant/java/res/values-v17/dimens.xml
index fac59b8..211772366 100644
--- a/chrome/android/features/autofill_assistant/java/res/values-v17/dimens.xml
+++ b/chrome/android/features/autofill_assistant/java/res/values-v17/dimens.xml
@@ -24,6 +24,7 @@
     <dimen name="autofill_assistant_choicelist_edit_button_size">20dp</dimen>
     <dimen name="autofill_assistant_minimum_touch_target_size">48dp</dimen>
 
+    <dimen name="autofill_assistant_payment_request_choice_top_bottom_padding">8dp</dimen>
     <!-- This is less than autofill_assistant_bottombar_horizontal_spacing to allow for larger
          touch targets at the right side of the PR, while keeping alignment with bottom bar. -->
     <dimen name="autofill_assistant_payment_request_choice_list_padding_end">12dp</dimen>
@@ -33,4 +34,5 @@
     <dimen name="autofill_assistant_payment_request_title_padding">2dp</dimen>
     <!-- Payment method needs slightly more padding to because of vendor logo. in summary/expanded part. -->
     <dimen name="autofill_assistant_payment_request_payment_method_title_padding">4dp</dimen>
+    <dimen name="autofill_assistant_payment_request_column_spacing">8dp</dimen>
 </resources>
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
index 3cad8edd..db851e5 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
@@ -136,7 +136,7 @@
                 mPaymentRequestCoordinator.getView()
                         .findViewWithTag(
                                 AssistantTagsForTesting.PAYMENT_REQUEST_PAYMENT_METHOD_SECTION_TAG)
-                        .findViewById(R.id.section_choice_list),
+                        .findViewWithTag(AssistantTagsForTesting.PAYMENT_REQUEST_CHOICE_LIST),
                 /* exclude= */ true);
 
         // Add child views to bottom bar container. We put all child views in the scrollable
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTagsForTesting.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTagsForTesting.java
index 72edf5a..af4a15f4 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTagsForTesting.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTagsForTesting.java
@@ -12,9 +12,11 @@
  */
 public class AssistantTagsForTesting {
     public static final String PAYMENT_REQUEST_ACCORDION_TAG = "accordion";
+    public static final String PAYMENT_REQUEST_LOGIN_SECTION_TAG = "login";
     public static final String PAYMENT_REQUEST_CONTACT_DETAILS_SECTION_TAG = "contact";
     public static final String PAYMENT_REQUEST_PAYMENT_METHOD_SECTION_TAG = "payment";
     public static final String PAYMENT_REQUEST_SHIPPING_ADDRESS_SECTION_TAG = "shipping";
     public static final String PAYMENT_REQUEST_TERMS_REQUIRE_REVIEW = "require_review";
     public static final String VERTICAL_EXPANDER_CHEVRON = "chevron";
+    public static final String PAYMENT_REQUEST_CHOICE_LIST = "choicelist";
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTextUtils.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTextUtils.java
new file mode 100644
index 0000000..45d62f9
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTextUtils.java
@@ -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.
+
+package org.chromium.chrome.browser.autofill_assistant;
+
+import android.support.annotation.Nullable;
+import android.text.method.LinkMovementMethod;
+import android.text.style.StyleSpan;
+import android.widget.TextView;
+
+import org.chromium.base.Callback;
+import org.chromium.ui.text.NoUnderlineClickableSpan;
+import org.chromium.ui.text.SpanApplier;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Common text utilities used by autofill assistant.
+ */
+public class AssistantTextUtils {
+    /** Bold tags of the form <b>...</b>. */
+    private static final SpanApplier.SpanInfo BOLD_SPAN_INFO =
+            new SpanApplier.SpanInfo("<b>", "</b>", new StyleSpan(android.graphics.Typeface.BOLD));
+    /** Links of the form <link0>...</link0>. */
+    private static final Pattern LINK_PATTERN = Pattern.compile("<link(\\d+)>");
+
+    /**
+     * Sets the text of {@code view} by automatically applying all special appearance tags in {@code
+     * text}.
+     *
+     * Special appearance tags are of the form <b></b> or <link0></link0>. For links, a callback can
+     * be provided.
+     */
+    public static void applyVisualAppearanceTags(
+            TextView view, String text, @Nullable Callback<Integer> linkCallback) {
+        List<SpanApplier.SpanInfo> spans = new ArrayList<>();
+        if (text.contains("<b>")) {
+            spans.add(BOLD_SPAN_INFO);
+        }
+
+        // We first collect the link IDs into a set to allow multiple links with same ID.
+        Set<Integer> linkIds = new HashSet<>();
+        Matcher matcher = LINK_PATTERN.matcher(text);
+        while (matcher.find()) {
+            linkIds.add(Integer.parseInt(matcher.group(1)));
+        }
+
+        for (Integer linkId : linkIds) {
+            spans.add(new SpanApplier.SpanInfo("<link" + linkId + ">", "</link" + linkId + ">",
+                    new NoUnderlineClickableSpan(view.getContext().getResources(), (unusedView) -> {
+                        if (linkCallback != null) {
+                            linkCallback.onResult(linkId);
+                        }
+                    })));
+        }
+
+        view.setText(SpanApplier.applySpans(
+                text, spans.toArray(new SpanApplier.SpanInfo[spans.size()])));
+        view.setMovementMethod(linkIds.isEmpty() ? null : LinkMovementMethod.getInstance());
+    }
+}
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantChoiceList.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantChoiceList.java
index 8469d73..c0485ee6 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantChoiceList.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantChoiceList.java
@@ -82,20 +82,26 @@
     private boolean mAllowMultipleChoices;
     private Runnable mAddButtonListener;
 
+    /**
+     * Constructor, automatically invoked when inflating this class from XML.
+     */
     public AssistantChoiceList(Context context, AttributeSet attrs) {
+        this(context, attrs,
+                context.getTheme().obtainStyledAttributes(
+                        attrs, R.styleable.AssistantChoiceList, 0, 0));
+    }
+
+    /**
+     * Constructor for use in Java code.
+     */
+    public AssistantChoiceList(Context context, AttributeSet attrs, @Nullable String addButtonText,
+            int rowSpacingInPixels, int columnSpacingInPixels) {
         super(context, attrs);
-        TypedArray a = context.getTheme().obtainStyledAttributes(
-                attrs, R.styleable.AssistantChoiceList, 0, 0);
-        mCanAddItems = a.getBoolean(R.styleable.AssistantChoiceList_can_add_items, true);
-        String addButtonText =
-                a.hasValue(R.styleable.AssistantChoiceList_add_button_text) && mCanAddItems
-                ? a.getString(R.styleable.AssistantChoiceList_add_button_text)
-                : null;
-        mRowSpacing = a.getDimensionPixelSize(R.styleable.AssistantChoiceList_row_spacing, 0);
-        mColumnSpacing = a.getDimensionPixelSize(R.styleable.AssistantChoiceList_column_spacing, 0);
+        mCanAddItems = addButtonText != null;
+        mRowSpacing = rowSpacingInPixels;
+        mColumnSpacing = columnSpacingInPixels;
         mMinimumTouchTargetSize = context.getResources().getDimensionPixelSize(
                 R.dimen.autofill_assistant_minimum_touch_target_size);
-        a.recycle();
 
         // One column for the radio buttons, one for the content, one for the edit buttons.
         setColumnCount(3);
@@ -116,6 +122,16 @@
         }
     }
 
+    private AssistantChoiceList(Context context, AttributeSet attrs, TypedArray a) {
+        this(context, attrs,
+                a.hasValue(R.styleable.AssistantChoiceList_add_button_text)
+                        ? a.getString(R.styleable.AssistantChoiceList_add_button_text)
+                        : null,
+                a.getDimensionPixelSize(R.styleable.AssistantChoiceList_row_spacing, 0),
+                a.getDimensionPixelSize(R.styleable.AssistantChoiceList_column_spacing, 0));
+        a.recycle();
+    }
+
     /**
      * Set whether this list allows multiple choices to be selected at the same time. This method
      * can only be called when no items have been added, otherwise it will throw an exception.
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestBinder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestBinder.java
index eace029..f69ba8a 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestBinder.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestBinder.java
@@ -93,6 +93,7 @@
         private final View mRootView;
         private final AssistantVerticalExpanderAccordion mPaymentRequestExpanderAccordion;
         private final int mSectionToSectionPadding;
+        private final AssistantPaymentRequestLoginSection mLoginSection;
         private final AssistantPaymentRequestContactDetailsSection mContactDetailsSection;
         private final AssistantPaymentRequestPaymentMethodSection mPaymentMethodSection;
         private final AssistantPaymentRequestShippingAddressSection mShippingAddressSection;
@@ -103,7 +104,7 @@
         private PersonalDataManager.PersonalDataManagerObserver mPersonalDataManagerObserver;
 
         public ViewHolder(View rootView, AssistantVerticalExpanderAccordion accordion,
-                int sectionPadding,
+                int sectionPadding, AssistantPaymentRequestLoginSection loginSection,
                 AssistantPaymentRequestContactDetailsSection contactDetailsSection,
                 AssistantPaymentRequestPaymentMethodSection paymentMethodSection,
                 AssistantPaymentRequestShippingAddressSection shippingAddressSection,
@@ -113,6 +114,7 @@
             mRootView = rootView;
             mPaymentRequestExpanderAccordion = accordion;
             mSectionToSectionPadding = sectionPadding;
+            mLoginSection = loginSection;
             mContactDetailsSection = contactDetailsSection;
             mPaymentMethodSection = paymentMethodSection;
             mShippingAddressSection = shippingAddressSection;
@@ -152,6 +154,7 @@
         boolean handled = updateEditors(model, propertyKey, view);
         handled = updateRootVisibility(model, propertyKey, view) || handled;
         handled = updateSectionVisibility(model, propertyKey, view) || handled;
+        handled = updateSectionTitles(model, propertyKey, view) || handled;
         handled = updateSectionContents(model, propertyKey, view) || handled;
         handled = updateSectionSelectedItem(model, propertyKey, view) || handled;
         /* Update section paddings *after* updating section visibility. */
@@ -180,6 +183,8 @@
                     delegate != null ? delegate::onPaymentMethodChanged : null);
             view.mShippingAddressSection.setListener(
                     delegate != null ? delegate::onShippingAddressChanged : null);
+            view.mLoginSection.setListener(
+                    delegate != null ? delegate::onLoginChoiceChanged : null);
         } else if (propertyKey == AssistantPaymentRequestModel.SUPPORTED_BASIC_CARD_NETWORKS) {
             updateAvailablePaymentMethods(model);
         } else if (propertyKey == AssistantPaymentRequestModel.SUPPORTED_PAYMENT_METHODS) {
@@ -195,6 +200,16 @@
                 || model.get(AssistantPaymentRequestModel.REQUEST_EMAIL);
     }
 
+    private boolean updateSectionTitles(
+            AssistantPaymentRequestModel model, PropertyKey propertyKey, ViewHolder view) {
+        if (propertyKey == AssistantPaymentRequestModel.LOGIN_SECTION_TITLE) {
+            view.mLoginSection.setTitle(
+                    model.get(AssistantPaymentRequestModel.LOGIN_SECTION_TITLE));
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Updates the available items for each PR section.
      * @return whether the property key was handled.
@@ -233,6 +248,12 @@
             view.mPaymentMethodSection.setBillingPostalCodeMissingText(
                     model.get(AssistantPaymentRequestModel.BILLING_POSTAL_CODE_MISSING_TEXT));
             return true;
+        } else if (propertyKey == AssistantPaymentRequestModel.AVAILABLE_LOGINS) {
+            if (model.get(AssistantPaymentRequestModel.REQUEST_LOGIN_CHOICE)) {
+                view.mLoginSection.onLoginsChanged(
+                        model.get(AssistantPaymentRequestModel.AVAILABLE_LOGINS));
+            }
+            return true;
         }
 
         return false;
@@ -272,6 +293,10 @@
                 view.mTermsAsCheckboxSection.getView().setVisibility(View.GONE);
             }
             return true;
+        } else if (propertyKey == AssistantPaymentRequestModel.REQUEST_LOGIN_CHOICE) {
+            view.mLoginSection.setVisible(
+                    model.get(AssistantPaymentRequestModel.REQUEST_LOGIN_CHOICE));
+            return true;
         }
         return false;
     }
@@ -293,11 +318,10 @@
                 updateAvailablePaymentMethods(model);
                 WebContents webContents = model.get(AssistantPaymentRequestModel.WEB_CONTENTS);
                 if (webContents != null) {
-                    view.mTermsSection.setOrigin(UrlFormatter.formatUrlForSecurityDisplayOmitScheme(
-                            webContents.getLastCommittedUrl()));
-                    view.mTermsAsCheckboxSection.setOrigin(
-                            UrlFormatter.formatUrlForSecurityDisplayOmitScheme(
-                                    webContents.getLastCommittedUrl()));
+                    String origin = UrlFormatter.formatUrlForSecurityDisplayOmitScheme(
+                            webContents.getLastCommittedUrl());
+                    view.mTermsSection.setOrigin(origin);
+                    view.mTermsAsCheckboxSection.setOrigin(origin);
                 }
                 view.startListenToPersonalDataManager(() -> {
                     AssistantPaymentRequestBinder.this.updateAvailableProfiles(model, view);
@@ -334,6 +358,10 @@
             view.mTermsSection.setTermsStatus(termsStatus);
             view.mTermsAsCheckboxSection.setTermsStatus(termsStatus);
             return true;
+        } else if (propertyKey == AssistantPaymentRequestModel.SELECTED_LOGIN) {
+            view.mLoginSection.addOrUpdateItem(
+                    model.get(AssistantPaymentRequestModel.SELECTED_LOGIN), true);
+            return true;
         }
         return false;
     }
@@ -360,13 +388,22 @@
                 && (propertyKey != AssistantPaymentRequestModel.REQUEST_EMAIL)
                 && (propertyKey != AssistantPaymentRequestModel.REQUEST_PHONE)
                 && (propertyKey != AssistantPaymentRequestModel.REQUEST_PAYMENT)
+                && (propertyKey != AssistantPaymentRequestModel.REQUEST_LOGIN_CHOICE)
                 && (propertyKey != AssistantPaymentRequestModel.EXPANDED_SECTION)) {
             return false;
         }
 
         // Update section paddings such that the first and last section are flush to the top/bottom,
         // and all other sections have the same amount of padding in-between them.
-        if (shouldShowContactDetails(model)) {
+        if (model.get(AssistantPaymentRequestModel.REQUEST_LOGIN_CHOICE)) {
+            view.mLoginSection.setPaddings(0, view.mSectionToSectionPadding);
+            view.mContactDetailsSection.setPaddings(
+                    view.mSectionToSectionPadding, view.mSectionToSectionPadding);
+            view.mPaymentMethodSection.setPaddings(
+                    view.mSectionToSectionPadding, view.mSectionToSectionPadding);
+            view.mShippingAddressSection.setPaddings(
+                    view.mSectionToSectionPadding, view.mSectionToSectionPadding);
+        } else if (shouldShowContactDetails(model)) {
             view.mContactDetailsSection.setPaddings(0, view.mSectionToSectionPadding);
             view.mPaymentMethodSection.setPaddings(
                     view.mSectionToSectionPadding, view.mSectionToSectionPadding);
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestContactDetailsSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestContactDetailsSection.java
index 8e0296ec..0e7ecdb 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestContactDetailsSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestContactDetailsSection.java
@@ -33,9 +33,9 @@
                 R.layout.autofill_assistant_contact_full,
                 context.getResources().getDimensionPixelSize(
                         R.dimen.autofill_assistant_payment_request_title_padding),
-                context.getString(R.string.payments_contact_details_label),
                 context.getString(R.string.payments_add_contact),
-                context.getString(R.string.payments_add_contact));
+                context.getString(R.string.payments_add_contact), /*canEditItems=*/true);
+        setTitle(context.getString(R.string.payments_contact_details_label));
     }
 
     public void setEditor(ContactEditor editor) {
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 726274b..5b914ac1 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
@@ -52,6 +52,9 @@
                 new LinearLayout.LayoutParams(
                         ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
 
+        AssistantPaymentRequestLoginSection loginSection =
+                new AssistantPaymentRequestLoginSection(mActivity, paymentRequestExpanderAccordion);
+        createSeparator(paymentRequestExpanderAccordion);
         AssistantPaymentRequestContactDetailsSection contactDetailsSection =
                 new AssistantPaymentRequestContactDetailsSection(
                         mActivity, paymentRequestExpanderAccordion);
@@ -72,6 +75,7 @@
 
         paymentRequestExpanderAccordion.setTag(
                 AssistantTagsForTesting.PAYMENT_REQUEST_ACCORDION_TAG);
+        loginSection.getView().setTag(AssistantTagsForTesting.PAYMENT_REQUEST_LOGIN_SECTION_TAG);
         contactDetailsSection.getView().setTag(
                 AssistantTagsForTesting.PAYMENT_REQUEST_CONTACT_DETAILS_SECTION_TAG);
         paymentMethodSection.getView().setTag(
@@ -81,9 +85,9 @@
 
         // Bind view and mediator through the model.
         mViewHolder = new AssistantPaymentRequestBinder.ViewHolder(mPaymentRequestUI,
-                paymentRequestExpanderAccordion, sectionToSectionPadding, contactDetailsSection,
-                paymentMethodSection, shippingAddressSection, termsSection, termsAsCheckboxSection,
-                DIVIDER_TAG, activity);
+                paymentRequestExpanderAccordion, sectionToSectionPadding, loginSection,
+                contactDetailsSection, paymentMethodSection, shippingAddressSection, termsSection,
+                termsAsCheckboxSection, DIVIDER_TAG, activity);
         AssistantPaymentRequestBinder binder = new AssistantPaymentRequestBinder();
         PropertyModelChangeProcessor.create(model, mViewHolder, binder);
 
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestDelegate.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestDelegate.java
index f3c67672..1e6a71e 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestDelegate.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestDelegate.java
@@ -31,4 +31,7 @@
 
     /** Called when a link on the terms and conditions message is clicked. */
     void onTermsAndConditionsLinkClicked(int link);
+
+    /** The currently selected login choice has changed. */
+    void onLoginChoiceChanged(@Nullable AssistantPaymentRequestLoginChoice loginChoice);
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestLoginChoice.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestLoginChoice.java
new file mode 100644
index 0000000..16db191
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestLoginChoice.java
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.autofill_assistant.payment;
+
+import org.chromium.chrome.browser.widget.prefeditor.EditableOption;
+
+/**
+ * Represents a single login choice.
+ *
+ * <p>Note: currently, login choices are always considered 'complete'.</p>
+ */
+public class AssistantPaymentRequestLoginChoice extends EditableOption {
+    private final int mPriority;
+    /**
+     * @param identifier The unique identifier of this login choice.
+     * @param label The label to display to the user.
+     * @param priority The priority of this login choice (lower value == higher priority). Can be -1
+     * to indicate default/auto.
+     */
+    public AssistantPaymentRequestLoginChoice(String identifier, String label, int priority) {
+        super(identifier, label, null, null);
+        mPriority = priority;
+    }
+
+    public int getPriority() {
+        return mPriority;
+    }
+}
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestLoginSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestLoginSection.java
new file mode 100644
index 0000000..99a02fc
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestLoginSection.java
@@ -0,0 +1,79 @@
+// 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.autofill_assistant.payment;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * The login details section of the Autofill Assistant payment request.
+ */
+public class AssistantPaymentRequestLoginSection
+        extends AssistantPaymentRequestSection<AssistantPaymentRequestLoginChoice> {
+    AssistantPaymentRequestLoginSection(Context context, ViewGroup parent) {
+        super(context, parent,
+                org.chromium.chrome.autofill_assistant.R.layout.autofill_assistant_login,
+                org.chromium.chrome.autofill_assistant.R.layout.autofill_assistant_login,
+                context.getResources().getDimensionPixelSize(
+                        org.chromium.chrome.autofill_assistant.R.dimen
+                                .autofill_assistant_payment_request_title_padding),
+                /*titleAddButton=*/null, /*listAddButton=*/null, /*canEditItems=*/false);
+    }
+
+    @Override
+    protected void createOrEditItem(@Nullable AssistantPaymentRequestLoginChoice oldItem) {
+        // Nothing to do, this section currently does not support adding or creating items.
+    }
+
+    @Override
+    protected void updateFullView(View fullView, AssistantPaymentRequestLoginChoice option) {
+        updateSummaryView(fullView, option);
+    }
+
+    @Override
+    protected void updateSummaryView(View summaryView, AssistantPaymentRequestLoginChoice option) {
+        TextView usernameView =
+                summaryView.findViewById(org.chromium.chrome.autofill_assistant.R.id.username);
+        usernameView.setText(option.getLabel());
+    }
+
+    /**
+     * The login options have changed externally. This will rebuild the UI with the new/changed
+     * set of login options, while keeping the selected item if possible.
+     */
+    void onLoginsChanged(List<AssistantPaymentRequestLoginChoice> options) {
+        int indexToSelect = -1;
+        if (mSelectedOption != null) {
+            for (int i = 0; i < getItems().size(); i++) {
+                if (TextUtils.equals(
+                            mSelectedOption.getIdentifier(), getItems().get(i).getIdentifier())) {
+                    indexToSelect = i;
+                    break;
+                }
+            }
+        }
+
+        // Preselect login option according to priority. This will implicitly select the first
+        // option if all options have the same (default) priority.
+        if (indexToSelect == -1) {
+            int highestPriority = Integer.MAX_VALUE;
+            for (int i = 0; i < options.size(); i++) {
+                int priority = options.get(i).getPriority();
+                if (priority < highestPriority) {
+                    highestPriority = priority;
+                    indexToSelect = i;
+                }
+            }
+        }
+
+        setItems(options, indexToSelect);
+    }
+}
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 62b839c..02b09a5 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
@@ -14,6 +14,7 @@
 import org.chromium.payments.mojom.PaymentMethodData;
 import org.chromium.ui.modelutil.PropertyModel;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
@@ -47,6 +48,14 @@
     public static final WritableObjectPropertyKey<AutofillContact> CONTACT_DETAILS =
             new WritableObjectPropertyKey<>();
 
+    /** The login section title. */
+    public static final WritableObjectPropertyKey<String> LOGIN_SECTION_TITLE =
+            new WritableObjectPropertyKey<>();
+
+    /** The chosen login option. */
+    public static final WritableObjectPropertyKey<AssistantPaymentRequestLoginChoice>
+            SELECTED_LOGIN = new WritableObjectPropertyKey<>();
+
     /** The status of the third party terms & conditions. */
     public static final WritableIntPropertyKey TERMS_STATUS = new WritableIntPropertyKey();
 
@@ -61,6 +70,8 @@
             new WritableObjectPropertyKey<>();
     public static final WritableBooleanPropertyKey SHOW_TERMS_AS_CHECKBOX =
             new WritableBooleanPropertyKey();
+    public static final WritableBooleanPropertyKey REQUEST_LOGIN_CHOICE =
+            new WritableBooleanPropertyKey();
 
     public static final WritableObjectPropertyKey<List<PersonalDataManager.AutofillProfile>>
             AVAILABLE_PROFILES = new WritableObjectPropertyKey<>();
@@ -75,6 +86,10 @@
     public static final WritableObjectPropertyKey<Map<String, PaymentMethodData>>
             SUPPORTED_PAYMENT_METHODS = new WritableObjectPropertyKey<>();
 
+    /** The available login choices. */
+    public static final WritableObjectPropertyKey<List<AssistantPaymentRequestLoginChoice>>
+            AVAILABLE_LOGINS = new WritableObjectPropertyKey<>();
+
     /** The currently expanded section (may be null). */
     public static final WritableObjectPropertyKey<AssistantVerticalExpander> EXPANDED_SECTION =
             new WritableObjectPropertyKey<>();
@@ -87,11 +102,12 @@
 
     public AssistantPaymentRequestModel() {
         super(DELEGATE, WEB_CONTENTS, VISIBLE, SHIPPING_ADDRESS, PAYMENT_METHOD, CONTACT_DETAILS,
-                TERMS_STATUS, REQUEST_NAME, REQUEST_EMAIL, REQUEST_PHONE, REQUEST_SHIPPING_ADDRESS,
-                REQUEST_PAYMENT, ACCEPT_TERMS_AND_CONDITIONS_TEXT, SHOW_TERMS_AS_CHECKBOX,
+                LOGIN_SECTION_TITLE, SELECTED_LOGIN, TERMS_STATUS, REQUEST_NAME, REQUEST_EMAIL,
+                REQUEST_PHONE, REQUEST_SHIPPING_ADDRESS, REQUEST_PAYMENT,
+                ACCEPT_TERMS_AND_CONDITIONS_TEXT, SHOW_TERMS_AS_CHECKBOX, REQUEST_LOGIN_CHOICE,
                 AVAILABLE_PROFILES, AVAILABLE_AUTOFILL_PAYMENT_METHODS,
-                SUPPORTED_BASIC_CARD_NETWORKS, SUPPORTED_PAYMENT_METHODS, EXPANDED_SECTION,
-                REQUIRE_BILLING_POSTAL_CODE, BILLING_POSTAL_CODE_MISSING_TEXT);
+                SUPPORTED_BASIC_CARD_NETWORKS, SUPPORTED_PAYMENT_METHODS, AVAILABLE_LOGINS,
+                EXPANDED_SECTION, REQUIRE_BILLING_POSTAL_CODE, BILLING_POSTAL_CODE_MISSING_TEXT);
 
         /**
          * Set initial state for basic type properties (others are implicitly null).
@@ -104,6 +120,7 @@
         set(REQUEST_PHONE, false);
         set(REQUEST_PAYMENT, false);
         set(REQUEST_SHIPPING_ADDRESS, false);
+        set(REQUEST_LOGIN_CHOICE, false);
         set(REQUIRE_BILLING_POSTAL_CODE, false);
     }
 
@@ -153,6 +170,16 @@
     }
 
     @CalledByNative
+    private void setLoginSectionTitle(String loginSectionTitle) {
+        set(LOGIN_SECTION_TITLE, loginSectionTitle);
+    }
+
+    @CalledByNative
+    private void setRequestLoginChoice(boolean requestLoginChoice) {
+        set(REQUEST_LOGIN_CHOICE, requestLoginChoice);
+    }
+
+    @CalledByNative
     private void setSupportedBasicCardNetworks(String[] supportedBasicCardNetworks) {
         set(SUPPORTED_BASIC_CARD_NETWORKS, Arrays.asList(supportedBasicCardNetworks));
     }
@@ -176,4 +203,23 @@
     private void setDelegate(AssistantPaymentRequestDelegate delegate) {
         set(DELEGATE, delegate);
     }
+
+    /** Creates an empty list of login options. */
+    @CalledByNative
+    private static List<AssistantPaymentRequestLoginChoice> createLoginChoiceList() {
+        return new ArrayList<>();
+    }
+
+    /** Appends a login choice to {@code logins}. */
+    @CalledByNative
+    private void addLoginChoice(List<AssistantPaymentRequestLoginChoice> loginChoices,
+            String identifier, String label, int priority) {
+        loginChoices.add(new AssistantPaymentRequestLoginChoice(identifier, label, priority));
+    }
+
+    /** Sets the list of available login choices. */
+    @CalledByNative
+    private void setLoginChoices(List<AssistantPaymentRequestLoginChoice> loginChoices) {
+        set(AVAILABLE_LOGINS, loginChoices);
+    }
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestNativeDelegate.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestNativeDelegate.java
index a583578..4a25351f 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestNativeDelegate.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestNativeDelegate.java
@@ -75,6 +75,14 @@
         }
     }
 
+    @Override
+    public void onLoginChoiceChanged(AssistantPaymentRequestLoginChoice loginChoice) {
+        if (mNativeAssistantPaymentRequestDelegate != 0) {
+            nativeOnLoginChoiceChanged(mNativeAssistantPaymentRequestDelegate,
+                    loginChoice != null ? loginChoice.getIdentifier() : null);
+        }
+    }
+
     @CalledByNative
     private void clearNativePtr() {
         mNativeAssistantPaymentRequestDelegate = 0;
@@ -90,4 +98,6 @@
             long nativeAssistantPaymentRequestDelegate, int state);
     private native void nativeOnTermsAndConditionsLinkClicked(
             long nativeAssistantPaymentRequestDelegate, int link);
+    private native void nativeOnLoginChoiceChanged(
+            long nativeAssistantPaymentRequestDelegate, String choice);
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestPaymentMethodSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestPaymentMethodSection.java
index e4d7ca0..3760eb3 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestPaymentMethodSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestPaymentMethodSection.java
@@ -36,9 +36,9 @@
                 R.layout.autofill_assistant_payment_method_full,
                 context.getResources().getDimensionPixelSize(
                         R.dimen.autofill_assistant_payment_request_payment_method_title_padding),
-                context.getString(R.string.payments_method_of_payment_label),
                 context.getString(R.string.payments_add_card),
-                context.getString(R.string.payments_add_card));
+                context.getString(R.string.payments_add_card), /*canEditItems=*/true);
+        setTitle(context.getString(R.string.payments_method_of_payment_label));
     }
 
     public void setEditor(CardEditor editor) {
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestSection.java
index c262d95..dd299c3 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestSection.java
@@ -13,8 +13,11 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.chrome.autofill_assistant.R;
+import org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting;
+import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
 import org.chromium.chrome.browser.payments.ui.SectionInformation;
 import org.chromium.chrome.browser.widget.prefeditor.EditableOption;
 
@@ -28,13 +31,14 @@
  * such as |AutofillContact|, |AutofillPaymentMethod|, etc.
  */
 public abstract class AssistantPaymentRequestSection<T extends EditableOption> {
-    private final View mTitleAddButton;
-    private final AssistantVerticalExpander mSectionExpander;
-    private final AssistantChoiceList mItemsView;
+    private final @Nullable View mTitleAddButton;
+    protected final AssistantVerticalExpander mSectionExpander;
+    protected final AssistantChoiceList mItemsView;
     private final View mSummaryView;
     private final int mFullViewResId;
     private final int mTitleToContentPadding;
     private final List<Item> mItems;
+    private final boolean mCanEditItems;
 
     protected final Context mContext;
     protected T mSelectedOption;
@@ -60,26 +64,27 @@
      * @param summaryViewResId The resource ID of the summary view to inflate.
      * @param fullViewResId The resource ID of the full view to inflate.
      * @param titleToContentPadding The amount of padding between title and content views.
-     * @param title The title string to display.
-     * @param titleAddButton The string to display in the title add button.
-     * @param listAddButton The string to display in the add button at the bottom of the list.
+     * @param titleAddButton The string to display in the title add button. Can be null if no add
+     *         button should be created.
+     * @param listAddButton The string to display in the add button at the bottom of the list. Can
+     *         be null if no add button should be created.
+     * @param canEditItems Whether items can be edited (i.e., show an edit button) or not.
      */
     public AssistantPaymentRequestSection(Context context, ViewGroup parent, int summaryViewResId,
-            int fullViewResId, int titleToContentPadding, String title, String titleAddButton,
-            String listAddButton) {
+            int fullViewResId, int titleToContentPadding, @Nullable String titleAddButton,
+            @Nullable String listAddButton, boolean canEditItems) {
         mContext = context;
         mFullViewResId = fullViewResId;
         mItems = new ArrayList<>();
         mTitleToContentPadding = titleToContentPadding;
+        mCanEditItems = canEditItems;
 
         LayoutInflater inflater = LayoutInflater.from(context);
         mSectionExpander = new AssistantVerticalExpander(context, null);
         View sectionTitle =
                 inflater.inflate(R.layout.autofill_assistant_payment_request_section_title, null);
         mSummaryView = inflater.inflate(summaryViewResId, null);
-        mItemsView = (AssistantChoiceList) inflater.inflate(
-                R.layout.autofill_assistant_payment_request_choice_list, null);
-        mItemsView.setAddButtonLabel(listAddButton);
+        mItemsView = createChoiceList(listAddButton);
 
         mSectionExpander.setTitleView(sectionTitle,
                 new LinearLayout.LayoutParams(
@@ -100,17 +105,17 @@
         setHorizontalMargins(mSummaryView, horizontalMargin, 0);
         setHorizontalMargins(mItemsView, 0, 0);
 
-        TextView titleView = mSectionExpander.findViewById(R.id.section_title);
-        titleView.setText(title);
+        if (titleAddButton == null) {
+            mSectionExpander.findViewById(R.id.section_title_add_button).setVisibility(View.GONE);
+            mTitleAddButton = null;
+        } else {
+            mTitleAddButton = mSectionExpander.findViewById(R.id.section_title_add_button);
+            TextView titleAddButtonLabelView =
+                    mSectionExpander.findViewById(R.id.section_title_add_button_label);
+            titleAddButtonLabelView.setText(titleAddButton);
+            mTitleAddButton.setOnClickListener(unusedView -> createOrEditItem(null));
+        }
 
-        TextView titleAddButtonLabelView =
-                mSectionExpander.findViewById(R.id.section_title_add_button_label);
-        titleAddButtonLabelView.setText(titleAddButton);
-
-        mTitleAddButton = mSectionExpander.findViewById(R.id.section_title_add_button);
-        mTitleAddButton.setOnClickListener(unusedView -> createOrEditItem(null));
-
-        mItemsView.setOnAddButtonClickedListener(() -> createOrEditItem(null));
         parent.addView(mSectionExpander,
                 new ViewGroup.LayoutParams(
                         ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
@@ -129,6 +134,11 @@
         mListener = listener;
     }
 
+    void setTitle(String title) {
+        TextView titleView = mSectionExpander.findViewById(R.id.section_title);
+        AssistantTextUtils.applyVisualAppearanceTags(titleView, title, null);
+    }
+
     /**
      * Replaces the set of displayed items.
      *
@@ -227,6 +237,27 @@
         updatePaddings();
     }
 
+    private AssistantChoiceList createChoiceList(@Nullable String addButtonText) {
+        AssistantChoiceList list = new AssistantChoiceList(mContext, null, addButtonText, 0,
+                mContext.getResources().getDimensionPixelSize(
+                        R.dimen.autofill_assistant_payment_request_column_spacing));
+        int verticalPadding = mContext.getResources().getDimensionPixelSize(
+                R.dimen.autofill_assistant_payment_request_choice_top_bottom_padding);
+        list.setPadding(mContext.getResources().getDimensionPixelSize(
+                                R.dimen.autofill_assistant_bottombar_horizontal_spacing),
+                verticalPadding,
+                mContext.getResources().getDimensionPixelSize(
+                        R.dimen.autofill_assistant_payment_request_choice_list_padding_end),
+                verticalPadding);
+        list.setBackgroundColor(ApiCompatibilityUtils.getColor(
+                mContext.getResources(), R.color.payments_section_edit_background));
+        list.setTag(AssistantTagsForTesting.PAYMENT_REQUEST_CHOICE_LIST);
+        if (addButtonText != null) {
+            list.setOnAddButtonClickedListener(() -> createOrEditItem(null));
+        }
+        return list;
+    }
+
     private void updatePaddings() {
         View titleView = mSectionExpander.getTitleView();
         if (isEmpty()) {
@@ -273,7 +304,7 @@
      */
     private void addItem(Item item) {
         mItems.add(item);
-        mItemsView.addItem(item.mFullView, /*hasEditButton=*/true, selected -> {
+        mItemsView.addItem(item.mFullView, /*hasEditButton=*/mCanEditItems, selected -> {
             if (mIgnoreItemSelectedNotifications || !selected) {
                 return;
             }
@@ -338,7 +369,9 @@
     }
 
     private void updateVisibility() {
-        mTitleAddButton.setVisibility(isEmpty() ? View.VISIBLE : View.GONE);
+        if (mTitleAddButton != null) {
+            mTitleAddButton.setVisibility(isEmpty() ? View.VISIBLE : View.GONE);
+        }
         mSectionExpander.setFixed(isEmpty());
         mSectionExpander.setCollapsedVisible(!isEmpty());
         mSectionExpander.setExpandedVisible(!isEmpty());
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestShippingAddressSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestShippingAddressSection.java
index 891a40cb..2cab0661 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestShippingAddressSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestShippingAddressSection.java
@@ -32,9 +32,9 @@
                 R.layout.autofill_assistant_address_full,
                 context.getResources().getDimensionPixelSize(
                         R.dimen.autofill_assistant_payment_request_title_padding),
-                context.getString(R.string.payments_shipping_address_label),
                 context.getString(R.string.payments_add_address),
-                context.getString(R.string.payments_add_address));
+                context.getString(R.string.payments_add_address), /*canEditItems=*/true);
+        setTitle(context.getString(R.string.payments_shipping_address_label));
     }
 
     public void setEditor(AddressEditor editor) {
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestTermsSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestTermsSection.java
index d614651..dde36b0 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestTermsSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestTermsSection.java
@@ -6,8 +6,6 @@
 
 import android.content.Context;
 import android.support.annotation.Nullable;
-import android.text.method.LinkMovementMethod;
-import android.text.style.StyleSpan;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -18,23 +16,12 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting;
-import org.chromium.ui.text.NoUnderlineClickableSpan;
-import org.chromium.ui.text.SpanApplier;
-import org.chromium.ui.text.SpanApplier.SpanInfo;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
 
 /**
  * The third party terms and conditions section of the Autofill Assistant payment request.
  */
 public class AssistantPaymentRequestTermsSection {
-    private static final Pattern LINK_PATTERN = Pattern.compile("<link(\\d+)>");
-
     interface Delegate {
         void onStateChanged(@AssistantTermsAndConditionsState int state);
 
@@ -50,8 +37,6 @@
     @Nullable
     private Delegate mDelegate;
 
-    private final SpanInfo mBoldSpanInfo =
-            new SpanInfo("<b>", "</b>", new StyleSpan(android.graphics.Typeface.BOLD));
     private String mOrigin = "";
     private String mAcceptTermsText = "";
 
@@ -104,43 +89,20 @@
         mOrigin = origin;
 
         setAcceptTermsText();
-
         Context context = mView.getContext();
         if (mTermsRequiresReview != null) {
-            mTermsRequiresReview.setText(SpanApplier.applySpans(
+            AssistantTextUtils.applyVisualAppearanceTags(mTermsRequiresReview,
                     context.getString(R.string.autofill_assistant_3rd_party_terms_review, origin),
-                    mBoldSpanInfo));
+                    /* linkCallback= */ null);
         }
-
-        mThirdPartyPrivacyNotice.setText(SpanApplier.applySpans(
+        AssistantTextUtils.applyVisualAppearanceTags(mThirdPartyPrivacyNotice,
                 context.getString(R.string.autofill_assistant_3rd_party_privacy_notice, origin),
-                mBoldSpanInfo));
+                /* linkCallback= */ null);
     }
 
     private void setAcceptTermsText() {
-        if (mAcceptTermsText.isEmpty()) return;
-
-        List<SpanInfo> spans = new ArrayList<>();
-        if (mAcceptTermsText.contains("<b>")) {
-            spans.add(mBoldSpanInfo);
-        }
-
-        // We first collect the link IDs into a set to allow multiple links with same ID.
-        Set<Integer> linkIds = new HashSet<>();
-        Matcher matcher = LINK_PATTERN.matcher(mAcceptTermsText);
-        while (matcher.find()) {
-            linkIds.add(Integer.parseInt(matcher.group(1)));
-        }
-
-        for (Integer linkId : linkIds) {
-            spans.add(new SpanInfo("<link" + linkId + ">", "</link" + linkId + ">",
-                    new NoUnderlineClickableSpan(mView.getContext().getResources(),
-                            (unusedView) -> onTermsAndConditionsLinkClicked(linkId))));
-        }
-
-        mTermsAgree.setText(SpanApplier.applySpans(String.format(mAcceptTermsText, mOrigin),
-                spans.toArray(new SpanInfo[spans.size()])));
-        mTermsAgree.setMovementMethod(linkIds.isEmpty() ? null : LinkMovementMethod.getInstance());
+        AssistantTextUtils.applyVisualAppearanceTags(mTermsAgree,
+                String.format(mAcceptTermsText, mOrigin), this::onTermsAndConditionsLinkClicked);
     }
 
     private void onTermsAndConditionsLinkClicked(int link) {
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestTestHelper.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestTestHelper.java
index af012df..ada634b 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestTestHelper.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestTestHelper.java
@@ -4,19 +4,22 @@
 
 package org.chromium.chrome.browser.autofill_assistant;
 
+import static org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting.PAYMENT_REQUEST_CHOICE_LIST;
+import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.findViewsWithTag;
 import static org.chromium.chrome.browser.autofill_assistant.payment.AssistantPaymentRequestCoordinator.DIVIDER_TAG;
 
 import android.support.annotation.Nullable;
 import android.view.View;
-import android.view.ViewGroup;
 
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
+import org.chromium.chrome.browser.autofill_assistant.payment.AssistantChoiceList;
 import org.chromium.chrome.browser.autofill_assistant.payment.AssistantPaymentRequestCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.payment.AssistantPaymentRequestDelegate;
+import org.chromium.chrome.browser.autofill_assistant.payment.AssistantPaymentRequestLoginChoice;
 import org.chromium.chrome.browser.autofill_assistant.payment.AssistantTermsAndConditionsState;
 import org.chromium.chrome.browser.autofill_assistant.payment.AssistantVerticalExpander;
 import org.chromium.chrome.browser.autofill_assistant.payment.AssistantVerticalExpanderAccordion;
@@ -25,7 +28,6 @@
 import org.chromium.chrome.browser.payments.AutofillPaymentInstrument;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.TimeoutException;
 
@@ -42,6 +44,11 @@
         final AssistantVerticalExpander mContactSection;
         final AssistantVerticalExpander mPaymentSection;
         final AssistantVerticalExpander mShippingSection;
+        final AssistantVerticalExpander mLoginsSection;
+        final AssistantChoiceList mContactList;
+        final AssistantChoiceList mPaymentMethodList;
+        final AssistantChoiceList mShippingAddressList;
+        final AssistantChoiceList mLoginList;
         final List<View> mDividers;
 
         ViewHolder(AssistantPaymentRequestCoordinator coordinator) {
@@ -53,21 +60,21 @@
                     AssistantTagsForTesting.PAYMENT_REQUEST_PAYMENT_METHOD_SECTION_TAG);
             mShippingSection = coordinator.getView().findViewWithTag(
                     AssistantTagsForTesting.PAYMENT_REQUEST_SHIPPING_ADDRESS_SECTION_TAG);
+            mLoginsSection = coordinator.getView().findViewWithTag(
+                    AssistantTagsForTesting.PAYMENT_REQUEST_LOGIN_SECTION_TAG);
             mDividers = findViewsWithTag(coordinator.getView(), DIVIDER_TAG);
-        }
-
-        /** Returns all views with a matching tag. */
-        private List<View> findViewsWithTag(View view, Object tag) {
-            List<View> viewsWithTag = new ArrayList<>();
-            if (view instanceof ViewGroup) {
-                for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
-                    viewsWithTag.addAll(findViewsWithTag(((ViewGroup) view).getChildAt(i), tag));
-                }
-            }
-            if (view.getTag() != null && view.getTag().equals(tag)) {
-                viewsWithTag.add(view);
-            }
-            return viewsWithTag;
+            mContactList = (AssistantChoiceList) (findViewsWithTag(
+                    mContactSection, PAYMENT_REQUEST_CHOICE_LIST)
+                                                          .get(0));
+            mPaymentMethodList = (AssistantChoiceList) (findViewsWithTag(
+                    mPaymentSection, PAYMENT_REQUEST_CHOICE_LIST)
+                                                                .get(0));
+            mShippingAddressList = (AssistantChoiceList) (findViewsWithTag(
+                    mShippingSection, PAYMENT_REQUEST_CHOICE_LIST)
+                                                                  .get(0));
+            mLoginList = (AssistantChoiceList) (findViewsWithTag(
+                    mLoginsSection, PAYMENT_REQUEST_CHOICE_LIST)
+                                                        .get(0));
         }
     }
 
@@ -80,6 +87,7 @@
         AutofillContact mContact;
         AutofillAddress mAddress;
         AutofillPaymentInstrument mPaymentMethod;
+        AssistantPaymentRequestLoginChoice mLoginChoice;
         @AssistantTermsAndConditionsState
         int mTermsStatus;
         @Nullable
@@ -106,6 +114,11 @@
         }
 
         @Override
+        public void onLoginChoiceChanged(@Nullable AssistantPaymentRequestLoginChoice loginChoice) {
+            mLoginChoice = loginChoice;
+        }
+
+        @Override
         public void onTermsAndConditionsLinkClicked(int link) {
             mLastLinkClicked = link;
         }
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestUiTest.java
index 74962faf..f0c0f063 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestUiTest.java
@@ -21,6 +21,7 @@
 import static org.hamcrest.core.AllOf.allOf;
 import static org.junit.Assert.assertThat;
 
+import static org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting.PAYMENT_REQUEST_CHOICE_LIST;
 import static org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting.PAYMENT_REQUEST_TERMS_REQUIRE_REVIEW;
 import static org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting.VERTICAL_EXPANDER_CHEVRON;
 
@@ -39,14 +40,16 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantPaymentRequestTestHelper.ViewHolder;
-import org.chromium.chrome.browser.autofill_assistant.payment.AssistantChoiceList;
 import org.chromium.chrome.browser.autofill_assistant.payment.AssistantPaymentRequestCoordinator;
+import org.chromium.chrome.browser.autofill_assistant.payment.AssistantPaymentRequestLoginChoice;
 import org.chromium.chrome.browser.autofill_assistant.payment.AssistantPaymentRequestModel;
 import org.chromium.chrome.browser.autofill_assistant.payment.AssistantTermsAndConditionsState;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
+import java.util.Collections;
+
 /**
  * Tests for the Autofill Assistant payment request UI.
  */
@@ -102,6 +105,7 @@
         assertThat(model.get(AssistantPaymentRequestModel.CONTACT_DETAILS), nullValue());
         assertThat(model.get(AssistantPaymentRequestModel.TERMS_STATUS),
                 is(AssistantTermsAndConditionsState.NOT_SELECTED));
+        assertThat(model.get(AssistantPaymentRequestModel.SELECTED_LOGIN), nullValue());
 
         /* Test initial UI state. */
         AutofillAssistantPaymentRequestTestHelper
@@ -109,15 +113,18 @@
                 () -> new AutofillAssistantPaymentRequestTestHelper.ViewHolder(coordinator));
 
         onView(is(coordinator.getView())).check(matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.section_choice_list),
+        onView(allOf(withTagValue(is(PAYMENT_REQUEST_CHOICE_LIST)),
                        isDescendantOfA(is(viewHolder.mContactSection))))
                 .check(matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.section_choice_list),
+        onView(allOf(withTagValue(is(PAYMENT_REQUEST_CHOICE_LIST)),
                        isDescendantOfA(is(viewHolder.mPaymentSection))))
                 .check(matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.section_choice_list),
+        onView(allOf(withTagValue(is(PAYMENT_REQUEST_CHOICE_LIST)),
                        isDescendantOfA(is(viewHolder.mShippingSection))))
                 .check(matches(not(isDisplayed())));
+        onView(allOf(withTagValue(is(PAYMENT_REQUEST_CHOICE_LIST)),
+                       isDescendantOfA(is(viewHolder.mLoginsSection))))
+                .check(matches(not(isDisplayed())));
 
         /* No section divider is visible. */
         for (View divider : viewHolder.mDividers) {
@@ -189,6 +196,11 @@
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> model.set(AssistantPaymentRequestModel.REQUEST_SHIPPING_ADDRESS, true));
         onView(is(viewHolder.mShippingSection)).check(matches(isDisplayed()));
+
+        /* Login section visibility test. */
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> model.set(AssistantPaymentRequestModel.REQUEST_LOGIN_CHOICE, true));
+        onView(is(viewHolder.mLoginsSection)).check(matches(isDisplayed()));
     }
 
     /**
@@ -213,6 +225,7 @@
             model.set(AssistantPaymentRequestModel.REQUEST_EMAIL, true);
             model.set(AssistantPaymentRequestModel.REQUEST_PAYMENT, true);
             model.set(AssistantPaymentRequestModel.REQUEST_SHIPPING_ADDRESS, true);
+            model.set(AssistantPaymentRequestModel.REQUEST_LOGIN_CHOICE, true);
             model.set(AssistantPaymentRequestModel.DELEGATE, delegate);
             model.set(AssistantPaymentRequestModel.VISIBLE, true);
         });
@@ -227,6 +240,10 @@
         onView(allOf(withId(R.id.section_title_add_button),
                        isDescendantOfA(is(viewHolder.mShippingSection))))
                 .check(matches(isDisplayed()));
+        /* ... Except for the logins section, which currently does not support adding items.*/
+        onView(allOf(withId(R.id.section_title_add_button),
+                       isDescendantOfA(is(viewHolder.mLoginsSection))))
+                .check(matches(not(isDisplayed())));
 
         /* Empty sections should be 'fixed', i.e., they can not be expanded. */
         onView(allOf(withTagValue(is(VERTICAL_EXPANDER_CHEVRON)),
@@ -240,27 +257,22 @@
                 .check(matches(not(isDisplayed())));
 
         /* Empty sections are collapsed. */
-        onView(allOf(withId(R.id.section_choice_list),
+        onView(allOf(withTagValue(is(PAYMENT_REQUEST_CHOICE_LIST)),
                        isDescendantOfA(is(viewHolder.mContactSection))))
                 .check(matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.section_choice_list),
+        onView(allOf(withTagValue(is(PAYMENT_REQUEST_CHOICE_LIST)),
                        isDescendantOfA(is(viewHolder.mPaymentSection))))
                 .check(matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.section_choice_list),
+        onView(allOf(withTagValue(is(PAYMENT_REQUEST_CHOICE_LIST)),
                        isDescendantOfA(is(viewHolder.mShippingSection))))
                 .check(matches(not(isDisplayed())));
 
         /* Empty sections should be empty. */
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            AssistantChoiceList contactsList =
-                    viewHolder.mContactSection.findViewById(R.id.section_choice_list);
-            AssistantChoiceList paymentsList =
-                    viewHolder.mPaymentSection.findViewById(R.id.section_choice_list);
-            AssistantChoiceList shippingList =
-                    viewHolder.mShippingSection.findViewById(R.id.section_choice_list);
-            assertThat(contactsList.getItemCount(), is(0));
-            assertThat(paymentsList.getItemCount(), is(0));
-            assertThat(shippingList.getItemCount(), is(0));
+            assertThat(viewHolder.mContactList.getItemCount(), is(0));
+            assertThat(viewHolder.mPaymentMethodList.getItemCount(), is(0));
+            assertThat(viewHolder.mShippingAddressList.getItemCount(), is(0));
+            assertThat(viewHolder.mLoginList.getItemCount(), is(0));
         });
 
         /* Test delegate status. */
@@ -268,6 +280,7 @@
         assertThat(delegate.mContact, nullValue());
         assertThat(delegate.mAddress, nullValue());
         assertThat(delegate.mTermsStatus, is(AssistantTermsAndConditionsState.NOT_SELECTED));
+        assertThat(delegate.mLoginChoice, nullValue());
     }
 
     /**
@@ -295,9 +308,7 @@
         onView(allOf(withId(R.id.section_title_add_button),
                        isDescendantOfA(is(viewHolder.mContactSection))))
                 .check(matches(isDisplayed()));
-        AssistantChoiceList contactsList = TestThreadUtils.runOnUiThreadBlocking(
-                () -> viewHolder.mContactSection.findViewById(R.id.section_choice_list));
-        assertThat(contactsList.getItemCount(), is(0));
+        assertThat(viewHolder.mContactList.getItemCount(), is(0));
 
         /* Add profile to the personal data manager. */
         String profileId = mHelper.addDummyProfile("John Doe", "john@gmail.com");
@@ -306,7 +317,7 @@
         onView(allOf(withId(R.id.section_title_add_button),
                        isDescendantOfA(is(viewHolder.mContactSection))))
                 .check(matches(not(isDisplayed())));
-        assertThat(contactsList.getItemCount(), is(1));
+        assertThat(viewHolder.mContactList.getItemCount(), is(1));
         onView(allOf(withId(R.id.contact_summary),
                        isDescendantOfA(is(viewHolder.mContactSection.getCollapsedView()))))
                 .check(matches(withText("john@gmail.com")));
@@ -316,7 +327,7 @@
         onView(allOf(withId(R.id.section_title_add_button),
                        isDescendantOfA(is(viewHolder.mContactSection))))
                 .check(matches(isDisplayed()));
-        assertThat(contactsList.getItemCount(), is(0));
+        assertThat(viewHolder.mContactList.getItemCount(), is(0));
 
         /* Tap the 'add' button to open the editor, to make sure that it still works. */
         onView(allOf(withId(R.id.section_title_add_button),
@@ -349,9 +360,7 @@
         onView(allOf(withId(R.id.section_title_add_button),
                        isDescendantOfA(is(viewHolder.mPaymentSection))))
                 .check(matches(isDisplayed()));
-        AssistantChoiceList paymentsList = TestThreadUtils.runOnUiThreadBlocking(
-                () -> viewHolder.mPaymentSection.findViewById(R.id.section_choice_list));
-        assertThat(paymentsList.getItemCount(), is(0));
+        assertThat(viewHolder.mPaymentMethodList.getItemCount(), is(0));
 
         /* Add profile and credit card to the personal data manager. */
         String billingAddressId = mHelper.addDummyProfile("Jill Doe", "jill@gmail.com");
@@ -361,8 +370,9 @@
         onView(allOf(withId(R.id.section_title_add_button),
                        isDescendantOfA(is(viewHolder.mPaymentSection))))
                 .check(matches(not(isDisplayed())));
-        assertThat(paymentsList.getItemCount(), is(1));
-        onView(allOf(withId(R.id.credit_card_name), isDescendantOfA(is(paymentsList.getItem(0)))))
+        assertThat(viewHolder.mPaymentMethodList.getItemCount(), is(1));
+        onView(allOf(withId(R.id.credit_card_name),
+                       isDescendantOfA(is(viewHolder.mPaymentMethodList.getItem(0)))))
                 .check(matches(withText("Jill Doe")));
 
         /* Remove credit card from personal data manager. Section should be empty again. */
@@ -370,7 +380,7 @@
         onView(allOf(withId(R.id.section_title_add_button),
                        isDescendantOfA(is(viewHolder.mPaymentSection))))
                 .check(matches(isDisplayed()));
-        assertThat(paymentsList.getItemCount(), is(0));
+        assertThat(viewHolder.mPaymentMethodList.getItemCount(), is(0));
 
         /* Tap the 'add' button to open the editor, to make sure that it still works. */
         onView(allOf(withId(R.id.section_title_add_button),
@@ -415,6 +425,10 @@
             model.set(AssistantPaymentRequestModel.REQUEST_SHIPPING_ADDRESS, true);
             model.set(AssistantPaymentRequestModel.DELEGATE, delegate);
             model.set(AssistantPaymentRequestModel.VISIBLE, true);
+            model.set(AssistantPaymentRequestModel.REQUEST_LOGIN_CHOICE, true);
+            model.set(AssistantPaymentRequestModel.AVAILABLE_LOGINS,
+                    Collections.singletonList(
+                            new AssistantPaymentRequestLoginChoice("id", "Guest", 0)));
         });
 
         /* Non-empty sections should not display the 'add' button in their title. */
@@ -427,6 +441,9 @@
         onView(allOf(withId(R.id.section_title_add_button),
                        isDescendantOfA(is(viewHolder.mShippingSection))))
                 .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.section_title_add_button),
+                       isDescendantOfA(is(viewHolder.mLoginsSection))))
+                .check(matches(not(isDisplayed())));
 
         /* Non-empty sections should not be 'fixed', i.e., they can be expanded. */
         onView(allOf(withTagValue(is(VERTICAL_EXPANDER_CHEVRON)),
@@ -438,6 +455,9 @@
         onView(allOf(withTagValue(is(VERTICAL_EXPANDER_CHEVRON)),
                        isDescendantOfA(is(viewHolder.mShippingSection))))
                 .check(matches(isDisplayed()));
+        onView(allOf(withTagValue(is(VERTICAL_EXPANDER_CHEVRON)),
+                       isDescendantOfA(is(viewHolder.mLoginsSection))))
+                .check(matches(isDisplayed()));
 
         /* All section dividers are visible. */
         for (View divider : viewHolder.mDividers) {
@@ -445,24 +465,22 @@
         }
 
         /* Check contents of sections. */
-        AssistantChoiceList contactsList = TestThreadUtils.runOnUiThreadBlocking(
-                () -> viewHolder.mContactSection.findViewById(R.id.section_choice_list));
-        AssistantChoiceList paymentsList = TestThreadUtils.runOnUiThreadBlocking(
-                () -> viewHolder.mPaymentSection.findViewById(R.id.section_choice_list));
-        AssistantChoiceList shippingList = TestThreadUtils.runOnUiThreadBlocking(
-                () -> viewHolder.mShippingSection.findViewById(R.id.section_choice_list));
-
-        assertThat(contactsList.getItemCount(), is(1));
-        assertThat(paymentsList.getItemCount(), is(1));
-        assertThat(shippingList.getItemCount(), is(1));
+        assertThat(viewHolder.mContactList.getItemCount(), is(1));
+        assertThat(viewHolder.mPaymentMethodList.getItemCount(), is(1));
+        assertThat(viewHolder.mShippingAddressList.getItemCount(), is(1));
+        assertThat(viewHolder.mLoginList.getItemCount(), is(1));
 
         testContact("maggie@simpson.com", "Maggie Simpson\nmaggie@simpson.com",
-                viewHolder.mContactSection.getCollapsedView(), contactsList.getItem(0));
+                viewHolder.mContactSection.getCollapsedView(), viewHolder.mContactList.getItem(0));
         testPaymentMethod("1111", "Jon Doe", "12/2050",
-                viewHolder.mPaymentSection.getCollapsedView(), paymentsList.getItem(0));
+                viewHolder.mPaymentSection.getCollapsedView(),
+                viewHolder.mPaymentMethodList.getItem(0));
         testShippingAddress("Maggie Simpson", "Acme Inc., 123 Main, 90210 Los Angeles, California",
                 "Acme Inc., 123 Main, 90210 Los Angeles, California, Uzbekistan",
-                viewHolder.mShippingSection.getCollapsedView(), shippingList.getItem(0));
+                viewHolder.mShippingSection.getCollapsedView(),
+                viewHolder.mShippingAddressList.getItem(0));
+        testLoginDetails("Guest", viewHolder.mLoginsSection.getCollapsedView(),
+                viewHolder.mLoginList.getItem(0));
 
         /* Check delegate status. */
         assertThat(delegate.mPaymentMethod.getCard().getNumber(), is("4111111111111111"));
@@ -476,6 +494,7 @@
         assertThat(delegate.mAddress.getProfile().getFullName(), is("Maggie Simpson"));
         assertThat(delegate.mAddress.getProfile().getStreetAddress(), containsString("123 Main"));
         assertThat(delegate.mTermsStatus, is(AssistantTermsAndConditionsState.NOT_SELECTED));
+        assertThat(delegate.mLoginChoice.getIdentifier(), is("id"));
     }
 
     /**
@@ -720,4 +739,11 @@
         onView(allOf(withId(R.id.incomplete_error), isDescendantOfA(is(fullView))))
                 .check(matches(not(isDisplayed())));
     }
+
+    private void testLoginDetails(String expectedLabel, View summaryView, View fullView) {
+        onView(allOf(withId(R.id.username), isDescendantOfA(is(summaryView))))
+                .check(matches(withText(expectedLabel)));
+        onView(allOf(withId(R.id.username), isDescendantOfA(is(fullView))))
+                .check(matches(withText(expectedLabel)));
+    }
 }
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java
index 92a7222..0a16d4e 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java
@@ -37,6 +37,9 @@
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import jp.tomorrowkey.android.gifplayer.BaseGifImage;
 
 /**
@@ -91,6 +94,20 @@
         };
     }
 
+    /** Returns all views with a matching tag. */
+    public static List<View> findViewsWithTag(View view, Object tag) {
+        List<View> viewsWithTag = new ArrayList<>();
+        if (view instanceof ViewGroup) {
+            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
+                viewsWithTag.addAll(findViewsWithTag(((ViewGroup) view).getChildAt(i), tag));
+            }
+        }
+        if (view.getTag() != null && view.getTag().equals(tag)) {
+            viewsWithTag.add(view);
+        }
+        return viewsWithTag;
+    }
+
     /**
      * Waits until {@code matcher} matches {@code condition}. Will automatically fail after a
      * default timeout.
diff --git a/chrome/android/features/keyboard_accessory/internal/BUILD.gn b/chrome/android/features/keyboard_accessory/internal/BUILD.gn
index ee860f2a..62670dd 100644
--- a/chrome/android/features/keyboard_accessory/internal/BUILD.gn
+++ b/chrome/android/features/keyboard_accessory/internal/BUILD.gn
@@ -65,6 +65,7 @@
     "java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessoryInfoView.java",
     "java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetCoordinator.java",
     "java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewBinder.java",
+    "java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/DynamicInfoViewBottomSpacer.java",
     "java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryInfoView.java",
     "java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetCoordinator.java",
     "java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewBinder.java",
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/address_accessory_sheet.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/address_accessory_sheet.xml
index 61ad525..2d0a899 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/address_accessory_sheet.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/address_accessory_sheet.xml
@@ -10,4 +10,4 @@
     android:layout_height="match_parent"
     android:layout_width="match_parent"
     android:clipToPadding="false"
-    android:paddingBottom="8dp"/>
+    android:paddingBottom="@dimen/keyboard_accessory_sheet_footer_trailing_margin"/>
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/credit_card_accessory_sheet.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/credit_card_accessory_sheet.xml
index 4dbcb82..49683b0 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/credit_card_accessory_sheet.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/credit_card_accessory_sheet.xml
@@ -10,4 +10,4 @@
     android:layout_height="match_parent"
     android:layout_width="match_parent"
     android:clipToPadding="false"
-    android:paddingBottom="8dp"/>
+    android:paddingBottom="@dimen/keyboard_accessory_sheet_footer_trailing_margin"/>
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_address_info.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_address_info.xml
index 5ea4e1a..b491a19 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_address_info.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_address_info.xml
@@ -46,7 +46,7 @@
     <LinearLayout
         android:gravity="center_vertical|start"
         android:fillViewport="true"
-        android:layout_height="@dimen/keyboard_accessory_suggestion_height"
+        android:layout_height="wrap_content"
         android:layout_width="match_parent"
         android:orientation="horizontal">
 
@@ -78,8 +78,6 @@
     <org.chromium.ui.widget.ChipView
         android:id="@+id/address_home_country"
         android:gravity="center_vertical|start"
-        android:layout_marginTop="@dimen/keyboard_accessory_sheet_padding"
-        android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         style="@style/InputChip" />
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_credit_card_info.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_credit_card_info.xml
index a5bc4e4..9edb35e9 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_credit_card_info.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_credit_card_info.xml
@@ -34,7 +34,6 @@
             <org.chromium.ui.widget.ChipView
                 android:id="@+id/cc_number"
                 android:gravity="center_vertical|start"
-                android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 style="@style/InputChip" />
@@ -43,7 +42,7 @@
                 android:id="@+id/exp_group"
                 android:gravity="center_vertical|start"
                 android:fillViewport="true"
-                android:layout_height="@dimen/keyboard_accessory_suggestion_height"
+                android:layout_height="wrap_content"
                 android:layout_width="match_parent"
                 android:orientation="horizontal">
 
@@ -77,7 +76,6 @@
             <org.chromium.ui.widget.ChipView
                 android:id="@+id/cardholder"
                 android:gravity="center_vertical|start"
-                android:layout_marginTop="@dimen/keyboard_accessory_sheet_padding"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 style="@style/InputChip" />
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/password_accessory_sheet.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/password_accessory_sheet.xml
index a8964ec..15f8885 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/password_accessory_sheet.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/password_accessory_sheet.xml
@@ -10,4 +10,4 @@
     android:layout_height="match_parent"
     android:layout_width="match_parent"
     android:clipToPadding="false"
-    android:paddingBottom="8dp"/>
+    android:paddingBottom="@dimen/keyboard_accessory_sheet_footer_trailing_margin"/>
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/values/dimens.xml b/chrome/android/features/keyboard_accessory/internal/java/res/values/dimens.xml
index b2a4b3bbd..a9e2837 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/values/dimens.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/values/dimens.xml
@@ -15,6 +15,7 @@
     <dimen name="keyboard_accessory_chip_vertical_margin">8dp</dimen>
     <!--dimen name="keyboard_accessory_sheet_height">330dp</dimen-->
     <dimen name="keyboard_accessory_sheet_divider_margin">24dp</dimen>
+    <dimen name="keyboard_accessory_sheet_footer_trailing_margin">64dp</dimen>
     <dimen name="keyboard_accessory_sheet_padding">8dp</dimen>
     <dimen name="keyboard_accessory_sheet_top_margin">16dp</dimen>
     <dimen name="keyboard_accessory_sheet_bottom_margin">8dp</dimen>
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessoryInfoView.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessoryInfoView.java
index 1460611..a65f96c 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessoryInfoView.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessoryInfoView.java
@@ -5,10 +5,7 @@
 package org.chromium.chrome.browser.keyboard_accessory.sheet_tabs;
 
 import android.content.Context;
-import android.graphics.Rect;
-import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
-import android.view.View;
 import android.widget.LinearLayout;
 
 import org.chromium.chrome.browser.keyboard_accessory.R;
@@ -30,26 +27,6 @@
     private ChipView mEmailAddress;
 
     /**
-     * This decoration adds a space between the last AddressAccessoryInfoView and the first
-     * non-AddressAccessoryInfoView. This allows to define a margin below the whole list of
-     * AddressAccessoryInfoViews without having to wrap them in a new layout. This would reduce
-     * the reusability of single info containers in a RecyclerView.
-     */
-    public static class DynamicBottomSpacer extends RecyclerView.ItemDecoration {
-        @Override
-        public void getItemOffsets(
-                Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
-            super.getItemOffsets(outRect, view, parent, state);
-            if (view instanceof AddressAccessoryInfoView) return;
-            int previous = parent.indexOfChild(view) - 1;
-            if (previous < 0) return;
-            if (!(parent.getChildAt(previous) instanceof AddressAccessoryInfoView)) return;
-            outRect.top = view.getContext().getResources().getDimensionPixelSize(
-                    R.dimen.keyboard_accessory_suggestion_padding);
-        }
-    }
-
-    /**
      * Constructor for inflating from XML.
      */
     public AddressAccessoryInfoView(Context context, AttributeSet attrs) {
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetViewBinder.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetViewBinder.java
index dda8664..aef73c7 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetViewBinder.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetViewBinder.java
@@ -74,6 +74,6 @@
 
     static void initializeView(RecyclerView view, AccessorySheetTabModel model) {
         view.setAdapter(AddressAccessorySheetCoordinator.createAdapter(model));
-        view.addItemDecoration(new AddressAccessoryInfoView.DynamicBottomSpacer());
+        view.addItemDecoration(new DynamicInfoViewBottomSpacer(AddressAccessoryInfoView.class));
     }
 }
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewBinder.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewBinder.java
index dccacd7..51f19a8f 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewBinder.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewBinder.java
@@ -103,5 +103,6 @@
                 new SimpleRecyclerViewMcp<>(model, AccessorySheetDataPiece::getType,
                         AccessorySheetTabViewBinder.ElementViewHolder::bind),
                 CreditCardAccessorySheetViewBinder::create));
+        view.addItemDecoration(new DynamicInfoViewBottomSpacer(CreditCardAccessoryInfoView.class));
     }
 }
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/DynamicInfoViewBottomSpacer.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/DynamicInfoViewBottomSpacer.java
new file mode 100644
index 0000000..f3c1980
--- /dev/null
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/DynamicInfoViewBottomSpacer.java
@@ -0,0 +1,40 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.keyboard_accessory.sheet_tabs;
+
+import android.graphics.Rect;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+import org.chromium.chrome.browser.keyboard_accessory.R;
+
+/**
+ * This decoration adds a space between the last info view and the first non-info view. This allows
+ * to define a margin below the whole list of info views without having to wrap them in a new
+ * layout. This would reduce the reusability of single info containers in a RecyclerView.
+ */
+class DynamicInfoViewBottomSpacer extends RecyclerView.ItemDecoration {
+    private final Class mInfoViewClass;
+
+    DynamicInfoViewBottomSpacer(Class infoViewClass) {
+        mInfoViewClass = infoViewClass;
+    }
+
+    @Override
+    public void getItemOffsets(
+            Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+        super.getItemOffsets(outRect, view, parent, state);
+        if (isUserInfoView(view)) return;
+        int previous = parent.indexOfChild(view) - 1;
+        if (previous < 0) return;
+        if (!isUserInfoView(parent.getChildAt(previous))) return;
+        outRect.top = view.getContext().getResources().getDimensionPixelSize(
+                R.dimen.keyboard_accessory_suggestion_padding);
+    }
+
+    private boolean isUserInfoView(View view) {
+        return view.getClass().isAssignableFrom(mInfoViewClass);
+    }
+}
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryInfoView.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryInfoView.java
index d13fb28..8f5db65 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryInfoView.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryInfoView.java
@@ -6,14 +6,11 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.Nullable;
 import android.support.v7.content.res.AppCompatResources;
-import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
-import android.view.View;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -31,26 +28,6 @@
     private ChipView mPassword;
 
     /**
-     * This decoration adds a space between the last PasswordAccessoryInfoView and the first
-     * non-PasswordAccessoryInfoView. This allows to define a margin below the whole list of
-     * PasswordAccessoryInfoViews without having to wrap them in a new layout. This would reduce
-     * the reusability of single info containers in a RecyclerView.
-     */
-    public static class DynamicBottomSpacer extends RecyclerView.ItemDecoration {
-        @Override
-        public void getItemOffsets(
-                Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
-            super.getItemOffsets(outRect, view, parent, state);
-            if (view instanceof PasswordAccessoryInfoView) return;
-            int previous = parent.indexOfChild(view) - 1;
-            if (previous < 0) return;
-            if (!(parent.getChildAt(previous) instanceof PasswordAccessoryInfoView)) return;
-            outRect.top = view.getContext().getResources().getDimensionPixelSize(
-                    R.dimen.keyboard_accessory_suggestion_padding);
-        }
-    }
-
-    /**
      * Constructor for inflating from XML.
      */
     public PasswordAccessoryInfoView(Context context, AttributeSet attrs) {
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewBinder.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewBinder.java
index 92a773c..a9b7f32e 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewBinder.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewBinder.java
@@ -75,6 +75,6 @@
 
     static void initializeView(RecyclerView view, AccessorySheetTabModel model) {
         view.setAdapter(PasswordAccessorySheetCoordinator.createModernAdapter(model));
-        view.addItemDecoration(new PasswordAccessoryInfoView.DynamicBottomSpacer());
+        view.addItemDecoration(new DynamicInfoViewBottomSpacer(PasswordAccessoryInfoView.class));
     }
 }
diff --git a/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected b/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
index 6931b4a..db68924 100644
--- a/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
+++ b/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
@@ -888,6 +888,13 @@
         android:process=":playcore_missing_splits_activity"
         android:stateNotNeeded="true"/>
     <activity
+        android:enabled="false"
+        android:exported="false"
+        android:name="com.google.android.play.core.common.PlayCoreDialogWrapperActivity"
+        android:process=":playcore_dialog_wrapper_activity"
+        android:stateNotNeeded="true"
+        android:theme="@style/Theme.PlayCore.Transparent"/>
+    <activity
         android:excludeFromRecents="true"
         android:exported="false"
         android:launchMode="singleInstance"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
index bedd235..54dfca0e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
@@ -50,6 +50,15 @@
     private static final int ACTION_CONTACTS_SELECTED = 1;
     private static final int ACTION_BOUNDARY = 2;
 
+    // These values are written to logs as bitmasks (combination of names/emails and/or telephones).
+    // New enum values can be added, but existing enums must never be renumbered or deleted and
+    // reused.
+    private static final int PROPERTIES_NONE = 0;
+    private static final int PROPERTIES_TELS = 1 << 0;
+    private static final int PROPERTIES_EMAILS = 1 << 1;
+    private static final int PROPERTIES_NAMES = 1 << 2;
+    private static final int PROPERTIES_BOUNDARY = 1 << 3;
+
     // Constants for the RoundedIconGenerator.
     private static final int ICON_SIZE_DP = 36;
     private static final int ICON_CORNER_RADIUS_DP = 20;
@@ -267,12 +276,14 @@
             mSelectionDelegate.setSelectedItems(
                     new HashSet<ContactDetails>(mPickerAdapter.getAllContacts()));
             mListener.onContactsPickerUserAction(
-                    ContactsPickerListener.ContactsPickerAction.SELECT_ALL, null, 0);
+                    ContactsPickerListener.ContactsPickerAction.SELECT_ALL, /*contacts=*/null,
+                    /*percentageShared=*/0, /*propertiesRequested=*/0);
         } else {
             mSelectionDelegate.setSelectedItems(new HashSet<ContactDetails>());
             mPreviousSelection = null;
             mListener.onContactsPickerUserAction(
-                    ContactsPickerListener.ContactsPickerAction.UNDO_SELECT_ALL, null, 0);
+                    ContactsPickerListener.ContactsPickerAction.UNDO_SELECT_ALL, /*contacts=*/null,
+                    /*percentageShared=*/0, /*propertiesRequested=*/0);
         }
     }
 
@@ -372,10 +383,18 @@
         int selectCount = contacts != null ? contacts.size() : 0;
         int contactCount = mPickerAdapter.getAllContacts().size();
         int percentageShared = (100 * selectCount) / contactCount;
-        mListener.onContactsPickerUserAction(action, contacts, percentageShared);
+
+        int propertiesRequested = PROPERTIES_NONE;
+        if (includeNames) propertiesRequested |= PROPERTIES_NAMES;
+        if (includeEmails) propertiesRequested |= PROPERTIES_EMAILS;
+        if (includeTel) propertiesRequested |= PROPERTIES_TELS;
+
+        mListener.onContactsPickerUserAction(
+                action, contacts, percentageShared, propertiesRequested);
         mDialog.dismiss();
         UiUtils.onContactsPickerDismissed();
-        recordFinalUmaStats(umaId, contactCount, selectCount, percentageShared);
+        recordFinalUmaStats(
+                umaId, contactCount, selectCount, percentageShared, propertiesRequested);
     }
 
     /**
@@ -384,15 +403,18 @@
      * @param contactCount The number of contacts in the contact list.
      * @param selectCount The number of contacts selected.
      * @param percentageShared The percentage shared (of the whole contact list).
+     * @param propertiesRequested The properties (names/emails/tels) requested by the website.
      */
-    private void recordFinalUmaStats(
-            int action, int contactCount, int selectCount, int percentageShared) {
+    private void recordFinalUmaStats(int action, int contactCount, int selectCount,
+            int percentageShared, int propertiesRequested) {
         RecordHistogram.recordEnumeratedHistogram(
                 "Android.ContactsPicker.DialogAction", action, ACTION_BOUNDARY);
         RecordHistogram.recordCountHistogram("Android.ContactsPicker.ContactCount", contactCount);
         RecordHistogram.recordCountHistogram("Android.ContactsPicker.SelectCount", selectCount);
         RecordHistogram.recordPercentageHistogram(
                 "Android.ContactsPicker.SelectPercentage", percentageShared);
+        RecordHistogram.recordEnumeratedHistogram("Android.ContactsPicker.PropertiesRequested",
+                propertiesRequested, PROPERTIES_BOUNDARY);
     }
 
     @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
index 48a3ad8..87ac314 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -20,6 +20,7 @@
 import android.text.InputType;
 import android.text.Layout;
 import android.text.Selection;
+import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
 import android.text.style.ReplacementSpan;
 import android.util.AttributeSet;
@@ -27,6 +28,7 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewStructure;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
@@ -154,6 +156,14 @@
     }
 
     /**
+     * An optional string to use with AccessibilityNodeInfo to report text content.
+     * This is particularly important for auto-fill applications, such as password managers, that
+     * rely on AccessibilityNodeInfo data to apply related form-fill data.
+     */
+    private CharSequence mTextForAutofillServices;
+    private boolean mRequestingAutofillStructure;
+
+    /**
      * Implement this to get updates when the direction of the text in the URL bar changes.
      * E.g. If the user is typing a URL, then erases it and starts typing a query in Arabic,
      * the direction will change from left-to-right to right-to-left.
@@ -589,6 +599,13 @@
         mTextChangeListener = listener;
     }
 
+    /**
+     * Set the text to report to Autofill services upon call to onProvideAutofillStructure.
+     */
+    public void setTextForAutofillServices(CharSequence text) {
+        mTextForAutofillServices = text;
+    }
+
     @Override
     public boolean onTextContextMenuItem(int id) {
         if (mTextContextMenuDelegate == null) return super.onTextContextMenuItem(id);
@@ -912,6 +929,21 @@
     }
 
     @Override
+    public void onProvideAutofillStructure(ViewStructure structure, int autofillFlags) {
+        // https://crbug.com/996402: Prevent breaking autofill services on newer versions of
+        // Android.
+        mRequestingAutofillStructure = true;
+        super.onProvideAutofillStructure(structure, autofillFlags);
+        mRequestingAutofillStructure = false;
+    }
+
+    @Override
+    public Editable getText() {
+        return mRequestingAutofillStructure ? new SpannableStringBuilder(mTextForAutofillServices)
+                                            : super.getText();
+    }
+
+    @Override
     public CharSequence getAccessibilityClassName() {
         // When UrlBar is used as a read-only TextView, force Talkback to pronounce it like
         // TextView. Otherwise Talkback will say "Edit box, http://...". crbug.com/636988
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
index 41463ac9..7f2da9e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
@@ -102,12 +102,18 @@
     private void pushTextToModel() {
         CharSequence text =
                 !mHasFocus ? mUrlBarData.displayText : mUrlBarData.getEditingOrDisplayText();
+        CharSequence textForAutofillServices = text;
+
+        if (!(mHasFocus || TextUtils.isEmpty(text) || mUrlBarData.url == null)) {
+            textForAutofillServices = mUrlBarData.url;
+        }
+
         @ScrollType
         int scrollType = mHasFocus ? UrlBar.ScrollType.NO_SCROLL : mScrollType;
         if (text == null) text = "";
 
-        UrlBarTextState state =
-                new UrlBarTextState(text, scrollType, mUrlBarData.originEndIndex, mSelectionState);
+        UrlBarTextState state = new UrlBarTextState(text, textForAutofillServices, scrollType,
+                mUrlBarData.originEndIndex, mSelectionState);
         mModel.set(UrlBarProperties.TEXT_STATE, state);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java
index e1f9d10..584624b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java
@@ -31,6 +31,9 @@
         /** Text to be shown. */
         public final CharSequence text;
 
+        /** Text for Autofill services. */
+        public final CharSequence textForAutofillServices;
+
         /** Specifies how the text should be scrolled in the unfocused state. */
         public final @ScrollType int scrollType;
 
@@ -40,9 +43,10 @@
         /** Specifies how the text should be selected in the focused state. */
         public final @SelectionState int selectionState;
 
-        public UrlBarTextState(CharSequence text, @ScrollType int scrollType, int scrollToIndex,
-                @SelectionState int selectionState) {
+        public UrlBarTextState(CharSequence text, CharSequence textForAutofillServices,
+                @ScrollType int scrollType, int scrollToIndex, @SelectionState int selectionState) {
             this.text = text;
+            this.textForAutofillServices = textForAutofillServices;
             this.scrollType = scrollType;
             this.scrollToIndex = scrollToIndex;
             this.selectionState = selectionState;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
index 6ed4066..fc68b14 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
@@ -55,6 +55,7 @@
             UrlBarTextState state = model.get(UrlBarProperties.TEXT_STATE);
             view.setIgnoreTextChangesForAutocomplete(true);
             view.setText(state.text);
+            view.setTextForAutofillServices(state.textForAutofillServices);
             view.setScrollState(state.scrollType, state.scrollToIndex);
             view.setIgnoreTextChangesForAutocomplete(false);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncher.java
new file mode 100644
index 0000000..e4a21ad6
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncher.java
@@ -0,0 +1,24 @@
+// 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.password_manager;
+
+import android.content.Intent;
+import android.net.Uri;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.ui.base.WindowAndroid;
+
+/**
+ * A utitily class for launching the password leak check.
+ */
+public class PasswordCheckupLauncher {
+    @CalledByNative
+    private static void launchCheckup(String checkupUrl, WindowAndroid windowAndroid) {
+        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(checkupUrl));
+        ChromeActivity activity = (ChromeActivity) windowAndroid.getActivity().get();
+        intent.setPackage(activity.getPackageName());
+        activity.startActivity(intent);
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contacts_picker/ContactsPickerDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contacts_picker/ContactsPickerDialogTest.java
index 4d39a52..c9ec819 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contacts_picker/ContactsPickerDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contacts_picker/ContactsPickerDialogTest.java
@@ -76,6 +76,13 @@
     // The percentage of contacts shared.
     private int mLastPercentageShared;
 
+    // The properties requested (names, emails, telephone numbers) when the
+    // dialog was opened (not to be confused with the properties that will
+    // eventually be returned, after the user opts-out of some). Note: This is
+    // a bitmask, where the first bit is 1 if telephone numbers were requested,
+    // second bit is for emails and third bit is for names.
+    private int mLastPropertiesRequested;
+
     // The list of currently selected contacts (built piecemeal).
     private List<ContactDetails> mCurrentContactSelection;
 
@@ -108,10 +115,12 @@
 
     @Override
     public void onContactsPickerUserAction(@ContactsPickerAction int action,
-            List<ContactsPickerListener.Contact> contacts, int percentageShared) {
+            List<ContactsPickerListener.Contact> contacts, int percentageShared,
+            int propertiesRequested) {
         mLastActionRecorded = action;
         mLastSelectedContacts = (contacts != null) ? new ArrayList<>(contacts) : null;
         mLastPercentageShared = percentageShared;
+        mLastPropertiesRequested = propertiesRequested;
         onActionCallback.notifyCalled();
     }
 
@@ -307,6 +316,7 @@
 
         Assert.assertEquals(null, mLastSelectedContacts);
         Assert.assertEquals(0, mLastPercentageShared);
+        Assert.assertEquals(7, mLastPropertiesRequested);
         Assert.assertEquals(ContactsPickerAction.CANCEL, mLastActionRecorded);
 
         dismissDialog();
@@ -331,6 +341,7 @@
         Assert.assertEquals(
                 mTestContacts.get(1).getDisplayName(), mLastSelectedContacts.get(0).names.get(0));
         Assert.assertEquals(16, mLastPercentageShared);
+        Assert.assertEquals(7, mLastPropertiesRequested);
 
         dismissDialog();
     }
@@ -359,6 +370,7 @@
         Assert.assertEquals(
                 mTestContacts.get(0).getDisplayName(), mLastSelectedContacts.get(2).names.get(0));
         Assert.assertEquals(50, mLastPercentageShared);
+        Assert.assertEquals(7, mLastPropertiesRequested);
 
         dismissDialog();
     }
@@ -383,6 +395,7 @@
         Assert.assertEquals(mTestContacts.get(0).getEmails().get(0),
                 mLastSelectedContacts.get(0).emails.get(0));
         Assert.assertEquals(16, mLastPercentageShared);
+        Assert.assertEquals(7, mLastPropertiesRequested);
 
         dismissDialog();
     }
@@ -407,6 +420,7 @@
                 mTestContacts.get(0).getDisplayName(), mLastSelectedContacts.get(0).names.get(0));
         Assert.assertEquals(new ArrayList<String>(), mLastSelectedContacts.get(0).emails);
         Assert.assertEquals(16, mLastPercentageShared);
+        Assert.assertEquals(7, mLastPropertiesRequested);
 
         dismissDialog();
     }
@@ -431,12 +445,44 @@
                 mTestContacts.get(0).getDisplayName(), mLastSelectedContacts.get(0).names.get(0));
         Assert.assertEquals(new ArrayList<String>(), mLastSelectedContacts.get(0).tel);
         Assert.assertEquals(16, mLastPercentageShared);
+        Assert.assertEquals(7, mLastPropertiesRequested);
 
         dismissDialog();
     }
 
     @Test
     @LargeTest
+    public void testPropertiesRequested() throws Throwable {
+        // Create a dialog showing names only.
+        createDialog(/* multiselect = */ false, /* includeNames = */ true,
+                /* includeEmails = */ false,
+                /* includeTel = */ false);
+        Assert.assertTrue(mDialog.isShowing());
+        clickCancel();
+        Assert.assertEquals(4, mLastPropertiesRequested);
+        dismissDialog();
+
+        // Create a dialog showing emails only.
+        createDialog(/* multiselect = */ false, /* includeNames = */ false,
+                /* includeEmails = */ true,
+                /* includeTel = */ false);
+        Assert.assertTrue(mDialog.isShowing());
+        clickCancel();
+        Assert.assertEquals(2, mLastPropertiesRequested);
+        dismissDialog();
+
+        // Create a dialog showing telephone numbers only.
+        createDialog(/* multiselect = */ false, /* includeNames = */ false,
+                /* includeEmails = */ false,
+                /* includeTel = */ true);
+        Assert.assertTrue(mDialog.isShowing());
+        clickCancel();
+        Assert.assertEquals(1, mLastPropertiesRequested);
+        dismissDialog();
+    }
+
+    @Test
+    @LargeTest
     public void testSelectAll() throws Throwable {
         createDialog(/* multiselect = */ true, /* includeNames = */ true,
                 /* includeEmails = */ true,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/UrlBarUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/UrlBarUnitTest.java
new file mode 100644
index 0000000..2d9915ce
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/UrlBarUnitTest.java
@@ -0,0 +1,64 @@
+// 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.omnibox;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.os.Build;
+import android.support.test.filters.SmallTest;
+import android.text.SpannableStringBuilder;
+import android.view.ViewStructure;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+
+import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.MinAndroidSdkLevel;
+import org.chromium.chrome.browser.omnibox.UrlBar.UrlBarDelegate;
+import org.chromium.testing.local.LocalRobolectricTestRunner;
+
+/**
+ * Unit tests for the URL bar UI component.
+ */
+@RunWith(LocalRobolectricTestRunner.class)
+public class UrlBarUnitTest {
+    private UrlBar mUrlBar;
+    @Mock
+    private UrlBarDelegate mUrlBarDelegate;
+    @Mock
+    private ViewStructure mViewStructure;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        Activity activity = Robolectric.buildActivity(Activity.class).setup().get();
+
+        mUrlBar = new UrlBar(activity, null);
+        mUrlBar.setDelegate(mUrlBarDelegate);
+    }
+
+    @Test
+    @SmallTest
+    @MinAndroidSdkLevel(Build.VERSION_CODES.O)
+    @Feature("Omnibox")
+    public void testAutofillStructureReceivesFullURL() throws InterruptedException {
+        mUrlBar.setTextForAutofillServices("https://www.google.com");
+        mUrlBar.setText("www.google.com");
+        mUrlBar.onProvideAutofillStructure(mViewStructure, 0);
+
+        ArgumentCaptor<SpannableStringBuilder> haveUrl =
+                ArgumentCaptor.forClass(SpannableStringBuilder.class);
+        verify(mViewStructure).setText(haveUrl.capture());
+        Assert.assertEquals("https://www.google.com", haveUrl.getValue().toString());
+    }
+}
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 155ab7fb..ecf95aa 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6666,21 +6666,19 @@
       <!-- Managed UI on pages like chrome://settings and chrome://downloads -->
       <if expr="not is_android">
         <if expr="chromeos">
-          <message name="IDS_MANAGED_WITH_HYPERLINK" desc="Message to end users in Enterprise/EDU, with a link for more info (ChromeOS)">
-            Your <ph name="BEGIN_LINK">&lt;a href="$1"&gt;</ph><ph name="DEVICE_TYPE">$2<ex>Chromebook</ex></ph> is managed<ph name="END_LINK">&lt;/a&gt;</ph> by your organization
+          <message name="IDS_DEVICE_MANAGED_WITH_HYPERLINK" desc="Message to end users in Enterprise/EDU, with a link for more info (ChromeOS)">
+            Your <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph><ph name="DEVICE_TYPE">$2<ex>Chromebook</ex></ph> is managed<ph name="END_LINK">&lt;/a&gt;</ph> by your organization
           </message>
-          <message name="IDS_MANAGED_BY_WITH_HYPERLINK" desc="Message to end users in Enterprise/EDU, with a link for more info (ChromeOS)">
-            Your <ph name="BEGIN_LINK">&lt;a href="$1"&gt;</ph><ph name="DEVICE_TYPE">$2<ex>Chromebook</ex></ph> is managed<ph name="END_LINK">&lt;/a&gt;</ph> by <ph name="ENROLLMENT_DOMAIN">$3<ex>example.com</ex></ph>
+          <message name="IDS_DEVICE_MANAGED_BY_WITH_HYPERLINK" desc="Message to end users in Enterprise/EDU, with a link for more info (ChromeOS)">
+            Your <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph><ph name="DEVICE_TYPE">$2<ex>Chromebook</ex></ph> is managed<ph name="END_LINK">&lt;/a&gt;</ph> by <ph name="ENROLLMENT_DOMAIN">$3<ex>example.com</ex></ph>
           </message>
         </if>
-        <if expr="not chromeos">
-          <message name="IDS_MANAGED_WITH_HYPERLINK" desc="Message to end users in Enterprise/EDU, with a link for more info (non-ChromeOS)">
-            Your <ph name="BEGIN_LINK">&lt;a href="$1"&gt;</ph>browser is managed<ph name="END_LINK">&lt;/a&gt;</ph> by your organization
-          </message>
-          <message name="IDS_MANAGED_BY_WITH_HYPERLINK" desc="Message to end users in Enterprise/EDU, with a link for more info (non-ChromeOS)">
-            Your <ph name="BEGIN_LINK">&lt;a href="$1"&gt;</ph>browser is managed<ph name="END_LINK">&lt;/a&gt;</ph> by <ph name="ENROLLMENT_DOMAIN">$2<ex>example.com</ex></ph>
-          </message>
-        </if>
+        <message name="IDS_MANAGED_WITH_HYPERLINK" desc="Message to end users in Enterprise/EDU, with a link for more info (non-ChromeOS)">
+          Your <ph name="BEGIN_LINK">&lt;a href="$1"&gt;</ph>browser is managed<ph name="END_LINK">&lt;/a&gt;</ph> by your organization
+        </message>
+        <message name="IDS_MANAGED_BY_WITH_HYPERLINK" desc="Message to end users in Enterprise/EDU, with a link for more info (non-ChromeOS)">
+          Your <ph name="BEGIN_LINK">&lt;a href="$1"&gt;</ph>browser is managed<ph name="END_LINK">&lt;/a&gt;</ph> by <ph name="ENROLLMENT_DOMAIN">$2<ex>example.com</ex></ph>
+        </message>
       </if>
 
       <!-- Cookies Window -->
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 18daa2b..f57cef5 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1499,8 +1499,6 @@
     "renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm",
     "renderer_preferences_util.cc",
     "renderer_preferences_util.h",
-    "resource_coordinator/discard_before_unload_helper.cc",
-    "resource_coordinator/discard_before_unload_helper.h",
     "resource_coordinator/resource_coordinator_parts.cc",
     "resource_coordinator/resource_coordinator_parts.h",
     "resource_coordinator/session_restore_policy.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index e48f9b8a..aa07715 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1628,10 +1628,6 @@
     {"newblue", flag_descriptions::kNewblueName,
      flag_descriptions::kNewblueDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(device::kNewblueDaemon)},
-    {"unfiltered-bluetooth-devices",
-     flag_descriptions::kUnfilteredBluetoothDevicesName,
-     flag_descriptions::kUnfilteredBluetoothDevicesDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(device::kUnfilteredBluetoothDevices)},
     {"shelf-dense-clamshell", flag_descriptions::kShelfDenseClamshellName,
      flag_descriptions::kShelfDenseClamshellDescription, kOsCrOS,
      SINGLE_VALUE_TYPE(chromeos::switches::kShelfDenseClamshell)},
@@ -2391,9 +2387,6 @@
      SINGLE_VALUE_TYPE(
          ::switches::kEnableExperimentalAccessibilityLanguageDetection)},
 #if defined(OS_CHROMEOS)
-    {"enable-bulk-printers", flag_descriptions::kBulkPrintersName,
-     flag_descriptions::kBulkPrintersDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(features::kBulkPrinters)},
     {"enable-encryption-migration",
      flag_descriptions::kEnableEncryptionMigrationName,
      flag_descriptions::kEnableEncryptionMigrationDescription, kOsCrOS,
diff --git a/chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.cc b/chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.cc
index e7fd6356..0a4491ff 100644
--- a/chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.cc
+++ b/chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.cc
@@ -116,6 +116,17 @@
   ui_controller_->OnTermsAndConditionsLinkClicked(link);
 }
 
+void AssistantPaymentRequestDelegate::OnLoginChoiceChanged(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcaller,
+    const base::android::JavaParamRef<jstring>& jidentifier) {
+  std::string identifier;
+  if (jidentifier) {
+    base::android::ConvertJavaStringToUTF8(env, jidentifier, &identifier);
+  }
+  ui_controller_->OnLoginChoiceChanged(identifier);
+}
+
 base::android::ScopedJavaGlobalRef<jobject>
 AssistantPaymentRequestDelegate::GetJavaObject() {
   return java_assistant_payment_request_delegate_;
diff --git a/chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.h b/chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.h
index 1b605847..59277af9 100644
--- a/chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.h
+++ b/chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.h
@@ -41,6 +41,11 @@
       const base::android::JavaParamRef<jobject>& jcaller,
       jint link);
 
+  void OnLoginChoiceChanged(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& jcaller,
+      const base::android::JavaParamRef<jstring>& jidentifier);
+
   base::android::ScopedJavaGlobalRef<jobject> GetJavaObject();
 
  private:
diff --git a/chrome/browser/android/autofill_assistant/client_android.cc b/chrome/browser/android/autofill_assistant/client_android.cc
index 671cadc..f722488 100644
--- a/chrome/browser/android/autofill_assistant/client_android.cc
+++ b/chrome/browser/android/autofill_assistant/client_android.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/android/chrome_feature_list.h"
 #include "chrome/browser/autofill/android/personal_data_manager_android.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
+#include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
@@ -28,6 +29,7 @@
 #include "components/autofill_assistant/browser/access_token_fetcher.h"
 #include "components/autofill_assistant/browser/controller.h"
 #include "components/autofill_assistant/browser/features.h"
+#include "components/autofill_assistant/browser/website_login_fetcher_impl.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/version_info/channel.h"
@@ -365,6 +367,14 @@
       ProfileManager::GetLastUsedProfile());
 }
 
+WebsiteLoginFetcher* ClientAndroid::GetWebsiteLoginFetcher() {
+  if (!website_login_fetcher_) {
+    website_login_fetcher_ = std::make_unique<WebsiteLoginFetcherImpl>(
+        ChromePasswordManagerClient::FromWebContents(web_contents_));
+  }
+  return website_login_fetcher_.get();
+}
+
 std::string ClientAndroid::GetServerUrl() {
   return server_url_;
 }
diff --git a/chrome/browser/android/autofill_assistant/client_android.h b/chrome/browser/android/autofill_assistant/client_android.h
index 7467a67..fd7df1f 100644
--- a/chrome/browser/android/autofill_assistant/client_android.h
+++ b/chrome/browser/android/autofill_assistant/client_android.h
@@ -16,6 +16,7 @@
 #include "components/autofill_assistant/browser/access_token_fetcher.h"
 #include "components/autofill_assistant/browser/client.h"
 #include "components/autofill_assistant/browser/controller.h"
+#include "components/autofill_assistant/browser/website_login_fetcher.h"
 #include "content/public/browser/web_contents_user_data.h"
 
 namespace autofill_assistant {
@@ -86,6 +87,7 @@
   std::string GetAccountEmailAddress() override;
   AccessTokenFetcher* GetAccessTokenFetcher() override;
   autofill::PersonalDataManager* GetPersonalDataManager() override;
+  WebsiteLoginFetcher* GetWebsiteLoginFetcher() override;
   std::string GetServerUrl() override;
   std::string GetLocale() override;
   std::string GetCountryCode() override;
@@ -117,6 +119,7 @@
 
   base::android::ScopedJavaGlobalRef<jobject> java_object_;
   std::unique_ptr<Controller> controller_;
+  std::unique_ptr<WebsiteLoginFetcher> website_login_fetcher_;
 
   // True if Start() was called. This turns on the tracking of dropouts.
   bool started_ = false;
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 597b95cd..a2df97e 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -702,6 +702,10 @@
   ui_delegate_->SetTermsAndConditions(state);
 }
 
+void UiControllerAndroid::OnLoginChoiceChanged(std::string identifier) {
+  ui_delegate_->SetLoginOption(identifier);
+}
+
 void UiControllerAndroid::OnTermsAndConditionsLinkClicked(int link) {
   ui_delegate_->OnTermsAndConditionsLinkClicked(link);
 }
@@ -725,6 +729,12 @@
       env, jmodel, payment_options->request_shipping);
   Java_AssistantPaymentRequestModel_setRequestPayment(
       env, jmodel, payment_options->request_payment_method);
+  Java_AssistantPaymentRequestModel_setRequestLoginChoice(
+      env, jmodel, payment_options->request_login_choice);
+  Java_AssistantPaymentRequestModel_setLoginSectionTitle(
+      env, jmodel,
+      base::android::ConvertUTF8ToJavaString(
+          env, payment_options->login_section_title));
   Java_AssistantPaymentRequestModel_setAcceptTermsAndConditionsText(
       env, jmodel,
       base::android::ConvertUTF8ToJavaString(
@@ -741,6 +751,17 @@
       env, jmodel,
       base::android::ToJavaArrayOfStrings(
           env, payment_options->supported_basic_card_networks));
+  if (payment_options->request_login_choice) {
+    auto jlist = Java_AssistantPaymentRequestModel_createLoginChoiceList(env);
+    for (const auto& login_choice : payment_options->login_choices) {
+      Java_AssistantPaymentRequestModel_addLoginChoice(
+          env, jmodel, jlist,
+          base::android::ConvertUTF8ToJavaString(env, login_choice.identifier),
+          base::android::ConvertUTF8ToJavaString(env, login_choice.label),
+          login_choice.preselect_priority);
+    }
+    Java_AssistantPaymentRequestModel_setLoginChoices(env, jmodel, jlist);
+  }
 
   Java_AssistantPaymentRequestModel_setVisible(env, jmodel, true);
 }
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index 061d8df..329889b 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -121,6 +121,7 @@
                             std::string email);
   void OnCreditCardChanged(std::unique_ptr<autofill::CreditCard> card);
   void OnTermsAndConditionsChanged(TermsAndConditionsState state);
+  void OnLoginChoiceChanged(std::string identifier);
   void OnTermsAndConditionsLinkClicked(int link);
 
   // Called by AssistantFormDelegate:
diff --git a/chrome/browser/android/download/download_location_dialog_bridge_impl.cc b/chrome/browser/android/download/download_location_dialog_bridge_impl.cc
index db0fab51..ea5c39e 100644
--- a/chrome/browser/android/download/download_location_dialog_bridge_impl.cc
+++ b/chrome/browser/android/download/download_location_dialog_bridge_impl.cc
@@ -15,7 +15,7 @@
     : is_dialog_showing_(false) {
   JNIEnv* env = base::android::AttachCurrentThread();
   java_obj_.Reset(env, Java_DownloadLocationDialogBridge_create(
-                           env, reinterpret_cast<long>(this))
+                           env, reinterpret_cast<intptr_t>(this))
                            .obj());
   DCHECK(!java_obj_.is_null());
 }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 4b058f3..eb147f43 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2763,12 +2763,16 @@
 }
 
 void AddDataReductionProxyBinding(
-    content::BrowserContext* browser_context,
+    int render_process_id,
     data_reduction_proxy::mojom::DataReductionProxyRequest request) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  auto* rph = content::RenderProcessHost::FromID(render_process_id);
+  if (!rph)
+    return;
+
   auto* drp_settings =
       DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
-          browser_context);
+          rph->GetBrowserContext());
   if (!drp_settings)
     return;
 
@@ -3738,10 +3742,9 @@
 #endif
 
   if (data_reduction_proxy::params::IsEnabledWithNetworkService()) {
-    registry->AddInterface(
-        base::BindRepeating(&AddDataReductionProxyBinding,
-                            render_process_host->GetBrowserContext()),
-        ui_task_runner);
+    registry->AddInterface(base::BindRepeating(&AddDataReductionProxyBinding,
+                                               render_process_host->GetID()),
+                           ui_task_runner);
   }
 
 #if defined(OS_WIN)
@@ -4380,7 +4383,12 @@
       base::BindRepeating(&language::BindContentTranslateDriver));
 
   frame_interfaces_parameterized_->AddInterface(
-      base::Bind(&InsecureSensitiveInputDriverFactory::BindDriver));
+      base::BindRepeating([](blink::mojom::InsecureInputServiceRequest request,
+                             content::RenderFrameHost* render_frame_host) {
+        // Implicit conversion to PendingReceiver<T>.
+        InsecureSensitiveInputDriverFactory::BindDriver(std::move(request),
+                                                        render_frame_host);
+      }));
 
 #if defined(OS_ANDROID)
   frame_interfaces_parameterized_->AddInterface(base::Bind(
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
index ed6c40e..c4d12d3 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
+++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
@@ -832,7 +832,8 @@
 bool DeviceLocalAccountManagementPolicyProvider::UserMayLoad(
     const extensions::Extension* extension,
     base::string16* error) const {
-  if (account_type_ == policy::DeviceLocalAccount::TYPE_PUBLIC_SESSION) {
+  if (account_type_ == policy::DeviceLocalAccount::TYPE_PUBLIC_SESSION ||
+      account_type_ == policy::DeviceLocalAccount::TYPE_SAML_PUBLIC_SESSION) {
     // Allow extension if it is a component of Chrome.
     if (extension->location() == extensions::Manifest::EXTERNAL_COMPONENT ||
         extension->location() == extensions::Manifest::COMPONENT) {
diff --git a/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.cc b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.cc
index 43c402fb..e3ea0413 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.cc
@@ -7,6 +7,7 @@
 #include "ash/public/cpp/login_screen.h"
 #include "ash/public/cpp/login_screen_model.h"
 #include "ash/public/cpp/login_types.h"
+#include "base/macros.h"
 #include "chrome/browser/chromeos/login/ui/login_screen_extension_ui/login_screen_extension_ui_create_options.h"
 #include "chrome/browser/chromeos/login/ui/login_screen_extension_ui/login_screen_extension_ui_window.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -24,13 +25,37 @@
 namespace {
 
 const char kErrorWindowAlreadyExists[] =
-    "Can't create more than one window per extension.";
+    "Login screen extension UI already in use.";
 const char kErrorNoExistingWindow[] = "No open window to close.";
 const char kErrorNotOnLoginOrLockScreen[] =
     "Windows can only be created on the login and lock screen.";
 
 LoginScreenExtensionUiHandler* g_instance = nullptr;
 
+struct HardcodedExtensionNameMapping {
+  const char* extension_id;
+  const char* extension_name;
+};
+
+// Hardcoded extension names to be used in the window's dialog title.
+// Intentionally not using |extension->name()| here to prevent a compromised
+// extension from being able to control the dialog title's content.
+const HardcodedExtensionNameMapping kHardcodedExtensionNameMappings[] = {
+    {"cdgickkdpbekbnalbmpgochbninibkko", "Imprivata"},
+    {"lpimkpkllnkdlcigdbgmabfplniahkgm", "Imprivata"},
+    {"oclffehlkdgibkainkilopaalpdobkan", "LoginScreenUi test extension"},
+};
+
+std::string GetHardcodedExtensionName(const extensions::Extension* extension) {
+  for (const HardcodedExtensionNameMapping& mapping :
+       kHardcodedExtensionNameMappings) {
+    if (mapping.extension_id == extension->id())
+      return mapping.extension_name;
+  }
+  NOTREACHED();
+  return "UNKNOWN EXTENSION";
+}
+
 bool CanUseLoginScreenUiApi(const extensions::Extension* extension) {
   return extensions::ExtensionRegistry::Get(ProfileHelper::GetSigninProfile())
              ->enabled_extensions()
@@ -42,6 +67,13 @@
 
 }  // namespace
 
+ExtensionIdToWindowMapping::ExtensionIdToWindowMapping(
+    const std::string& extension_id,
+    std::unique_ptr<LoginScreenExtensionUiWindow> window)
+    : extension_id(extension_id), window(std::move(window)) {}
+
+ExtensionIdToWindowMapping::~ExtensionIdToWindowMapping() = default;
+
 // static
 LoginScreenExtensionUiHandler* LoginScreenExtensionUiHandler::Get(
     bool can_create) {
@@ -82,26 +114,24 @@
     *error = kErrorNotOnLoginOrLockScreen;
     return false;
   }
-  if (HasOpenWindow(extension->id())) {
+  if (current_window_) {
     *error = kErrorWindowAlreadyExists;
     return false;
   }
 
-  if (!HasOpenWindow()) {
-    ash::LoginScreen::Get()->GetModel()->NotifyOobeDialogState(
-        ash::OobeDialogState::EXTENSION_LOGIN);
-  }
+  ash::LoginScreen::Get()->GetModel()->NotifyOobeDialogState(
+      ash::OobeDialogState::EXTENSION_LOGIN);
 
   LoginScreenExtensionUiCreateOptions create_options(
-      extension->short_name(), extension->GetResourceURL(resource_path),
-      can_be_closed_by_user,
+      GetHardcodedExtensionName(extension),
+      extension->GetResourceURL(resource_path), can_be_closed_by_user,
       base::BindOnce(
           base::IgnoreResult(
               &LoginScreenExtensionUiHandler::RemoveWindowForExtension),
           weak_ptr_factory_.GetWeakPtr(), extension->id()));
-  std::unique_ptr<LoginScreenExtensionUiWindow> window =
-      window_factory_->Create(&create_options);
-  windows_.emplace(extension->id(), std::move(window));
+
+  current_window_ = std::make_unique<ExtensionIdToWindowMapping>(
+      extension->id(), window_factory_->Create(&create_options));
 
   return true;
 }
@@ -119,26 +149,20 @@
 
 bool LoginScreenExtensionUiHandler::RemoveWindowForExtension(
     const std::string& extension_id) {
-  WindowMap::iterator it = windows_.find(extension_id);
-  if (it == windows_.end())
+  if (!HasOpenWindow(extension_id))
     return false;
-  windows_.erase(it);
 
-  if (!HasOpenWindow()) {
-    ash::LoginScreen::Get()->GetModel()->NotifyOobeDialogState(
-        ash::OobeDialogState::HIDDEN);
-  }
+  current_window_.reset(nullptr);
+
+  ash::LoginScreen::Get()->GetModel()->NotifyOobeDialogState(
+      ash::OobeDialogState::HIDDEN);
 
   return true;
 }
 
 bool LoginScreenExtensionUiHandler::HasOpenWindow(
     const std::string& extension_id) const {
-  return windows_.find(extension_id) != windows_.end();
-}
-
-bool LoginScreenExtensionUiHandler::HasOpenWindow() const {
-  return !windows_.empty();
+  return current_window_ && current_window_->extension_id == extension_id;
 }
 
 void LoginScreenExtensionUiHandler::UpdateSessionState() {
@@ -154,7 +178,7 @@
   login_or_lock_screen_active_ = new_login_or_lock_screen_active;
 
   if (!login_or_lock_screen_active_)
-    windows_.clear();
+    current_window_.reset(nullptr);
 }
 
 void LoginScreenExtensionUiHandler::OnSessionStateChanged() {
@@ -171,11 +195,10 @@
 LoginScreenExtensionUiWindow*
 LoginScreenExtensionUiHandler::GetWindowForTesting(
     const std::string& extension_id) {
-  WindowMap::iterator it = windows_.find(extension_id);
-  if (it == windows_.end())
+  if (!HasOpenWindow(extension_id))
     return nullptr;
 
-  return it->second.get();
+  return current_window_->window.get();
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h
index 83c0b88..e847f1f 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h
+++ b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h
@@ -5,7 +5,6 @@
 #ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_SCREEN_UI_LOGIN_SCREEN_EXTENSION_UI_HANDLER_H_
 #define CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_SCREEN_UI_LOGIN_SCREEN_EXTENSION_UI_HANDLER_H_
 
-#include <map>
 #include <memory>
 #include <string>
 
@@ -28,9 +27,20 @@
 class LoginScreenExtensionUiWindow;
 class LoginScreenExtensionUiWindowFactory;
 
+struct ExtensionIdToWindowMapping {
+  ExtensionIdToWindowMapping(
+      const std::string& extension_id,
+      std::unique_ptr<LoginScreenExtensionUiWindow> window);
+  ~ExtensionIdToWindowMapping();
+
+  const std::string extension_id;
+  std::unique_ptr<LoginScreenExtensionUiWindow> window;
+};
+
 // This class receives calls from the chrome.loginScreenUi API and manages the
-// associated windows. Windows can only be created on the login and lock screen
-// and are automatically closed when logging in or unlocking a user session.
+// associated window (only one window can be active at a time). Windows can only
+// be created on the login and lock screen and are automatically closed when
+// logging in or unlocking a user session.
 // TODO(hendrich): handle interference with other ChromeOS UI that pops up
 // during login (e.g. arc ToS).
 class LoginScreenExtensionUiHandler
@@ -62,7 +72,6 @@
   bool RemoveWindowForExtension(const std::string& extension_id);
 
   bool HasOpenWindow(const std::string& extension_id) const;
-  bool HasOpenWindow() const;
 
   // session_manager::SessionManagerObserver
   void OnSessionStateChanged() override;
@@ -71,9 +80,6 @@
       const std::string& extension_id);
 
  private:
-  using WindowMap =
-      std::map<std::string, std::unique_ptr<LoginScreenExtensionUiWindow>>;
-
   void UpdateSessionState();
 
   // extensions::ExtensionRegistryObserver
@@ -85,7 +91,7 @@
 
   bool login_or_lock_screen_active_ = false;
 
-  WindowMap windows_;
+  std::unique_ptr<ExtensionIdToWindowMapping> current_window_;
 
   ScopedObserver<session_manager::SessionManager,
                  session_manager::SessionManagerObserver>
diff --git a/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler_unittest.cc b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler_unittest.cc
index 06794ef..2ee98c5d 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler_unittest.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler_unittest.cc
@@ -28,7 +28,7 @@
 namespace {
 
 const char kErrorWindowAlreadyExists[] =
-    "Can't create more than one window per extension.";
+    "Login screen extension UI already in use.";
 const char kErrorNoExistingWindow[] = "No open window to close.";
 const char kErrorNotOnLoginOrLockScreen[] =
     "Windows can only be created on the login and lock screen.";
@@ -124,7 +124,8 @@
     session_manager_.SetSessionState(
         session_manager::SessionState::LOGIN_PRIMARY);
 
-    extension_ = extensions::ExtensionBuilder("test" /*extension_name*/)
+    extension_ = extensions::ExtensionBuilder(
+                     /*extension_name=*/"LoginScreenUi test extension")
                      .SetID(kWhitelistedExtensionID1)
                      .SetLocation(extensions::Manifest::EXTERNAL_POLICY)
                      .AddPermission(kPermissionName)
@@ -284,9 +285,9 @@
   EXPECT_FALSE(ui_handler_->HasOpenWindow(extension_->id()));
 }
 
-TEST_F(LoginScreenExtensionUiHandlerUnittest, TwoExtensionsInParallel) {
+TEST_F(LoginScreenExtensionUiHandlerUnittest, OnlyOneWindow) {
   scoped_refptr<const extensions::Extension> other_extension =
-      extensions::ExtensionBuilder("other extension" /*extension_name*/)
+      extensions::ExtensionBuilder(/*extension_name=*/"Imprivata")
           .SetID(kWhitelistedExtensionID2)
           .SetLocation(extensions::Manifest::EXTERNAL_POLICY)
           .AddPermission(kPermissionName)
@@ -299,30 +300,24 @@
   EXPECT_TRUE(ui_handler_->HasOpenWindow(extension_->id()));
   EXPECT_FALSE(ui_handler_->HasOpenWindow(other_extension->id()));
 
-  // Open window with extension 2.
-  CheckCanOpenWindow(other_extension.get());
+  // Try to open another window with extension 1.
+  CheckCannotOpenWindow(extension_.get(), kErrorWindowAlreadyExists);
   EXPECT_TRUE(ui_handler_->HasOpenWindow(extension_->id()));
-  EXPECT_TRUE(ui_handler_->HasOpenWindow(other_extension->id()));
+  EXPECT_FALSE(ui_handler_->HasOpenWindow(other_extension->id()));
+
+  // Open window with extension 2.
+  CheckCannotOpenWindow(other_extension.get(), kErrorWindowAlreadyExists);
+  EXPECT_TRUE(ui_handler_->HasOpenWindow(extension_->id()));
+  EXPECT_FALSE(ui_handler_->HasOpenWindow(other_extension->id()));
 
   // Close window with extension 1.
   CheckCanCloseWindow(extension_.get());
   EXPECT_FALSE(ui_handler_->HasOpenWindow(extension_->id()));
-  EXPECT_TRUE(ui_handler_->HasOpenWindow(other_extension->id()));
-
-  // Close window with extension 1 again.
-  CheckCannotCloseWindow(extension_.get(), kErrorNoExistingWindow);
-  EXPECT_FALSE(ui_handler_->HasOpenWindow(extension_->id()));
-  EXPECT_TRUE(ui_handler_->HasOpenWindow(other_extension->id()));
+  EXPECT_FALSE(ui_handler_->HasOpenWindow(other_extension->id()));
 }
 
-TEST_F(LoginScreenExtensionUiHandlerUnittest, OnlyOneWindowPerExtension) {
-  // Open window with extension 1.
-  CheckCanOpenWindow(extension_.get());
-  EXPECT_TRUE(ui_handler_->HasOpenWindow(extension_->id()));
-
-  // Open window with extension 1 again.
-  CheckCannotOpenWindow(extension_.get(), kErrorWindowAlreadyExists);
-  EXPECT_TRUE(ui_handler_->HasOpenWindow(extension_->id()));
+TEST_F(LoginScreenExtensionUiHandlerUnittest, CannotCloseNoWindow) {
+  CheckCannotCloseWindow(extension_.get(), kErrorNoExistingWindow);
 }
 
 TEST_F(LoginScreenExtensionUiHandlerUnittest, ManualClose) {
diff --git a/chrome/browser/chromeos/printing/bulk_printers_calculator.cc b/chrome/browser/chromeos/printing/bulk_printers_calculator.cc
index 279a86e..428abb9 100644
--- a/chrome/browser/chromeos/printing/bulk_printers_calculator.cc
+++ b/chrome/browser/chromeos/printing/bulk_printers_calculator.cc
@@ -7,7 +7,6 @@
 #include <set>
 
 #include "base/bind.h"
-#include "base/feature_list.h"
 #include "base/json/json_reader.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
@@ -20,7 +19,6 @@
 #include "base/task_runner_util.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "base/values.h"
-#include "chrome/common/chrome_features.h"
 #include "chromeos/printing/printer_translator.h"
 
 namespace chromeos {
@@ -239,9 +237,6 @@
 
   void ClearData() override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    if (!base::FeatureList::IsEnabled(features::kBulkPrinters)) {
-      return;
-    }
     data_is_set_ = false;
     last_processed_task_ = ++last_received_task_;
     printers_.clear();
@@ -256,9 +251,6 @@
 
   void SetData(std::unique_ptr<std::string> data) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    if (!base::FeatureList::IsEnabled(features::kBulkPrinters)) {
-      return;
-    }
     data_is_set_ = true;
     TaskData task_data =
         std::make_unique<TaskDataInternal>(++last_received_task_);
diff --git a/chrome/browser/chromeos/printing/bulk_printers_calculator_unittest.cc b/chrome/browser/chromeos/printing/bulk_printers_calculator_unittest.cc
index bb04159..40b2e45c 100644
--- a/chrome/browser/chromeos/printing/bulk_printers_calculator_unittest.cc
+++ b/chrome/browser/chromeos/printing/bulk_printers_calculator_unittest.cc
@@ -9,9 +9,7 @@
 
 #include "base/bind.h"
 #include "base/strings/stringprintf.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
-#include "chrome/common/chrome_features.h"
 #include "chromeos/printing/printer_configuration.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -93,8 +91,6 @@
 class BulkPrintersCalculatorTest : public testing::Test {
  public:
   BulkPrintersCalculatorTest() : task_environment_() {
-    scoped_feature_list_.InitAndEnableFeature(
-        base::Feature(features::kBulkPrinters));
     external_printers_ = BulkPrintersCalculator::Create();
   }
   ~BulkPrintersCalculatorTest() override {
@@ -105,9 +101,6 @@
  protected:
   std::unique_ptr<BulkPrintersCalculator> external_printers_;
   base::test::TaskEnvironment task_environment_;
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 // Verify that we're initiall unset and empty.
diff --git a/chrome/browser/chromeos/printing/cups_proxy_service_manager.cc b/chrome/browser/chromeos/printing/cups_proxy_service_manager.cc
index b083559..5dc473e0 100644
--- a/chrome/browser/chromeos/printing/cups_proxy_service_manager.cc
+++ b/chrome/browser/chromeos/printing/cups_proxy_service_manager.cc
@@ -10,7 +10,6 @@
 #include "chrome/browser/chromeos/printing/cups_proxy_service_delegate_impl.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/chrome_features.h"
-#include "chrome/services/cups_proxy/public/mojom/constants.mojom.h"
 #include "chromeos/dbus/cups_proxy/cups_proxy_client.h"
 #include "content/public/browser/browser_context.h"
 #include "services/service_manager/public/cpp/connector.h"
diff --git a/chrome/browser/chromeos/printing/enterprise_printers_provider.cc b/chrome/browser/chromeos/printing/enterprise_printers_provider.cc
index 5a7d047..9610922 100644
--- a/chrome/browser/chromeos/printing/enterprise_printers_provider.cc
+++ b/chrome/browser/chromeos/printing/enterprise_printers_provider.cc
@@ -6,7 +6,6 @@
 
 #include <vector>
 
-#include "base/feature_list.h"
 #include "base/hash/md5.h"
 #include "base/json/json_reader.h"
 #include "chrome/browser/chromeos/printing/bulk_printers_calculator.h"
@@ -14,7 +13,6 @@
 #include "chrome/browser/chromeos/printing/calculators_policies_binder.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/printing/printer_configuration.h"
 #include "chromeos/printing/printer_translator.h"
@@ -48,28 +46,22 @@
     // initialization of pref_change_registrar
     pref_change_registrar_.Init(profile->GetPrefs());
 
-    if (base::FeatureList::IsEnabled(features::kBulkPrinters)) {
-      // Binds instances of BulkPrintersCalculator to policies.
-      policies_binder_ = CalculatorsPoliciesBinder::Create(settings, profile);
-      // Get instance of BulkPrintersCalculator for device policies.
-      device_printers_ = BulkPrintersCalculatorFactory::Get()->GetForDevice(
-          /*create_if_not_exists=*/false);
-      if (device_printers_) {
-        device_printers_->AddObserver(this);
-        RecalculateCompleteFlagForDevicePrinters();
-      }
-      // Get instance of BulkPrintersCalculator for user policies.
-      user_printers_ = BulkPrintersCalculatorFactory::Get()->GetForProfile(
-          profile,
-          /*create_if_not_exists=*/false);
-      if (user_printers_) {
-        user_printers_->AddObserver(this);
-        RecalculateCompleteFlagForUserPrinters();
-      }
-    } else {
-      // If a "Bulk Printers" feature is inactive, we do not bind anything.
-      // The list of printers is always empty and is reported as complete.
-      complete_ = true;
+    // Binds instances of BulkPrintersCalculator to policies.
+    policies_binder_ = CalculatorsPoliciesBinder::Create(settings, profile);
+    // Get instance of BulkPrintersCalculator for device policies.
+    device_printers_ = BulkPrintersCalculatorFactory::Get()->GetForDevice(
+        /*create_if_not_exists=*/false);
+    if (device_printers_) {
+      device_printers_->AddObserver(this);
+      RecalculateCompleteFlagForDevicePrinters();
+    }
+    // Get instance of BulkPrintersCalculator for user policies.
+    user_printers_ = BulkPrintersCalculatorFactory::Get()->GetForProfile(
+        profile,
+        /*create_if_not_exists=*/false);
+    if (user_printers_) {
+      user_printers_->AddObserver(this);
+      RecalculateCompleteFlagForUserPrinters();
     }
     // Binds policy with recommended printers (deprecated). This method calls
     // indirectly RecalculateCurrentPrintersList() that prepares the first
diff --git a/chrome/browser/extensions/extension_crash_recovery_browsertest.cc b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc
index ae366015..cba5305 100644
--- a/chrome/browser/extensions/extension_crash_recovery_browsertest.cc
+++ b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc
@@ -146,7 +146,13 @@
   content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes_;
 };
 
-IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, Basic) {
+// Flaky on ASAN builds (https://crbug.com/997634)
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_Basic DISABLED_Basic
+#else
+#define MAYBE_Basic Basic
+#endif
+IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, MAYBE_Basic) {
   const size_t count_before = GetEnabledExtensionCount();
   const size_t crash_count_before = GetTerminatedExtensionCount();
   LoadTestExtension();
@@ -243,8 +249,15 @@
   ASSERT_EQ(0U, CountNotifications());
 }
 
+// Flaky on ASAN builds (https://crbug.com/997634)
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_ReloadIndependentlyNavigatePage \
+  DISABLED_ReloadIndependentlyNavigatePage
+#else
+#define MAYBE_ReloadIndependentlyNavigatePage ReloadIndependentlyNavigatePage
+#endif
 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
-                       ReloadIndependentlyNavigatePage) {
+                       MAYBE_ReloadIndependentlyNavigatePage) {
   const size_t count_before = GetEnabledExtensionCount();
   LoadTestExtension();
   CrashExtension(first_extension_id_);
@@ -279,7 +292,14 @@
   ASSERT_EQ(count_before, GetEnabledExtensionCount());
 }
 
-IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, TwoExtensionsCrashFirst) {
+// Flaky on ASAN builds (https://crbug.com/997634)
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_TwoExtensionsCrashFirst DISABLED_TwoExtensionsCrashFirst
+#else
+#define MAYBE_TwoExtensionsCrashFirst TwoExtensionsCrashFirst
+#endif
+IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
+                       MAYBE_TwoExtensionsCrashFirst) {
   const size_t count_before = GetEnabledExtensionCount();
   LoadTestExtension();
   LoadSecondExtension();
@@ -292,7 +312,14 @@
   CheckExtensionConsistency(second_extension_id_);
 }
 
-IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, TwoExtensionsCrashSecond) {
+// Flaky on ASAN builds (https://crbug.com/997634)
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_TwoExtensionsCrashSecond DISABLED_TwoExtensionsCrashSecond
+#else
+#define MAYBE_TwoExtensionsCrashSecond TwoExtensionsCrashSecond
+#endif
+IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
+                       MAYBE_TwoExtensionsCrashSecond) {
   const size_t count_before = GetEnabledExtensionCount();
   LoadTestExtension();
   LoadSecondExtension();
@@ -305,8 +332,14 @@
   CheckExtensionConsistency(second_extension_id_);
 }
 
+// Flaky on ASAN builds (https://crbug.com/997634)
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_TwoExtensionsCrashBothAtOnce DISABLED_TwoExtensionsCrashBothAtOnce
+#else
+#define MAYBE_TwoExtensionsCrashBothAtOnce TwoExtensionsCrashBothAtOnce
+#endif
 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
-                       TwoExtensionsCrashBothAtOnce) {
+                       MAYBE_TwoExtensionsCrashBothAtOnce) {
   const size_t count_before = GetEnabledExtensionCount();
   const size_t crash_count_before = GetTerminatedExtensionCount();
   LoadTestExtension();
@@ -332,7 +365,14 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, TwoExtensionsOneByOne) {
+// Flaky on ASAN builds (https://crbug.com/997634)
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_TwoExtensionsOneByOne DISABLED_TwoExtensionsOneByOne
+#else
+#define MAYBE_TwoExtensionsOneByOne TwoExtensionsOneByOne
+#endif
+IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
+                       MAYBE_TwoExtensionsOneByOne) {
   const size_t count_before = GetEnabledExtensionCount();
   LoadTestExtension();
   CrashExtension(first_extension_id_);
@@ -393,8 +433,15 @@
   CheckExtensionConsistency(second_extension_id_);
 }
 
+// Flaky on ASAN builds (https://crbug.com/997634)
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_TwoExtensionsReloadIndependently \
+  DISABLED_TwoExtensionsReloadIndependently
+#else
+#define MAYBE_TwoExtensionsReloadIndependently TwoExtensionsReloadIndependently
+#endif
 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
-                       TwoExtensionsReloadIndependently) {
+                       MAYBE_TwoExtensionsReloadIndependently) {
   const size_t count_before = GetEnabledExtensionCount();
   LoadTestExtension();
   LoadSecondExtension();
diff --git a/chrome/browser/extensions/service_worker_apitest.cc b/chrome/browser/extensions/service_worker_apitest.cc
index 9fddbbe..126e32f 100644
--- a/chrome/browser/extensions/service_worker_apitest.cc
+++ b/chrome/browser/extensions/service_worker_apitest.cc
@@ -399,7 +399,13 @@
 }
 
 // Tests chrome.webRequest APIs.
-IN_PROC_BROWSER_TEST_F(ServiceWorkerBasedBackgroundTest, WebRequest) {
+// Times out on Mac/Win only.  https://crbug.com/997686
+#if defined(OS_WIN) || defined(OS_MACOSX)
+#define MAYBE_WebRequest DISABLED_WebRequest
+#else
+#define MAYBE_WebRequest WebRequest
+#endif
+IN_PROC_BROWSER_TEST_F(ServiceWorkerBasedBackgroundTest, MAYBE_WebRequest) {
   ASSERT_TRUE(
       RunExtensionTest("service_worker/worker_based_background/web_request"))
       << message_;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 6be1c9d..f2688120 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3185,11 +3185,6 @@
     "expiry_milestone": -1
   },
   {
-    "name": "unfiltered-bluetooth-devices",
-    "owners": [ "sonnysasaka", "qiyuh@google.com" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "unified-consent",
     "owners": [ "msarda", "tangltom" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 792dd5b0..ec4b120e 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3106,9 +3106,6 @@
     "Enables a more aggressive Bluetooth filter in the UI to hide devices that "
     "likely cannot be connected to.";
 
-const char kBulkPrintersName[] = "Bulk Printers Policy";
-const char kBulkPrintersDescription[] = "Enables the new bulk printers policy";
-
 const char kCameraSystemWebAppName[] = "Camera System Web App";
 const char kCameraSystemWebAppDescription[] =
     "Run the Chrome Camera App as a System Web App.";
@@ -3183,9 +3180,9 @@
     "page.";
 
 const char kEnableAdvancedPpdAttributesName[] =
-    "Enables advanced PPD attributes";
+    "Enable advanced PPD attributes";
 const char kEnableAdvancedPpdAttributesDescription[] =
-    "Enables advanced settings on CUPS printers";
+    "Enable advanced settings on CUPS printers";
 
 const char kEnableAppDataSearchName[] = "Enable app data search in launcher";
 const char kEnableAppDataSearchDescription[] =
@@ -3520,10 +3517,6 @@
 const char kUiSlowAnimationsName[] = "Slow UI animations";
 const char kUiSlowAnimationsDescription[] = "Makes all UI animations slow.";
 
-const char kUnfilteredBluetoothDevicesName[] = "Unfiltered Bluetooth devices";
-const char kUnfilteredBluetoothDevicesDescription[] =
-    "Shows all Bluetooth devices in UI (System Tray/Settings Page.)";
-
 const char kUsbguardName[] = "Block new USB devices at the lock screen.";
 const char kUsbguardDescription[] =
     "Prevents newly connected USB devices from operating at the lock screen"
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index bed0321..121c498 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1850,9 +1850,6 @@
 extern const char kBluetoothAggressiveAppearanceFilterName[];
 extern const char kBluetoothAggressiveAppearanceFilterDescription[];
 
-extern const char kBulkPrintersName[];
-extern const char kBulkPrintersDescription[];
-
 extern const char kCameraSystemWebAppName[];
 extern const char kCameraSystemWebAppDescription[];
 
@@ -2111,9 +2108,6 @@
 extern const char kUiSlowAnimationsName[];
 extern const char kUiSlowAnimationsDescription[];
 
-extern const char kUnfilteredBluetoothDevicesName[];
-extern const char kUnfilteredBluetoothDevicesDescription[];
-
 extern const char kUsbguardName[];
 extern const char kUsbguardDescription[];
 
diff --git a/chrome/browser/lifetime/browser_close_manager_browsertest.cc b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
index c9243da6..5310015 100644
--- a/chrome/browser/lifetime/browser_close_manager_browsertest.cc
+++ b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
@@ -398,7 +398,14 @@
 
 // Test that browser windows are only closed if all browsers are ready to close
 // and that all beforeunload dialogs are shown again after a cancel.
-IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest, TestMultipleWindows) {
+// Flaky on Windows too: https://crbug.com/997649
+#if defined(OS_WIN)
+#define MAYBE_TestMultipleWindows DISABLED_TestMultipleWindows
+#else
+#define MAYBE_TestMultipleWindows TestMultipleWindows
+#endif
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
+                       MAYBE_TestMultipleWindows) {
   browsers_.push_back(CreateBrowser(browser()->profile()));
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
       browsers_[0], embedded_test_server()->GetURL("/beforeunload.html")));
@@ -438,8 +445,9 @@
 // Test that tabs in the same window with a beforeunload event that hangs are
 // treated the same as the user accepting the close, but do not close the tab
 // early.
+// crbug.com/997649. The test is flaky.
 IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
-                       TestHangInBeforeUnloadMultipleTabs) {
+                       DISABLED_TestHangInBeforeUnloadMultipleTabs) {
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
       browsers_[0], embedded_test_server()->GetURL("/beforeunload_hang.html")));
   AddBlankTabAndShow(browsers_[0]);
@@ -547,7 +555,8 @@
 // tab early.
 // Regression for crbug.com/365052 caused CHECK in tabstrip.
 // Flaky on Mac and Linux: https://crbug.com/819541
-#if defined(OS_LINUX) || defined(OS_MACOSX)
+// Flaky on Windows too: https://crbug.com/997649
+#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
 #define MAYBE_TestBeforeUnloadMultipleSlowWindows \
   DISABLED_TestBeforeUnloadMultipleSlowWindows
 #else
diff --git a/chrome/browser/media/android/router/media_router_android.cc b/chrome/browser/media/android/router/media_router_android.cc
index 6f0c1d9..fcc3ec9 100644
--- a/chrome/browser/media/android/router/media_router_android.cc
+++ b/chrome/browser/media/android/router/media_router_android.cc
@@ -28,21 +28,19 @@
 MediaRouterAndroid::PresentationConnectionProxy::PresentationConnectionProxy(
     MediaRouterAndroid* media_router_android,
     const MediaRoute::Id& route_id)
-    : binding_(this),
-      media_router_android_(media_router_android),
-      route_id_(route_id) {}
+    : media_router_android_(media_router_android), route_id_(route_id) {}
 
 MediaRouterAndroid::PresentationConnectionProxy::
     ~PresentationConnectionProxy() = default;
 
 mojom::RoutePresentationConnectionPtr
 MediaRouterAndroid::PresentationConnectionProxy::Init() {
-  auto request = mojo::MakeRequest(&peer_);
-  peer_.set_connection_error_handler(
+  auto receiver = peer_.BindNewPipeAndPassReceiver();
+  peer_.set_disconnect_handler(
       base::BindOnce(&MediaRouterAndroid::OnPresentationConnectionError,
                      base::Unretained(media_router_android_), route_id_));
   peer_->DidChangeState(blink::mojom::PresentationConnectionState::CONNECTED);
-  return mojom::RoutePresentationConnection::New(Bind(), std::move(request));
+  return mojom::RoutePresentationConnection::New(Bind(), std::move(receiver));
 }
 
 void MediaRouterAndroid::PresentationConnectionProxy::OnMessage(
@@ -66,15 +64,14 @@
   });
 }
 
-blink::mojom::PresentationConnectionPtrInfo
+mojo::PendingRemote<blink::mojom::PresentationConnection>
 MediaRouterAndroid::PresentationConnectionProxy::Bind() {
-  blink::mojom::PresentationConnectionPtrInfo conn_info;
-  auto request = mojo::MakeRequest(&conn_info);
-  binding_.Bind(std::move(request));
-  binding_.set_connection_error_handler(
+  mojo::PendingRemote<blink::mojom::PresentationConnection> connection_remote;
+  receiver_.Bind(connection_remote.InitWithNewPipeAndPassReceiver());
+  receiver_.set_disconnect_handler(
       base::BindOnce(&MediaRouterAndroid::OnPresentationConnectionError,
                      base::Unretained(media_router_android_), route_id_));
-  return conn_info;
+  return connection_remote;
 }
 
 void MediaRouterAndroid::PresentationConnectionProxy::SendMessage(
diff --git a/chrome/browser/media/android/router/media_router_android.h b/chrome/browser/media/android/router/media_router_android.h
index 2674ed0..c13d901 100644
--- a/chrome/browser/media/android/router/media_router_android.h
+++ b/chrome/browser/media/android/router/media_router_android.h
@@ -15,6 +15,9 @@
 #include "base/observer_list.h"
 #include "chrome/browser/media/android/router/media_router_android_bridge.h"
 #include "chrome/browser/media/router/media_router_base.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace content {
 class BrowserContext;
@@ -128,10 +131,10 @@
     void Terminate();
 
    private:
-    blink::mojom::PresentationConnectionPtrInfo Bind();
+    mojo::PendingRemote<blink::mojom::PresentationConnection> Bind();
 
-    blink::mojom::PresentationConnectionPtr peer_;
-    mojo::Binding<blink::mojom::PresentationConnection> binding_;
+    mojo::Remote<blink::mojom::PresentationConnection> peer_;
+    mojo::Receiver<blink::mojom::PresentationConnection> receiver_{this};
     // |media_router_android_| owns |this|, so it will outlive |this|.
     MediaRouterAndroid* media_router_android_;
     MediaRoute::Id route_id_;
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
index f6b93b3..3f263dc 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
@@ -210,7 +210,7 @@
     const base::Optional<std::string>& error_text,
     RouteRequestResult::ResultCode result_code) {
   DCHECK(!connection ||
-         (connection->connection_ptr && connection->connection_request));
+         (connection->connection_remote && connection->connection_receiver));
   std::unique_ptr<RouteRequestResult> result;
   if (!media_route) {
     // An error occurred.
diff --git a/chrome/browser/media/router/presentation/browser_presentation_connection_proxy.cc b/chrome/browser/media/router/presentation/browser_presentation_connection_proxy.cc
index 8f202192..d87deb02 100644
--- a/chrome/browser/media/router/presentation/browser_presentation_connection_proxy.cc
+++ b/chrome/browser/media/router/presentation/browser_presentation_connection_proxy.cc
@@ -24,24 +24,25 @@
 BrowserPresentationConnectionProxy::BrowserPresentationConnectionProxy(
     MediaRouter* router,
     const MediaRoute::Id& route_id,
-    blink::mojom::PresentationConnectionRequest receiver_connection_request,
-    blink::mojom::PresentationConnectionPtr controller_connection_ptr)
+    mojo::PendingReceiver<blink::mojom::PresentationConnection>
+        receiver_connection_receiver,
+    mojo::PendingRemote<blink::mojom::PresentationConnection>
+        controller_connection_remote)
     : RouteMessageObserver(router, route_id),
       router_(router),
       route_id_(route_id),
-      binding_(this),
-      target_connection_ptr_(std::move(controller_connection_ptr)) {
+      target_connection_remote_(std::move(controller_connection_remote)) {
   DCHECK(router);
-  DCHECK(target_connection_ptr_);
+  DCHECK(target_connection_remote_);
 
-  binding_.Bind(std::move(receiver_connection_request));
-  target_connection_ptr_->DidChangeState(
+  receiver_.Bind(std::move(receiver_connection_receiver));
+  target_connection_remote_->DidChangeState(
       blink::mojom::PresentationConnectionState::CONNECTED);
   // TODO(btolsch): These pipes may need proper mojo error handlers.  They
   // probably need to be plumbed up to PSDImpl so the PresentationFrame knows
   // about the error.
-  binding_.set_connection_error_handler(base::BindOnce(LogMojoPipeError));
-  target_connection_ptr_.set_connection_error_handler(
+  receiver_.set_disconnect_handler(base::BindOnce(LogMojoPipeError));
+  target_connection_remote_.set_disconnect_handler(
       base::BindOnce(LogMojoPipeError));
 }
 
@@ -72,7 +73,7 @@
   // TODO(imcheng): It would be slightly more efficient to send messages in
   // a single batch.
   for (auto& message : messages) {
-    target_connection_ptr_->OnMessage(
+    target_connection_remote_->OnMessage(
         message_util::PresentationConnectionFromRouteMessage(
             std::move(message)));
   }
diff --git a/chrome/browser/media/router/presentation/browser_presentation_connection_proxy.h b/chrome/browser/media/router/presentation/browser_presentation_connection_proxy.h
index a9a93d6..9f791f4 100644
--- a/chrome/browser/media/router/presentation/browser_presentation_connection_proxy.h
+++ b/chrome/browser/media/router/presentation/browser_presentation_connection_proxy.h
@@ -10,7 +10,10 @@
 #include "chrome/browser/media/router/route_message_observer.h"
 #include "chrome/common/media_router/media_route.h"
 #include "content/public/browser/presentation_service_delegate.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom.h"
 
 namespace media_router {
@@ -41,17 +44,18 @@
       public RouteMessageObserver {
  public:
   // |router|: media router instance not owned by this class;
-  // |route_id|: underlying media route. |target_connection_ptr_| sends message
-  // to media route with |route_id|;
-  // |receiver_connection_request|: mojo interface request to be bind with this
-  // object;
-  // |controller_connection_ptr|: mojo interface ptr of controlling frame's
+  // |route_id|: underlying media route. |target_connection_remote_| sends
+  // message to media route with |route_id|;
+  // |receiver_connection_receiver|: mojo receiver to be bind with this object;
+  // |controller_connection_remote|: mojo remote of controlling frame's
   // connection proxy object.
   BrowserPresentationConnectionProxy(
       MediaRouter* router,
       const MediaRoute::Id& route_id,
-      blink::mojom::PresentationConnectionRequest receiver_connection_request,
-      blink::mojom::PresentationConnectionPtr controller_connection_ptr);
+      mojo::PendingReceiver<blink::mojom::PresentationConnection>
+          receiver_connection_receiver,
+      mojo::PendingRemote<blink::mojom::PresentationConnection>
+          controller_connection_remote);
   ~BrowserPresentationConnectionProxy() override;
 
   // blink::mojom::PresentationConnection implementation
@@ -74,8 +78,8 @@
   MediaRouter* const router_;
   const MediaRoute::Id route_id_;
 
-  mojo::Binding<blink::mojom::PresentationConnection> binding_;
-  blink::mojom::PresentationConnectionPtr target_connection_ptr_;
+  mojo::Receiver<blink::mojom::PresentationConnection> receiver_{this};
+  mojo::Remote<blink::mojom::PresentationConnection> target_connection_remote_;
 };
 
 }  // namespace media_router
diff --git a/chrome/browser/media/router/presentation/browser_presentation_connection_proxy_unittest.cc b/chrome/browser/media/router/presentation/browser_presentation_connection_proxy_unittest.cc
index cdf8cf65..fbfb801 100644
--- a/chrome/browser/media/router/presentation/browser_presentation_connection_proxy_unittest.cc
+++ b/chrome/browser/media/router/presentation/browser_presentation_connection_proxy_unittest.cc
@@ -12,6 +12,9 @@
 #include "chrome/browser/media/router/test/test_helper.h"
 #include "chrome/common/media_router/media_source.h"
 #include "content/public/test/browser_task_environment.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 using blink::mojom::PresentationConnectionMessage;
@@ -40,30 +43,32 @@
   void SetUp() override {
     mock_controller_connection_proxy_ =
         std::make_unique<MockPresentationConnectionProxy>();
-    blink::mojom::PresentationConnectionPtr controller_connection_ptr;
-    binding_.reset(new mojo::Binding<blink::mojom::PresentationConnection>(
+    mojo::PendingRemote<blink::mojom::PresentationConnection>
+        controller_connection_remote;
+    receiver_.reset(new mojo::Receiver<blink::mojom::PresentationConnection>(
         mock_controller_connection_proxy_.get(),
-        mojo::MakeRequest(&controller_connection_ptr)));
+        controller_connection_remote.InitWithNewPipeAndPassReceiver()));
     EXPECT_CALL(mock_router_, RegisterRouteMessageObserver(_));
     EXPECT_CALL(
         *mock_controller_connection_proxy_,
         DidChangeState(blink::mojom::PresentationConnectionState::CONNECTED));
 
-    blink::mojom::PresentationConnectionPtr receiver_connection_ptr;
+    mojo::Remote<blink::mojom::PresentationConnection>
+        receiver_connection_remote;
 
     base::RunLoop run_loop;
     browser_connection_proxy_ =
         std::make_unique<BrowserPresentationConnectionProxy>(
             &mock_router_, "MockRouteId",
-            mojo::MakeRequest(&receiver_connection_ptr),
-            std::move(controller_connection_ptr));
+            receiver_connection_remote.BindNewPipeAndPassReceiver(),
+            std::move(controller_connection_remote));
     run_loop.RunUntilIdle();
   }
 
   void TearDown() override {
     EXPECT_CALL(mock_router_, UnregisterRouteMessageObserver(_));
     browser_connection_proxy_.reset();
-    binding_.reset();
+    receiver_.reset();
     mock_controller_connection_proxy_.reset();
   }
 
@@ -81,7 +86,8 @@
   content::BrowserTaskEnvironment task_environment_;
   std::unique_ptr<MockPresentationConnectionProxy>
       mock_controller_connection_proxy_;
-  std::unique_ptr<mojo::Binding<blink::mojom::PresentationConnection>> binding_;
+  std::unique_ptr<mojo::Receiver<blink::mojom::PresentationConnection>>
+      receiver_;
   std::unique_ptr<BrowserPresentationConnectionProxy> browser_connection_proxy_;
   MockMediaRouter mock_router_;
 };
diff --git a/chrome/browser/media/router/presentation/local_presentation_manager.cc b/chrome/browser/media/router/presentation/local_presentation_manager.cc
index 1474565..04ce4c7 100644
--- a/chrome/browser/media/router/presentation/local_presentation_manager.cc
+++ b/chrome/browser/media/router/presentation/local_presentation_manager.cc
@@ -37,8 +37,10 @@
 void LocalPresentationManager::RegisterLocalPresentationController(
     const PresentationInfo& presentation_info,
     const content::GlobalFrameRoutingId& render_frame_host_id,
-    content::PresentationConnectionPtr controller_connection_ptr,
-    content::PresentationConnectionRequest receiver_connection_request,
+    mojo::PendingRemote<blink::mojom::PresentationConnection>
+        controller_connection_remote,
+    mojo::PendingReceiver<blink::mojom::PresentationConnection>
+        receiver_connection_receiver,
     const MediaRoute& route) {
   DVLOG(2) << __func__ << " [presentation_id]: " << presentation_info.id
            << ", [render_frame_host_id]: "
@@ -47,8 +49,8 @@
 
   auto* presentation = GetOrCreateLocalPresentation(presentation_info);
   presentation->RegisterController(
-      render_frame_host_id, std::move(controller_connection_ptr),
-      std::move(receiver_connection_request), route);
+      render_frame_host_id, std::move(controller_connection_remote),
+      std::move(receiver_connection_receiver), route);
 }
 
 void LocalPresentationManager::UnregisterLocalPresentationController(
@@ -111,18 +113,20 @@
 
 void LocalPresentationManager::LocalPresentation::RegisterController(
     const content::GlobalFrameRoutingId& render_frame_host_id,
-    content::PresentationConnectionPtr controller_connection_ptr,
-    content::PresentationConnectionRequest receiver_connection_request,
+    mojo::PendingRemote<blink::mojom::PresentationConnection>
+        controller_connection_remote,
+    mojo::PendingReceiver<blink::mojom::PresentationConnection>
+        receiver_connection_receiver,
     const MediaRoute& route) {
   if (!receiver_callback_.is_null()) {
     receiver_callback_.Run(PresentationInfo::New(presentation_info_),
-                           std::move(controller_connection_ptr),
-                           std::move(receiver_connection_request));
+                           std::move(controller_connection_remote),
+                           std::move(receiver_connection_receiver));
   } else {
     pending_controllers_.insert(std::make_pair(
         render_frame_host_id, std::make_unique<ControllerConnection>(
-                                  std::move(controller_connection_ptr),
-                                  std::move(receiver_connection_request))));
+                                  std::move(controller_connection_remote),
+                                  std::move(receiver_connection_receiver))));
   }
 
   route_ = route;
@@ -140,8 +144,8 @@
   for (auto& controller : pending_controllers_) {
     receiver_callback.Run(
         PresentationInfo::New(presentation_info_),
-        std::move(controller.second->controller_connection_ptr),
-        std::move(controller.second->receiver_connection_request));
+        std::move(controller.second->controller_connection_remote),
+        std::move(controller.second->receiver_connection_receiver));
   }
   receiver_callback_ = receiver_callback;
   pending_controllers_.clear();
@@ -153,10 +157,12 @@
 
 LocalPresentationManager::LocalPresentation::ControllerConnection::
     ControllerConnection(
-        content::PresentationConnectionPtr controller_connection_ptr,
-        content::PresentationConnectionRequest receiver_connection_request)
-    : controller_connection_ptr(std::move(controller_connection_ptr)),
-      receiver_connection_request(std::move(receiver_connection_request)) {}
+        mojo::PendingRemote<blink::mojom::PresentationConnection>
+            controller_connection_remote,
+        mojo::PendingReceiver<blink::mojom::PresentationConnection>
+            receiver_connection_receiver)
+    : controller_connection_remote(std::move(controller_connection_remote)),
+      receiver_connection_receiver(std::move(receiver_connection_receiver)) {}
 
 LocalPresentationManager::LocalPresentation::ControllerConnection::
     ~ControllerConnection() {}
diff --git a/chrome/browser/media/router/presentation/local_presentation_manager.h b/chrome/browser/media/router/presentation/local_presentation_manager.h
index bcddd4f..cb31e052 100644
--- a/chrome/browser/media/router/presentation/local_presentation_manager.h
+++ b/chrome/browser/media/router/presentation/local_presentation_manager.h
@@ -17,6 +17,8 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/presentation_service_delegate.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom.h"
 
 namespace media_router {
@@ -42,40 +44,40 @@
 //
 // Controlling frame establishes connection with the receiver side, resulting
 // in a connection with the two endpoints being the controller
-// PresentationConnectionPtr and receiver PresentationConnectionPtr.
+// PresentationConnectionRemote and receiver PresentationConnectionReceiver.
 // Note calling this will trigger receiver frame's
 // PresentationServiceImpl::OnReceiverConnectionAvailable.
 //
 //   manager->RegisterLocalPresentationController(
 //       presentation_info,
-//       std::move(controller_connection_ptr,
-//       std::move(receiver_connection_request));
+//       std::move(controller_connection_remote,
+//       std::move(receiver_connection_receiver));
 //
 // Invoked on receiver's PresentationServiceImpl when controller connection is
 // established.
 //
-//   |presentation_receiver_client_|: blink::mojom::PresentationServiceClienPtr
-//   for the presentation receiver.
-//   |controller_connection_ptr|: blink::mojom::PresentationConnectionPtr for
+//   |presentation_receiver_remote_|: mojo::Remote<T> for the implementation of
+//   the blink::mojom::PresentationService interface in the renderer process.
+//   |controller_connection_remote|: mojo::PendingRemote<T> for
 //   blink::PresentationConnection object in controlling frame's render process.
-//   |receiver_connection_request|: Mojo InterfaceRequest to be bind to
+//   |receiver_connection_receiver|: mojo::PendingReceiver<T> to be bind to
 //   blink::PresentationConnection object in receiver frame's render process.
 //   void PresentationServiceImpl::OnReceiverConnectionAvailable(
 //       const blink::mojom::PresentationInfo& presentation_info,
-//       PresentationConnectionPtr controller_connection_ptr,
-//       PresentationConnectionRequest receiver_connection_request) {
-//     presentation_receiver_client_->OnReceiverConnectionAvailable(
+//       PresentationConnectionRemote controller_connection_remote,
+//       PresentationConnectionReceiver receiver_connection_receiver) {
+//     presentation_receiver_remote_->OnReceiverConnectionAvailable(
 //         blink::mojom::PresentationInfo::From(presentation_info),
-//         std::move(controller_connection_ptr),
-//         std::move(receiver_connection_request));
+//         std::move(controller_connection_remote),
+//         std::move(receiver_connection_receiver));
 //   }
 //
 // Send message from controlling/receiver frame to receiver/controlling frame:
 //
-//   |target_connection_|: member variable of
-//                         blink::mojom::PresentationConnectionPtr type,
-//                         refering to remote PresentationConnectionProxy
-//                         object on receiver/controlling frame.
+//   |target_connection_|: member variable of mojo::PendingRemote<T> for
+//                         blink::PresentationConnection type, referring to
+//                         remote PresentationConnectionProxy object on
+//                         receiver/controlling frame.
 //   |message|: Text message to be sent.
 //   PresentationConnctionPtr::SendString(
 //       const blink::WebString& message) {
@@ -109,14 +111,16 @@
   // |presentation_id| and |render_frame_id|.
   // Creates a new presentation if no presentation with |presentation_id|
   // exists.
-  // |controller_connection_ptr|, |receiver_connection_request|: Not owned by
-  // this class. Ownership is transferred to presentation receiver via
+  // |controller_connection_remote|, |receiver_connection_receiver|: Not owned
+  // by this class. Ownership is transferred to presentation receiver via
   // |receiver_callback| passed below.
   virtual void RegisterLocalPresentationController(
       const blink::mojom::PresentationInfo& presentation_info,
       const content::GlobalFrameRoutingId& render_frame_id,
-      content::PresentationConnectionPtr controller_connection_ptr,
-      content::PresentationConnectionRequest receiver_connection_request,
+      mojo::PendingRemote<blink::mojom::PresentationConnection>
+          controller_connection_remote,
+      mojo::PendingReceiver<blink::mojom::PresentationConnection>
+          receiver_connection_receiver,
       const MediaRoute& route);
 
   // Unregisters controller PresentationConnectionPtr to presentation with
@@ -159,15 +163,17 @@
     ~LocalPresentation();
 
     // Register controller with |render_frame_id|. If |receiver_callback_| has
-    // been set, invoke |receiver_callback_| with |controller_connection_ptr|
-    // and |receiver_connection_request| as parameter, else creates a
-    // ControllerConnection object with |controller_connection_ptr| and
-    // |receiver_connection_request|, and store it in |pending_controllers_|
+    // been set, invoke |receiver_callback_| with |controller_connection_remote|
+    // and |receiver_connection_receiver| as parameter, else creates a
+    // ControllerConnection object with |controller_connection_remote| and
+    // |receiver_connection_receiver|, and store it in |pending_controllers_|
     // map.
     void RegisterController(
         const content::GlobalFrameRoutingId& render_frame_id,
-        content::PresentationConnectionPtr controller_connection_ptr,
-        content::PresentationConnectionRequest receiver_connection_request,
+        mojo::PendingRemote<blink::mojom::PresentationConnection>
+            controller_connection_remote,
+        mojo::PendingReceiver<blink::mojom::PresentationConnection>
+            receiver_connection_receiver,
         const MediaRoute& route);
 
     // Unregister controller with |render_frame_id|. Do nothing if there is no
@@ -197,19 +203,23 @@
     content::ReceiverConnectionAvailableCallback receiver_callback_;
 
     // Stores controller information.
-    // |controller_connection_ptr|: Mojo::InterfacePtr to
+    // |controller_connection_remote|: mojo::PendingRemote<T> to
     // blink::PresentationConnection object in controlling frame;
-    // |receiver_connection_request|: Mojo::InterfaceRequest to be bind to
+    // |receiver_connection_receiver|: mojo::PendingReceiver<T> to be bind to
     // blink::PresentationConnection object in receiver frame.
     struct ControllerConnection {
      public:
       ControllerConnection(
-          content::PresentationConnectionPtr controller_connection_ptr,
-          content::PresentationConnectionRequest receiver_connection_request);
+          mojo::PendingRemote<blink::mojom::PresentationConnection>
+              controller_connection_remote,
+          mojo::PendingReceiver<blink::mojom::PresentationConnection>
+              receiver_connection_receiver);
       ~ControllerConnection();
 
-      content::PresentationConnectionPtr controller_connection_ptr;
-      content::PresentationConnectionRequest receiver_connection_request;
+      mojo::PendingRemote<blink::mojom::PresentationConnection>
+          controller_connection_remote;
+      mojo::PendingReceiver<blink::mojom::PresentationConnection>
+          receiver_connection_receiver;
     };
 
     // Contains ControllerConnection objects registered via
diff --git a/chrome/browser/media/router/presentation/local_presentation_manager_unittest.cc b/chrome/browser/media/router/presentation/local_presentation_manager_unittest.cc
index 3d9335cd..d02b2fae 100644
--- a/chrome/browser/media/router/presentation/local_presentation_manager_unittest.cc
+++ b/chrome/browser/media/router/presentation/local_presentation_manager_unittest.cc
@@ -9,6 +9,8 @@
 #include "base/stl_util.h"
 #include "chrome/browser/media/router/presentation/local_presentation_manager.h"
 #include "chrome/browser/media/router/test/test_helper.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -27,10 +29,11 @@
 
 class MockReceiverConnectionAvailableCallback {
  public:
-  MOCK_METHOD3(OnReceiverConnectionAvailable,
-               void(PresentationInfoPtr,
-                    content::PresentationConnectionPtr,
-                    content::PresentationConnectionRequest));
+  MOCK_METHOD3(
+      OnReceiverConnectionAvailable,
+      void(PresentationInfoPtr,
+           mojo::PendingRemote<blink::mojom::PresentationConnection>,
+           mojo::PendingReceiver<blink::mojom::PresentationConnection>));
 };
 
 class LocalPresentationManagerTest : public ::testing::Test {
@@ -54,31 +57,36 @@
                             ->pending_controllers_.size());
   }
 
-  void RegisterController(const std::string& presentation_id,
-                          content::PresentationConnectionPtr controller) {
+  void RegisterController(
+      const std::string& presentation_id,
+      mojo::PendingRemote<blink::mojom::PresentationConnection> controller) {
     RegisterController(
         PresentationInfo(GURL(kPresentationUrl), presentation_id),
         render_frame_host_id_, std::move(controller));
   }
 
-  void RegisterController(const content::GlobalFrameRoutingId& render_frame_id,
-                          content::PresentationConnectionPtr controller) {
+  void RegisterController(
+      const content::GlobalFrameRoutingId& render_frame_id,
+      mojo::PendingRemote<blink::mojom::PresentationConnection> controller) {
     RegisterController(presentation_info_, render_frame_id,
                        std::move(controller));
   }
 
-  void RegisterController(content::PresentationConnectionPtr controller) {
+  void RegisterController(
+      mojo::PendingRemote<blink::mojom::PresentationConnection> controller) {
     RegisterController(presentation_info_, render_frame_host_id_,
                        std::move(controller));
   }
 
-  void RegisterController(const PresentationInfo& presentation_info,
-                          const content::GlobalFrameRoutingId& render_frame_id,
-                          content::PresentationConnectionPtr controller) {
-    content::PresentationConnectionRequest receiver_conn_request;
+  void RegisterController(
+      const PresentationInfo& presentation_info,
+      const content::GlobalFrameRoutingId& render_frame_id,
+      mojo::PendingRemote<blink::mojom::PresentationConnection> controller) {
+    mojo::PendingReceiver<blink::mojom::PresentationConnection>
+        receiver_conn_receiver;
     manager()->RegisterLocalPresentationController(
         presentation_info, render_frame_id, std::move(controller),
-        std::move(receiver_conn_request), route_);
+        std::move(receiver_conn_receiver), route_);
   }
 
   void RegisterReceiver(
@@ -119,7 +127,7 @@
 };
 
 TEST_F(LocalPresentationManagerTest, RegisterUnregisterController) {
-  content::PresentationConnectionPtr controller;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller;
   RegisterController(std::move(controller));
   VerifyPresentationsSize(1);
   UnregisterController();
@@ -146,10 +154,10 @@
 
 TEST_F(LocalPresentationManagerTest,
        RegisterMultipleControllersSamePresentation) {
-  content::PresentationConnectionPtr controller1;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller1;
   RegisterController(content::GlobalFrameRoutingId(1, 1),
                      std::move(controller1));
-  content::PresentationConnectionPtr controller2;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller2;
   RegisterController(content::GlobalFrameRoutingId(1, 2),
                      std::move(controller2));
   VerifyPresentationsSize(1);
@@ -157,16 +165,16 @@
 
 TEST_F(LocalPresentationManagerTest,
        RegisterMultipleControllersDifferentPresentations) {
-  content::PresentationConnectionPtr controller1;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller1;
   RegisterController(kPresentationId, std::move(controller1));
-  content::PresentationConnectionPtr controller2;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller2;
   RegisterController(kPresentationId2, std::move(controller2));
   VerifyPresentationsSize(2);
 }
 
 TEST_F(LocalPresentationManagerTest,
        RegisterControllerThenReceiverInvokesCallback) {
-  content::PresentationConnectionPtr controller;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller;
   MockReceiverConnectionAvailableCallback receiver_callback;
 
   VerifyPresentationsSize(0);
@@ -178,7 +186,7 @@
 
 TEST_F(LocalPresentationManagerTest,
        UnregisterReceiverFromConnectedPresentation) {
-  content::PresentationConnectionPtr controller;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller;
   MockReceiverConnectionAvailableCallback receiver_callback;
 
   VerifyPresentationsSize(0);
@@ -193,7 +201,7 @@
 
 TEST_F(LocalPresentationManagerTest,
        UnregisterControllerFromConnectedPresentation) {
-  content::PresentationConnectionPtr controller;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller;
   MockReceiverConnectionAvailableCallback receiver_callback;
 
   VerifyPresentationsSize(0);
@@ -208,7 +216,7 @@
 
 TEST_F(LocalPresentationManagerTest,
        UnregisterReceiverThenControllerFromConnectedPresentation) {
-  content::PresentationConnectionPtr controller;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller;
   MockReceiverConnectionAvailableCallback receiver_callback;
 
   VerifyPresentationsSize(0);
@@ -224,7 +232,7 @@
 
 TEST_F(LocalPresentationManagerTest,
        UnregisterControllerThenReceiverFromConnectedPresentation) {
-  content::PresentationConnectionPtr controller;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller;
   MockReceiverConnectionAvailableCallback receiver_callback;
 
   VerifyPresentationsSize(0);
@@ -240,10 +248,10 @@
 
 TEST_F(LocalPresentationManagerTest,
        RegisterTwoControllersThenReceiverInvokesCallbackTwice) {
-  content::PresentationConnectionPtr controller1;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller1;
   RegisterController(content::GlobalFrameRoutingId(1, 1),
                      std::move(controller1));
-  content::PresentationConnectionPtr controller2;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller2;
   RegisterController(content::GlobalFrameRoutingId(1, 2),
                      std::move(controller2));
 
@@ -255,7 +263,7 @@
 
 TEST_F(LocalPresentationManagerTest,
        RegisterControllerReceiverConontrollerInvokesCallbackTwice) {
-  content::PresentationConnectionPtr controller1;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller1;
   RegisterController(content::GlobalFrameRoutingId(1, 1),
                      std::move(controller1));
 
@@ -264,17 +272,17 @@
       .Times(2);
   RegisterReceiver(receiver_callback);
 
-  content::PresentationConnectionPtr controller2;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller2;
   RegisterController(content::GlobalFrameRoutingId(1, 2),
                      std::move(controller2));
 }
 
 TEST_F(LocalPresentationManagerTest,
        UnregisterFirstControllerFromeConnectedPresentation) {
-  content::PresentationConnectionPtr controller1;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller1;
   RegisterController(content::GlobalFrameRoutingId(1, 1),
                      std::move(controller1));
-  content::PresentationConnectionPtr controller2;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller2;
   RegisterController(content::GlobalFrameRoutingId(1, 2),
                      std::move(controller2));
 
@@ -289,7 +297,7 @@
 }
 
 TEST_F(LocalPresentationManagerTest, TwoPresentations) {
-  content::PresentationConnectionPtr controller1;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller1;
   RegisterController(kPresentationId, std::move(controller1));
 
   MockReceiverConnectionAvailableCallback receiver_callback1;
@@ -297,7 +305,7 @@
       .Times(1);
   RegisterReceiver(kPresentationId, receiver_callback1);
 
-  content::PresentationConnectionPtr controller2;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller2;
   RegisterController(kPresentationId2, std::move(controller2));
 
   MockReceiverConnectionAvailableCallback receiver_callback2;
@@ -313,7 +321,7 @@
 
 TEST_F(LocalPresentationManagerTest, TestIsLocalPresentation) {
   EXPECT_FALSE(manager()->IsLocalPresentation(kPresentationId));
-  content::PresentationConnectionPtr controller1;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller1;
   RegisterController(kPresentationId, std::move(controller1));
   EXPECT_TRUE(manager()->IsLocalPresentation(kPresentationId));
 }
@@ -323,7 +331,7 @@
   MediaRoute route("route_1", source, "sink_1", "", false, false);
 
   EXPECT_FALSE(manager()->GetRoute(kPresentationId));
-  content::PresentationConnectionPtr controller;
+  mojo::PendingRemote<blink::mojom::PresentationConnection> controller;
   RegisterController(std::move(controller));
 
   auto* actual_route = manager()->GetRoute(kPresentationId);
diff --git a/chrome/browser/media/router/presentation/presentation_service_delegate_impl.cc b/chrome/browser/media/router/presentation/presentation_service_delegate_impl.cc
index 4f2c1b5..12b75d3 100644
--- a/chrome/browser/media/router/presentation/presentation_service_delegate_impl.cc
+++ b/chrome/browser/media/router/presentation/presentation_service_delegate_impl.cc
@@ -32,6 +32,8 @@
 #include "content/public/browser/presentation_screen_availability_listener.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "url/gurl.h"
 
 #if !defined(OS_ANDROID)
@@ -40,9 +42,7 @@
 #include "components/prefs/pref_service.h"
 #endif
 
-using blink::mojom::PresentationConnectionPtr;
-using blink::mojom::PresentationConnectionPtrInfo;
-using blink::mojom::PresentationConnectionRequest;
+using blink::mojom::PresentationConnection;
 using blink::mojom::PresentationError;
 using blink::mojom::PresentationErrorType;
 using blink::mojom::PresentationInfo;
@@ -110,8 +110,9 @@
                        const MediaRoute& route);
   void ConnectToPresentation(
       const PresentationInfo& presentation_info,
-      PresentationConnectionPtr controller_connection_ptr,
-      PresentationConnectionRequest receiver_connection_request);
+      mojo::PendingRemote<PresentationConnection> controller_connection_remote,
+      mojo::PendingReceiver<PresentationConnection>
+          receiver_connection_receiver);
   void RemovePresentation(const std::string& presentation_id);
 
  private:
@@ -225,8 +226,9 @@
 
 void PresentationFrame::ConnectToPresentation(
     const PresentationInfo& presentation_info,
-    PresentationConnectionPtr controller_connection_ptr,
-    PresentationConnectionRequest receiver_connection_request) {
+    mojo::PendingRemote<PresentationConnection> controller_connection_remote,
+    mojo::PendingReceiver<PresentationConnection>
+        receiver_connection_receiver) {
   const auto pid_route_it =
       presentation_id_to_route_.find(presentation_info.id);
 
@@ -241,8 +243,8 @@
             web_contents_);
     local_presentation_manager->RegisterLocalPresentationController(
         presentation_info, render_frame_host_id_,
-        std::move(controller_connection_ptr),
-        std::move(receiver_connection_request), pid_route_it->second);
+        std::move(controller_connection_remote),
+        std::move(receiver_connection_receiver), pid_route_it->second);
   } else {
     DVLOG(2)
         << "Creating BrowserPresentationConnectionProxy for [presentation_id]: "
@@ -256,8 +258,8 @@
     }
 
     auto* proxy = new BrowserPresentationConnectionProxy(
-        router_, route_id, std::move(receiver_connection_request),
-        std::move(controller_connection_ptr));
+        router_, route_id, std::move(receiver_connection_receiver),
+        std::move(controller_connection_remote));
     browser_connection_proxies_.emplace(route_id, base::WrapUnique(proxy));
   }
 }
@@ -470,8 +472,8 @@
                                  &connection);
     std::move(success_cb)
         .Run(blink::mojom::PresentationConnectionResult::New(
-            presentation_info.Clone(), std::move(connection->connection_ptr),
-            std::move(connection->connection_request)));
+            presentation_info.Clone(), std::move(connection->connection_remote),
+            std::move(connection->connection_receiver)));
   }
 }
 
@@ -490,8 +492,9 @@
                                &connection);
   std::move(success_cb)
       .Run(blink::mojom::PresentationConnectionResult::New(
-          new_presentation_info.Clone(), std::move(connection->connection_ptr),
-          std::move(connection->connection_request)));
+          new_presentation_info.Clone(),
+          std::move(connection->connection_remote),
+          std::move(connection->connection_receiver)));
 }
 
 void PresentationServiceDelegateImpl::AddPresentation(
@@ -685,8 +688,8 @@
                                  presentation_info, &connection);
     default_presentation_started_callback_.Run(
         blink::mojom::PresentationConnectionResult::New(
-            presentation_info.Clone(), std::move(connection->connection_ptr),
-            std::move(connection->connection_request)));
+            presentation_info.Clone(), std::move(connection->connection_remote),
+            std::move(connection->connection_receiver)));
   } else {
     DCHECK(!connection);
   }
@@ -783,14 +786,15 @@
   // directly, we need to provide a BrowserPresentationConnectionProxy here or
   // connect to the LocalPresentationManager, as necessary.
   if (!*connection) {
-    PresentationConnectionPtr controller_ptr;
-    PresentationConnectionPtrInfo receiver_ptr_info;
+    mojo::PendingRemote<PresentationConnection> controller_remote;
+    mojo::PendingRemote<PresentationConnection> receiver_remote;
     *connection = mojom::RoutePresentationConnection::New(
-        std::move(receiver_ptr_info), mojo::MakeRequest(&controller_ptr));
+        std::move(receiver_remote),
+        controller_remote.InitWithNewPipeAndPassReceiver());
     auto* presentation_frame = GetOrAddPresentationFrame(render_frame_host_id);
     presentation_frame->ConnectToPresentation(
-        presentation_info, std::move(controller_ptr),
-        mojo::MakeRequest(&(*connection)->connection_ptr));
+        presentation_info, std::move(controller_remote),
+        (*connection)->connection_remote.InitWithNewPipeAndPassReceiver());
   }
 }
 
diff --git a/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc b/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
index 93faa12..e6464a3f49 100644
--- a/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
+++ b/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
@@ -25,7 +25,10 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/web_contents_tester.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "url/origin.h"
 
@@ -86,18 +89,18 @@
   void RegisterLocalPresentationController(
       const PresentationInfo& presentation_info,
       const content::GlobalFrameRoutingId& render_frame_id,
-      content::PresentationConnectionPtr controller,
-      content::PresentationConnectionRequest request,
+      mojo::PendingRemote<PresentationConnection> controller,
+      mojo::PendingReceiver<PresentationConnection> receiver,
       const MediaRoute& route) override {
     RegisterLocalPresentationControllerInternal(
-        presentation_info, render_frame_id, controller, request, route);
+        presentation_info, render_frame_id, controller, receiver, route);
   }
 
   MOCK_METHOD5(RegisterLocalPresentationControllerInternal,
                void(const PresentationInfo& presentation_info,
                     const content::GlobalFrameRoutingId& render_frame_id,
-                    content::PresentationConnectionPtr& controller,
-                    content::PresentationConnectionRequest& request,
+                    mojo::PendingRemote<PresentationConnection>& controller,
+                    mojo::PendingReceiver<PresentationConnection>& receiver,
                     const MediaRoute& route));
   MOCK_METHOD2(UnregisterLocalPresentationController,
                void(const std::string& presentation_id,
@@ -567,32 +570,32 @@
                          "mediaSinkId", "", true, true);
   media_route.set_local_presentation(true);
 
-  content::PresentationConnectionPtr receiver_ptr;
+  mojo::Remote<PresentationConnection> receiver_remote;
   MockPresentationConnectionProxy controller_proxy;
-  mojo::Binding<PresentationConnection> controller_binding(&controller_proxy);
-  auto success_cb = [&controller_binding,
-                     &receiver_ptr](PresentationConnectionResultPtr result) {
-    controller_binding.Bind(std::move(result->connection_request));
-    receiver_ptr =
-        content::PresentationConnectionPtr(std::move(result->connection_ptr));
+  mojo::Receiver<PresentationConnection> controller_receiver(&controller_proxy);
+  auto success_cb = [&controller_receiver,
+                     &receiver_remote](PresentationConnectionResultPtr result) {
+    controller_receiver.Bind(std::move(result->connection_receiver));
+    receiver_remote = mojo::Remote<PresentationConnection>(
+        std::move(result->connection_remote));
   };
 
-  content::PresentationConnectionPtr controller_ptr;
+  mojo::Remote<PresentationConnection> connection_remote;
   MockPresentationConnectionProxy receiver_proxy;
-  mojo::Binding<PresentationConnection> receiver_binding(&receiver_proxy);
+  mojo::Receiver<PresentationConnection> receiver_receiver(&receiver_proxy);
   auto& mock_local_manager = GetMockLocalPresentationManager();
   EXPECT_CALL(mock_local_manager,
               RegisterLocalPresentationControllerInternal(
                   InfoEquals(presentation_info), rfh_id, _, _, media_route))
-      .WillOnce([&receiver_binding, &controller_ptr](
+      .WillOnce([&receiver_receiver, &connection_remote](
                     const PresentationInfo&,
                     const content::GlobalFrameRoutingId&,
-                    content::PresentationConnectionPtr& controller,
-                    content::PresentationConnectionRequest& request,
+                    mojo::PendingRemote<PresentationConnection>& controller,
+                    mojo::PendingReceiver<PresentationConnection>& receiver,
                     const MediaRoute&) {
-        ASSERT_TRUE(controller && request);
-        receiver_binding.Bind(std::move(request));
-        controller_ptr = std::move(controller);
+        ASSERT_TRUE(controller && receiver);
+        receiver_receiver.Bind(std::move(receiver));
+        connection_remote.Bind(std::move(controller));
       });
 
   delegate_impl_->OnStartPresentationSucceeded(
@@ -605,14 +608,15 @@
     EXPECT_TRUE(
         message->Equals(*PresentationConnectionMessage::NewMessage("alpha")));
   });
-  controller_ptr->OnMessage(PresentationConnectionMessage::NewMessage("alpha"));
+  connection_remote->OnMessage(
+      PresentationConnectionMessage::NewMessage("alpha"));
   base::RunLoop().RunUntilIdle();
 
   EXPECT_CALL(receiver_proxy, OnMessage(_)).WillOnce([](auto message) {
     EXPECT_TRUE(
         message->Equals(*PresentationConnectionMessage::NewMessage("beta")));
   });
-  receiver_ptr->OnMessage(PresentationConnectionMessage::NewMessage("beta"));
+  receiver_remote->OnMessage(PresentationConnectionMessage::NewMessage("beta"));
   base::RunLoop().RunUntilIdle();
 
   EXPECT_CALL(mock_local_manager,
@@ -630,15 +634,15 @@
                          MediaSource::ForPresentationUrl(presentation_info.url),
                          "mediaSinkId", "", true, true);
 
-  content::PresentationConnectionPtr connection_ptr;
+  mojo::Remote<PresentationConnection> connection_remote;
   MockPresentationConnectionProxy mock_proxy;
-  mojo::Binding<PresentationConnection> binding(&mock_proxy);
-  auto success_cb = [&binding,
-                     &connection_ptr](PresentationConnectionResultPtr result) {
-    binding.Bind(std::move(result->connection_request));
-    connection_ptr =
-        content::PresentationConnectionPtr(std::move(result->connection_ptr));
-  };
+  mojo::Receiver<PresentationConnection> receiver(&mock_proxy);
+  auto success_cb =
+      [&receiver, &connection_remote](PresentationConnectionResultPtr result) {
+        receiver.Bind(std::move(result->connection_receiver));
+        connection_remote = mojo::Remote<PresentationConnection>(
+            std::move(result->connection_remote));
+      };
 
   RouteMessageObserver* proxy_message_observer = nullptr;
   EXPECT_CALL(*router_, RegisterRouteMessageObserver(_))
@@ -655,7 +659,8 @@
 
   EXPECT_CALL(*router_,
               SendRouteMessage(media_route.media_route_id(), "alpha"));
-  connection_ptr->OnMessage(PresentationConnectionMessage::NewMessage("alpha"));
+  connection_remote->OnMessage(
+      PresentationConnectionMessage::NewMessage("alpha"));
   base::RunLoop().RunUntilIdle();
 
   EXPECT_CALL(mock_proxy, OnMessage(_)).WillOnce([](auto message) {
diff --git a/chrome/browser/media/router/providers/cast/cast_session_client.cc b/chrome/browser/media/router/providers/cast/cast_session_client.cc
index cceb3ee..803cb462 100644
--- a/chrome/browser/media/router/providers/cast/cast_session_client.cc
+++ b/chrome/browser/media/router/providers/cast/cast_session_client.cc
@@ -10,11 +10,10 @@
 #include "chrome/browser/media/router/providers/cast/cast_activity_record.h"
 #include "chrome/browser/media/router/providers/cast/cast_internal_message_util.h"
 #include "components/cast_channel/enum_table.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 
 using blink::mojom::PresentationConnectionCloseReason;
 using blink::mojom::PresentationConnectionMessagePtr;
-using blink::mojom::PresentationConnectionPtrInfo;
 using blink::mojom::PresentationConnectionState;
 
 namespace media_router {
@@ -46,23 +45,25 @@
     : CastSessionClient(client_id, origin, tab_id),
       auto_join_policy_(auto_join_policy),
       data_decoder_(data_decoder),
-      activity_(activity),
-      connection_binding_(this) {}
+      activity_(activity) {}
 
 CastSessionClientImpl::~CastSessionClientImpl() = default;
 
 mojom::RoutePresentationConnectionPtr CastSessionClientImpl::Init() {
-  PresentationConnectionPtrInfo renderer_connection;
-  connection_binding_.Bind(mojo::MakeRequest(&renderer_connection));
-  auto connection_request = mojo::MakeRequest(&connection_);
-  connection_->DidChangeState(PresentationConnectionState::CONNECTED);
-  return mojom::RoutePresentationConnection::New(std::move(renderer_connection),
-                                                 std::move(connection_request));
+  auto renderer_connection = connection_receiver_.BindNewPipeAndPassRemote();
+  mojo::PendingRemote<blink::mojom::PresentationConnection>
+      pending_connection_remote;
+  auto connection_receiver =
+      pending_connection_remote.InitWithNewPipeAndPassReceiver();
+  connection_remote_.Bind(std::move(pending_connection_remote));
+  connection_remote_->DidChangeState(PresentationConnectionState::CONNECTED);
+  return mojom::RoutePresentationConnection::New(
+      std::move(renderer_connection), std::move(connection_receiver));
 }
 
 void CastSessionClientImpl::SendMessageToClient(
     PresentationConnectionMessagePtr message) {
-  connection_->OnMessage(std::move(message));
+  connection_remote_->OnMessage(std::move(message));
 }
 
 void CastSessionClientImpl::SendMediaStatusToClient(
@@ -242,23 +243,22 @@
 
 void CastSessionClientImpl::CloseConnection(
     PresentationConnectionCloseReason close_reason) {
-  if (connection_)
-    connection_->DidClose(close_reason);
+  if (connection_remote_)
+    connection_remote_->DidClose(close_reason);
 
   TearDownPresentationConnection();
 }
 
 void CastSessionClientImpl::TerminateConnection() {
-  if (connection_) {
-    connection_->DidChangeState(PresentationConnectionState::TERMINATED);
-  }
+  if (connection_remote_)
+    connection_remote_->DidChangeState(PresentationConnectionState::TERMINATED);
 
   TearDownPresentationConnection();
 }
 
 void CastSessionClientImpl::TearDownPresentationConnection() {
-  connection_.reset();
-  connection_binding_.Close();
+  connection_remote_.reset();
+  connection_receiver_.reset();
 }
 
 }  // namespace media_router
diff --git a/chrome/browser/media/router/providers/cast/cast_session_client.h b/chrome/browser/media/router/providers/cast/cast_session_client.h
index 0f004e2e..273ee89d 100644
--- a/chrome/browser/media/router/providers/cast/cast_session_client.h
+++ b/chrome/browser/media/router/providers/cast/cast_session_client.h
@@ -17,7 +17,8 @@
 #include "chrome/common/media_router/mojom/media_router.mojom.h"
 #include "chrome/common/media_router/providers/cast/cast_media_source.h"
 #include "components/cast_channel/cast_message_handler.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom.h"
 #include "url/origin.h"
 
@@ -178,13 +179,14 @@
   // sequence numbers can be used directly without generating request IDs.
   base::flat_map<int, int> pending_media_requests_;
 
-  // Binding for the PresentationConnection in Blink to receive incoming
+  // Receiver for the PresentationConnection in Blink to receive incoming
   // messages and respond to state changes.
-  mojo::Binding<blink::mojom::PresentationConnection> connection_binding_;
+  mojo::Receiver<blink::mojom::PresentationConnection> connection_receiver_{
+      this};
 
   // Mojo message pipe to PresentationConnection in Blink to send messages and
   // initiate state changes.
-  blink::mojom::PresentationConnectionPtr connection_;
+  mojo::Remote<blink::mojom::PresentationConnection> connection_remote_;
 
   base::WeakPtrFactory<CastSessionClientImpl> weak_ptr_factory_{this};
 };
diff --git a/chrome/browser/media/router/providers/cast/cast_session_client_unittest.cc b/chrome/browser/media/router/providers/cast/cast_session_client_unittest.cc
index 9a3a258..4ee7ca9 100644
--- a/chrome/browser/media/router/providers/cast/cast_session_client_unittest.cc
+++ b/chrome/browser/media/router/providers/cast/cast_session_client_unittest.cc
@@ -27,6 +27,7 @@
 #include "components/cast_channel/cast_test_util.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/test/browser_task_environment.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "services/data_decoder/public/cpp/testing_json_parser.h"
 #include "services/service_manager/public/cpp/test/test_connector_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -52,7 +53,8 @@
  public:
   explicit MockPresentationConnection(
       mojom::RoutePresentationConnectionPtr connections)
-      : binding_(this, std::move(connections->connection_request)) {}
+      : connection_receiver_(this,
+                             std::move(connections->connection_receiver)) {}
 
   ~MockPresentationConnection() override = default;
 
@@ -63,7 +65,7 @@
 
   // NOTE: This member doesn't look like it's used for anything, but it needs to
   // exist in order for Mojo magic to work correctly.
-  mojo::Binding<blink::mojom::PresentationConnection> binding_;
+  mojo::Receiver<blink::mojom::PresentationConnection> connection_receiver_;
 
   DISALLOW_COPY_AND_ASSIGN(MockPresentationConnection);
 };
diff --git a/chrome/browser/optimization_guide/optimization_guide_web_contents_observer.cc b/chrome/browser/optimization_guide/optimization_guide_web_contents_observer.cc
index 30acaf2..67d029b 100644
--- a/chrome/browser/optimization_guide/optimization_guide_web_contents_observer.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_web_contents_observer.cc
@@ -7,7 +7,6 @@
 #include "base/base64.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
-#include "chrome/browser/optimization_guide/optimization_guide_navigation_data.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/optimization_guide/hints_fetcher.h"
 #include "components/optimization_guide/optimization_guide_features.h"
diff --git a/chrome/browser/optimization_guide/optimization_guide_web_contents_observer.h b/chrome/browser/optimization_guide/optimization_guide_web_contents_observer.h
index 03b8257..4589636 100644
--- a/chrome/browser/optimization_guide/optimization_guide_web_contents_observer.h
+++ b/chrome/browser/optimization_guide/optimization_guide_web_contents_observer.h
@@ -11,6 +11,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/optimization_guide/optimization_guide_navigation_data.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 
@@ -19,7 +20,6 @@
 }  // namespace content
 
 class OptimizationGuideKeyedService;
-class OptimizationGuideNavigationData;
 
 // Observes navigation events.
 class OptimizationGuideWebContentsObserver
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
index 5bebc2c..6bb5c1c 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
@@ -816,6 +816,11 @@
   observer_ = nullptr;
 }
 
+const PageLoadMetricsObserverDelegate&
+MetricsWebContentsObserver::TestingObserver::GetDelegateForCommittedLoad() {
+  return observer_->GetDelegateForCommittedLoad();
+}
+
 void MetricsWebContentsObserver::BroadcastEventToObservers(
     const void* const event_key) {
   if (committed_load_)
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
index b483050..57ee3107d10c 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
@@ -64,6 +64,10 @@
     // fine.
     virtual void OnCommit(PageLoadTracker* tracker) {}
 
+    // Returns the observer delegate for the committed load associated with
+    // the MetricsWebContentsObserver.
+    const PageLoadMetricsObserverDelegate& GetDelegateForCommittedLoad();
+
    private:
     page_load_metrics::MetricsWebContentsObserver* observer_;
 
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
index 843669f..3b940e32 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
@@ -82,8 +82,7 @@
   }
 
   void OnTimingUpdate(content::RenderFrameHost* subframe_rfh,
-                      const mojom::PageLoadTiming& timing,
-                      const PageLoadExtraInfo& extra_info) override {
+                      const mojom::PageLoadTiming& timing) override {
     if (subframe_rfh) {
       DCHECK(subframe_rfh->GetParent());
       updated_subframe_timings_->push_back(timing.Clone());
@@ -97,14 +96,12 @@
     updated_cpu_timings_->push_back(timing.Clone());
   }
 
-  void OnComplete(const mojom::PageLoadTiming& timing,
-                  const PageLoadExtraInfo& extra_info) override {
+  void OnComplete(const mojom::PageLoadTiming& timing) override {
     complete_timings_->push_back(timing.Clone());
   }
 
   ObservePolicy FlushMetricsOnAppEnterBackground(
-      const mojom::PageLoadTiming& timing,
-      const PageLoadExtraInfo& extra_info) override {
+      const mojom::PageLoadTiming& timing) override {
     return STOP_OBSERVING;
   }
 
@@ -113,9 +110,9 @@
     loaded_resources_->emplace_back(extra_request_complete_info);
   }
 
-  void OnFeaturesUsageObserved(content::RenderFrameHost* rfh,
-                               const mojom::PageLoadFeatures& features,
-                               const PageLoadExtraInfo& extra_info) override {
+  void OnFeaturesUsageObserved(
+      content::RenderFrameHost* rfh,
+      const mojom::PageLoadFeatures& features) override {
     observed_features_->push_back(features);
   }
 
@@ -158,9 +155,8 @@
     return should_ignore ? STOP_OBSERVING : CONTINUE_OBSERVING;
   }
 
-  void OnComplete(const mojom::PageLoadTiming& timing,
-                  const PageLoadExtraInfo& extra_info) override {
-    completed_filtered_urls_->push_back(extra_info.url);
+  void OnComplete(const mojom::PageLoadTiming& timing) override {
+    completed_filtered_urls_->push_back(GetDelegate().GetUrl());
   }
 
  private:
diff --git a/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.cc
index 5f097ec..ddb3a96 100644
--- a/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.cc
@@ -261,8 +261,7 @@
 AbortsPageLoadMetricsObserver::AbortsPageLoadMetricsObserver() {}
 
 void AbortsPageLoadMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   page_load_metrics::PageAbortInfo abort_info = GetPageAbortInfo(GetDelegate());
   if (!ShouldTrackMetrics(GetDelegate(), abort_info))
     return;
@@ -290,8 +289,7 @@
 }
 
 void AbortsPageLoadMetricsObserver::OnFailedProvisionalLoad(
-    const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info) {
   page_load_metrics::PageAbortInfo abort_info = GetPageAbortInfo(GetDelegate());
   if (!ShouldTrackMetrics(GetDelegate(), abort_info))
     return;
diff --git a/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.h
index 46a2cd9..376be6b1 100644
--- a/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.h
@@ -33,11 +33,10 @@
 
   // page_load_metrics::PageLoadMetricsObserver:
   void OnComplete(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFailedProvisionalLoad(
-      const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info)
+      override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(AbortsPageLoadMetricsObserver);
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
index ca8ef9e..b89d3ec 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
@@ -145,8 +145,7 @@
 
 void AdsPageLoadMetricsObserver::OnTimingUpdate(
     content::RenderFrameHost* subframe_rfh,
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!subframe_rfh)
     return;
 
@@ -306,8 +305,7 @@
 // ad, even if it navigates to a non-ad page. This function labels all of a
 // page's frames, even those that fail to commit.
 void AdsPageLoadMetricsObserver::OnDidFinishSubFrameNavigation(
-    content::NavigationHandle* navigation_handle,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    content::NavigationHandle* navigation_handle) {
   // If the AdsPageLoadMetricsObserver is created, this does not return nullptr.
   auto* client = ChromeSubresourceFilterClient::FromWebContents(
       navigation_handle->GetWebContents());
@@ -340,27 +338,25 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 AdsPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   // The browser may come back, but there is no guarantee. To be safe, record
   // what we have now and ignore future changes to this navigation.
-  if (extra_info.did_commit) {
+  if (GetDelegate().DidCommit()) {
     if (timing.response_start) {
       time_commit_ =
           GetDelegate().GetNavigationStart() + *timing.response_start;
     }
-    RecordHistograms(extra_info.source_id);
+    RecordHistograms(GetDelegate().GetSourceId());
   }
 
   return STOP_OBSERVING;
 }
 
 void AdsPageLoadMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  if (info.did_commit && timing.response_start)
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  if (GetDelegate().DidCommit() && timing.response_start)
     time_commit_ = GetDelegate().GetNavigationStart() + *timing.response_start;
-  RecordHistograms(info.source_id);
+  RecordHistograms(GetDelegate().GetSourceId());
 }
 
 void AdsPageLoadMetricsObserver::OnResourceDataUseObserved(
@@ -374,8 +370,7 @@
 }
 
 void AdsPageLoadMetricsObserver::OnPageInteractive(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (timing.interactive_timing->interactive) {
     time_interactive_ = GetDelegate().GetNavigationStart() +
                         *timing.interactive_timing->interactive;
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
index 8f9a77ef..21130d4 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
@@ -69,8 +69,7 @@
                          ukm::SourceId source_id) override;
   void OnTimingUpdate(
       content::RenderFrameHost* subframe_rfh,
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnCpuTimingUpdate(
       content::RenderFrameHost* subframe_rfh,
       const page_load_metrics::mojom::CpuTiming& timing) override;
@@ -81,20 +80,17 @@
   void ReadyToCommitNextNavigation(
       content::NavigationHandle* navigation_handle) override;
   void OnDidFinishSubFrameNavigation(
-      content::NavigationHandle* navigation_handle,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      content::NavigationHandle* navigation_handle) override;
   ObservePolicy FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnComplete(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnResourceDataUseObserved(
       content::RenderFrameHost* rfh,
       const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
           resources) override;
   void OnPageInteractive(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void FrameReceivedFirstUserActivation(content::RenderFrameHost* rfh) override;
   void FrameDisplayStateChanged(content::RenderFrameHost* render_frame_host,
                                 bool is_display_none) override;
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
index c7c44f7..fa8eadb 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
@@ -303,13 +303,10 @@
   void OnMainFrameInteractive(base::TimeDelta frame_interactive_offset) {
     auto timing = page_load_metrics::mojom::PageLoadTimingPtr(base::in_place);
     page_load_metrics::InitPageLoadTimingForTest(timing.get());
-    auto extra_info = page_load_metrics::PageLoadExtraInfo::CreateForTesting(
-        web_contents()->GetLastCommittedURL(), true);
-
     timing->interactive_timing->interactive =
         base::Optional<base::TimeDelta>(frame_interactive_offset);
     // Call directly since main frame timing updates may be delayed.
-    ads_observer_->OnPageInteractive(*timing, extra_info);
+    ads_observer_->OnPageInteractive(*timing);
   }
 
   void OnCpuTimingUpdate(RenderFrameHost* render_frame_host,
diff --git a/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.cc
index 2ba6a7ea..95cde4d 100644
--- a/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.cc
@@ -124,8 +124,7 @@
 }
 
 void AMPPageLoadMetricsObserver::OnDidFinishSubFrameNavigation(
-    content::NavigationHandle* navigation_handle,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    content::NavigationHandle* navigation_handle) {
   if (!navigation_handle->HasCommitted())
     return;
 
@@ -168,8 +167,7 @@
 
 void AMPPageLoadMetricsObserver::OnTimingUpdate(
     content::RenderFrameHost* subframe_rfh,
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (subframe_rfh == nullptr)
     return;
 
@@ -182,8 +180,7 @@
 
 void AMPPageLoadMetricsObserver::OnSubFrameRenderDataUpdate(
     content::RenderFrameHost* subframe_rfh,
-    const page_load_metrics::mojom::FrameRenderDataUpdate& render_data,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::FrameRenderDataUpdate& render_data) {
   if (subframe_rfh == nullptr)
     return;
 
@@ -197,8 +194,7 @@
 }
 
 void AMPPageLoadMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   MaybeRecordAmpDocumentMetrics();
   current_main_frame_nav_info_ = nullptr;
 }
@@ -227,9 +223,8 @@
 
 void AMPPageLoadMetricsObserver::OnLoadingBehaviorObserved(
     content::RenderFrameHost* subframe_rfh,
-    int behavior_flags,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  RecordLoadingBehaviorObserved(info);
+    int behavior_flags) {
+  RecordLoadingBehaviorObserved();
 
   if (subframe_rfh == nullptr)
     return;
@@ -259,12 +254,11 @@
   }
 }
 
-void AMPPageLoadMetricsObserver::RecordLoadingBehaviorObserved(
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  ukm::builders::AmpPageLoad builder(info.source_id);
+void AMPPageLoadMetricsObserver::RecordLoadingBehaviorObserved() {
+  ukm::builders::AmpPageLoad builder(GetDelegate().GetSourceId());
   bool should_record = false;
   if (!observed_amp_main_frame_ &&
-      (info.main_frame_metadata.behavior_flags &
+      (GetDelegate().GetMainFrameMetadata().behavior_flags &
        blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded) !=
           0) {
     builder.SetMainFrameAmpPageLoad(true);
@@ -273,7 +267,7 @@
   }
 
   if (!observed_amp_sub_frame_ &&
-      (info.subframe_metadata.behavior_flags &
+      (GetDelegate().GetSubframeMetadata().behavior_flags &
        blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded) !=
           0) {
     builder.SetSubFrameAmpPageLoad(true);
diff --git a/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.h
index 9acd51b..4f7f467 100644
--- a/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.h
@@ -53,23 +53,19 @@
   void OnCommitSameDocumentNavigation(
       content::NavigationHandle* navigation_handle) override;
   void OnDidFinishSubFrameNavigation(
-      content::NavigationHandle* navigation_handle,
-      const page_load_metrics::PageLoadExtraInfo&) override;
+      content::NavigationHandle* navigation_handle) override;
   void OnFrameDeleted(content::RenderFrameHost* rfh) override;
   void OnTimingUpdate(
       content::RenderFrameHost* subframe_rfh,
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnSubFrameRenderDataUpdate(
       content::RenderFrameHost* subframe_rfh,
-      const page_load_metrics::mojom::FrameRenderDataUpdate& render_data,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
-  void OnLoadingBehaviorObserved(
-      content::RenderFrameHost* subframe_rfh,
-      int behavior_flags,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::FrameRenderDataUpdate& render_data)
+      override;
+  void OnComplete(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnLoadingBehaviorObserved(content::RenderFrameHost* subframe_rfh,
+                                 int behavior_flags) override;
 
  private:
   // Information about an AMP navigation in the main frame. Both regular and
@@ -113,8 +109,7 @@
     bool amp_document_loaded = false;
   };
 
-  void RecordLoadingBehaviorObserved(
-      const page_load_metrics::PageLoadExtraInfo& info);
+  void RecordLoadingBehaviorObserved();
 
   void ProcessMainFrameNavigation(content::NavigationHandle* navigation_handle);
   void MaybeRecordAmpDocumentMetrics();
diff --git a/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.cc
index 0c3abd7..77993b2 100644
--- a/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.cc
@@ -41,35 +41,32 @@
 }
 
 void AndroidPageLoadMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   int64_t first_contentful_paint_ms =
       timing.paint_timing->first_contentful_paint->InMilliseconds();
   ReportFirstContentfulPaint(
-      (extra_info.navigation_start - base::TimeTicks()).InMicroseconds(),
+      (GetDelegate().GetNavigationStart() - base::TimeTicks()).InMicroseconds(),
       first_contentful_paint_ms);
 }
 
 void AndroidPageLoadMetricsObserver::OnFirstMeaningfulPaintInMainFrameDocument(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   int64_t first_meaningful_paint_ms =
       timing.paint_timing->first_meaningful_paint->InMilliseconds();
   ReportFirstMeaningfulPaint(
-      (extra_info.navigation_start - base::TimeTicks()).InMicroseconds(),
+      (GetDelegate().GetNavigationStart() - base::TimeTicks()).InMicroseconds(),
       first_meaningful_paint_ms);
 }
 
 void AndroidPageLoadMetricsObserver::OnLoadEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   int64_t load_event_start_ms =
       timing.document_timing->load_event_start->InMilliseconds();
   ReportLoadEventStart(
-      (info.navigation_start - base::TimeTicks()).InMicroseconds(),
+      (GetDelegate().GetNavigationStart() - base::TimeTicks()).InMicroseconds(),
       load_event_start_ms);
 }
 
diff --git a/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.h
index b936d8c..289e9f0 100644
--- a/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.h
@@ -27,14 +27,11 @@
                         const GURL& currently_committed_url,
                         bool started_in_foreground) override;
   void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstMeaningfulPaintInMainFrameDocument(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnLoadEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnLoadedResource(const page_load_metrics::ExtraRequestCompleteInfo&
                             extra_request_complete_info) override;
 
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
index 2182573..349103b 100644
--- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
@@ -315,8 +315,7 @@
 }
 
 void CorePageLoadMetricsObserver::OnDomContentLoadedEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.document_timing->dom_content_loaded_event_start,
           GetDelegate())) {
@@ -331,8 +330,7 @@
 }
 
 void CorePageLoadMetricsObserver::OnLoadEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.document_timing->load_event_start, GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramLoad,
@@ -344,8 +342,7 @@
 }
 
 void CorePageLoadMetricsObserver::OnFirstLayout(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.document_timing->first_layout, GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstLayout,
@@ -357,10 +354,9 @@
 }
 
 void CorePageLoadMetricsObserver::OnFirstPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  first_paint_ =
-      info.navigation_start + timing.paint_timing->first_paint.value();
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  first_paint_ = GetDelegate().GetNavigationStart() +
+                 timing.paint_timing->first_paint.value();
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_paint, GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstPaint,
@@ -384,13 +380,12 @@
           timing.paint_timing->first_paint, GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramForegroundToFirstPaint,
                         timing.paint_timing->first_paint.value() -
-                            info.first_foreground_time.value());
+                            GetDelegate().GetFirstForegroundTime().value());
   }
 }
 
 void CorePageLoadMetricsObserver::OnFirstImagePaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_image_paint, GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstImagePaint,
@@ -402,8 +397,7 @@
 }
 
 void CorePageLoadMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_contentful_paint, GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstContentfulPaint,
@@ -413,7 +407,7 @@
                             timing.parse_timing->parse_start.value());
     UMA_HISTOGRAM_ENUMERATION(
         internal::kHistogramFirstContentfulPaintInitiatingProcess,
-        info.user_initiated_info.browser_initiated
+        GetDelegate().GetUserInitiatedInfo().browser_initiated
             ? content::PROCESS_TYPE_BROWSER
             : content::PROCESS_TYPE_RENDERER,
         content::PROCESS_TYPE_CONTENT_END);
@@ -425,8 +419,8 @@
 
     // TODO(bmcquade): consider adding a histogram that uses
     // UserInputInfo.user_input_event.
-    if (info.user_initiated_info.browser_initiated ||
-        info.user_initiated_info.user_gesture) {
+    if (GetDelegate().GetUserInitiatedInfo().browser_initiated ||
+        GetDelegate().GetUserInitiatedInfo().user_gesture) {
       PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstContentfulPaintUserInitiated,
                           timing.paint_timing->first_contentful_paint.value());
     }
@@ -458,8 +452,8 @@
             timing.paint_timing->first_contentful_paint.value());
         // TODO(bmcquade): consider adding a histogram that uses
         // UserInputInfo.user_input_event.
-        if (info.user_initiated_info.browser_initiated ||
-            info.user_initiated_info.user_gesture) {
+        if (GetDelegate().GetUserInitiatedInfo().browser_initiated ||
+            GetDelegate().GetUserInitiatedInfo().user_gesture) {
           PAGE_LOAD_HISTOGRAM(
               internal::kHistogramLoadTypeFirstContentfulPaintReloadByGesture,
               timing.paint_timing->first_contentful_paint.value());
@@ -506,13 +500,12 @@
           timing.paint_timing->first_contentful_paint, GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramForegroundToFirstContentfulPaint,
                         timing.paint_timing->first_contentful_paint.value() -
-                            info.first_foreground_time.value());
+                            GetDelegate().GetFirstForegroundTime().value());
   }
 }
 
 void CorePageLoadMetricsObserver::OnFirstMeaningfulPaintInMainFrameDocument(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_meaningful_paint, GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstMeaningfulPaint,
@@ -530,13 +523,12 @@
           timing.paint_timing->first_meaningful_paint, GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramForegroundToFirstMeaningfulPaint,
                         timing.paint_timing->first_meaningful_paint.value() -
-                            info.first_foreground_time.value());
+                            GetDelegate().GetFirstForegroundTime().value());
   }
 }
 
 void CorePageLoadMetricsObserver::OnPageInteractive(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   // Both interactive and interactive detection time must be present.
   DCHECK(timing.interactive_timing->interactive);
   DCHECK(timing.interactive_timing->interactive_detection);
@@ -570,8 +562,7 @@
 }
 
 void CorePageLoadMetricsObserver::OnFirstInputInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.interactive_timing->first_input_timestamp, GetDelegate())) {
     return;
@@ -618,8 +609,7 @@
 }
 
 void CorePageLoadMetricsObserver::OnParseStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.parse_timing->parse_start, GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramParseStart,
@@ -654,8 +644,7 @@
 }
 
 void CorePageLoadMetricsObserver::OnParseStop(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   base::TimeDelta parse_duration = timing.parse_timing->parse_stop.value() -
                                    timing.parse_timing->parse_start.value();
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
@@ -693,32 +682,29 @@
 }
 
 void CorePageLoadMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  RecordTimingHistograms(timing, info);
-  RecordByteAndResourceHistograms(timing, info);
-  RecordForegroundDurationHistograms(timing, info, base::TimeTicks());
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  RecordTimingHistograms(timing);
+  RecordByteAndResourceHistograms(timing);
+  RecordForegroundDurationHistograms(timing, base::TimeTicks());
 }
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 CorePageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   // FlushMetricsOnAppEnterBackground is invoked on Android in cases where the
   // app is about to be backgrounded, as part of the Activity.onPause()
   // flow. After this method is invoked, Chrome may be killed without further
   // notification, so we record final metrics collected up to this point.
-  if (info.did_commit) {
-    RecordTimingHistograms(timing, info);
-    RecordByteAndResourceHistograms(timing, info);
+  if (GetDelegate().DidCommit()) {
+    RecordTimingHistograms(timing);
+    RecordByteAndResourceHistograms(timing);
   }
-  RecordForegroundDurationHistograms(timing, info, base::TimeTicks::Now());
+  RecordForegroundDurationHistograms(timing, base::TimeTicks::Now());
   return STOP_OBSERVING;
 }
 
 void CorePageLoadMetricsObserver::OnFailedProvisionalLoad(
-    const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info) {
   // Only handle actual failures; provisional loads that failed due to another
   // committed load or due to user action are recorded in
   // AbortsPageLoadMetricsObserver.
@@ -733,13 +719,12 @@
   // Provide an empty PageLoadTiming, since we don't have any timing metrics
   // for failed provisional loads.
   RecordForegroundDurationHistograms(page_load_metrics::mojom::PageLoadTiming(),
-                                     extra_info, base::TimeTicks());
+                                     base::TimeTicks());
 }
 
 void CorePageLoadMetricsObserver::OnUserInput(
     const blink::WebInputEvent& event,
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   base::TimeTicks now;
 
   if (first_paint_.is_null())
@@ -790,13 +775,13 @@
 // collected yet. This is meant to be called at the end of a page lifetime, for
 // example, when the user is navigating away from the page.
 void CorePageLoadMetricsObserver::RecordTimingHistograms(
-    const page_load_metrics::mojom::PageLoadTiming& main_frame_timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& main_frame_timing) {
   // Log time to first foreground / time to first background. Log counts that we
   // started a relevant page load in the foreground / background.
-  if (!info.started_in_foreground && info.first_foreground_time) {
+  if (!GetDelegate().StartedInForeground() &&
+      GetDelegate().GetFirstForegroundTime()) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstForeground,
-                        info.first_foreground_time.value());
+                        GetDelegate().GetFirstForegroundTime().value());
   }
 
   const page_load_metrics::ContentfulPaintTimingInfo&
@@ -878,7 +863,6 @@
 
 void CorePageLoadMetricsObserver::RecordForegroundDurationHistograms(
     const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info,
     base::TimeTicks app_background_time) {
   base::Optional<base::TimeDelta> foreground_duration =
       page_load_metrics::GetInitialForegroundDuration(GetDelegate(),
@@ -886,7 +870,7 @@
   if (!foreground_duration)
     return;
 
-  if (info.did_commit) {
+  if (GetDelegate().DidCommit()) {
     PAGE_LOAD_LONG_HISTOGRAM(internal::kHistogramPageTimingForegroundDuration,
                              foreground_duration.value());
     if (timing.paint_timing->first_paint &&
@@ -909,18 +893,17 @@
         foreground_duration.value());
   }
 
-  if (info.page_end_reason == page_load_metrics::END_FORWARD_BACK &&
-      info.user_initiated_info.user_gesture &&
-      !info.user_initiated_info.browser_initiated &&
-      info.page_end_time <= foreground_duration) {
+  if (GetDelegate().GetPageEndReason() == page_load_metrics::END_FORWARD_BACK &&
+      GetDelegate().GetUserInitiatedInfo().user_gesture &&
+      !GetDelegate().GetUserInitiatedInfo().browser_initiated &&
+      GetDelegate().GetPageEndTime() <= foreground_duration) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramUserGestureNavigationToForwardBack,
-                        info.page_end_time.value());
+                        GetDelegate().GetPageEndTime().value());
   }
 }
 
 void CorePageLoadMetricsObserver::RecordByteAndResourceHistograms(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_GE(network_bytes_, 0);
   DCHECK_GE(cache_bytes_, 0);
   int64_t total_bytes = network_bytes_ + cache_bytes_;
@@ -980,15 +963,13 @@
 
 void CorePageLoadMetricsObserver::OnTimingUpdate(
     content::RenderFrameHost* subframe_rfh,
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   largest_contentful_paint_handler_.RecordTiming(timing.paint_timing,
                                                  subframe_rfh);
 }
 
 void CorePageLoadMetricsObserver::OnDidFinishSubFrameNavigation(
-    content::NavigationHandle* navigation_handle,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    content::NavigationHandle* navigation_handle) {
   largest_contentful_paint_handler_.OnDidFinishSubFrameNavigation(
-      navigation_handle, extra_info);
+      navigation_handle, GetDelegate());
 }
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h
index 1ac268a5..02e94e12 100644
--- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h
@@ -177,50 +177,37 @@
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
   void OnDomContentLoadedEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnLoadEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstLayout(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstImagePaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstMeaningfulPaintInMainFrameDocument(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnPageInteractive(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstInputInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnParseStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnParseStop(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnComplete(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFailedProvisionalLoad(
-      const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info)
+      override;
   ObservePolicy FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnUserInput(
       const blink::WebInputEvent& event,
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnResourceDataUseObserved(
       content::RenderFrameHost* rfh,
       const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
@@ -228,23 +215,18 @@
 
   void OnTimingUpdate(
       content::RenderFrameHost* subframe_rfh,
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
   void OnDidFinishSubFrameNavigation(
-      content::NavigationHandle* navigation_handle,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      content::NavigationHandle* navigation_handle) override;
 
  private:
   void RecordTimingHistograms(
-      const page_load_metrics::mojom::PageLoadTiming& main_frame_timing,
-      const page_load_metrics::PageLoadExtraInfo& info);
+      const page_load_metrics::mojom::PageLoadTiming& main_frame_timing);
   void RecordByteAndResourceHistograms(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info);
+      const page_load_metrics::mojom::PageLoadTiming& timing);
   void RecordForegroundDurationHistograms(
       const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info,
       base::TimeTicks app_background_time);
 
   ui::PageTransition transition_;
diff --git a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer.cc
index 1df4e86..dd92e78 100644
--- a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer.cc
@@ -89,24 +89,22 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 DataReductionProxyMetricsObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // FlushMetricsOnAppEnterBackground is invoked on Android in cases where the
   // app is about to be backgrounded, as part of the Activity.onPause()
   // flow. After this method is invoked, Chrome may be killed without further
   // notification, so we send a pingback with data collected up to this point.
-  if (info.did_commit) {
+  if (GetDelegate().DidCommit()) {
     RecordPageSizeUMA();
   }
   return DataReductionProxyMetricsObserverBase::
-      FlushMetricsOnAppEnterBackground(timing, info);
+      FlushMetricsOnAppEnterBackground(timing);
 }
 
 void DataReductionProxyMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  DataReductionProxyMetricsObserverBase::OnComplete(timing, info);
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  DataReductionProxyMetricsObserverBase::OnComplete(timing);
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   RecordPageSizeUMA();
 }
@@ -198,8 +196,7 @@
 }
 
 void DataReductionProxyMetricsObserver::OnDomContentLoadedEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   RECORD_FOREGROUND_HISTOGRAMS_FOR_SUFFIX(
       GetDelegate(), data(),
@@ -208,18 +205,16 @@
 }
 
 void DataReductionProxyMetricsObserver::OnLoadEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DataReductionProxyMetricsObserverBase::OnLoadEventStart(timing, info);
+  DataReductionProxyMetricsObserverBase::OnLoadEventStart(timing);
   RECORD_FOREGROUND_HISTOGRAMS_FOR_SUFFIX(
       GetDelegate(), data(), timing.document_timing->load_event_start,
       ::internal::kHistogramLoadEventFiredSuffix);
 }
 
 void DataReductionProxyMetricsObserver::OnFirstLayout(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   RECORD_FOREGROUND_HISTOGRAMS_FOR_SUFFIX(
       GetDelegate(), data(), timing.document_timing->first_layout,
@@ -227,8 +222,7 @@
 }
 
 void DataReductionProxyMetricsObserver::OnFirstPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   RECORD_FOREGROUND_HISTOGRAMS_FOR_SUFFIX(
       GetDelegate(), data(), timing.paint_timing->first_paint,
@@ -236,8 +230,7 @@
 }
 
 void DataReductionProxyMetricsObserver::OnFirstImagePaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   RECORD_FOREGROUND_HISTOGRAMS_FOR_SUFFIX(
       GetDelegate(), data(), timing.paint_timing->first_image_paint,
@@ -245,8 +238,7 @@
 }
 
 void DataReductionProxyMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   RECORD_FOREGROUND_HISTOGRAMS_FOR_SUFFIX(
       GetDelegate(), data(), timing.paint_timing->first_contentful_paint,
@@ -255,8 +247,7 @@
 
 void DataReductionProxyMetricsObserver::
     OnFirstMeaningfulPaintInMainFrameDocument(
-        const page_load_metrics::mojom::PageLoadTiming& timing,
-        const page_load_metrics::PageLoadExtraInfo& info) {
+        const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   RECORD_FOREGROUND_HISTOGRAMS_FOR_SUFFIX(
       GetDelegate(), data(), timing.paint_timing->first_meaningful_paint,
@@ -264,8 +255,7 @@
 }
 
 void DataReductionProxyMetricsObserver::OnParseStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   RECORD_FOREGROUND_HISTOGRAMS_FOR_SUFFIX(
       GetDelegate(), data(), timing.parse_timing->parse_start,
@@ -273,8 +263,7 @@
 }
 
 void DataReductionProxyMetricsObserver::OnParseStop(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.parse_timing->parse_stop, GetDelegate()))
diff --git a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer.h b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer.h
index fcefa8e5..3ae1f24 100644
--- a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer.h
@@ -44,34 +44,27 @@
 
   // page_load_metrics::PageLoadMetricsObserver:
   ObservePolicy FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnComplete(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnDomContentLoadedEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnLoadEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
-  void OnFirstLayout(const page_load_metrics::mojom::PageLoadTiming& timing,
-                     const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnFirstLayout(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstImagePaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstMeaningfulPaintInMainFrameDocument(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
-  void OnParseStart(const page_load_metrics::mojom::PageLoadTiming& timing,
-                    const page_load_metrics::PageLoadExtraInfo& info) override;
-  void OnParseStop(const page_load_metrics::mojom::PageLoadTiming& timing,
-                   const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnParseStart(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnParseStop(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
  private:
   // Records UMA of page size when the observer is about to be deleted.
diff --git a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.cc b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.cc
index 4c1877bc..54b4f79 100644
--- a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.cc
+++ b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.cc
@@ -204,26 +204,24 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 DataReductionProxyMetricsObserverBase::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // FlushMetricsOnAppEnterBackground is invoked on Android in cases where the
   // app is about to be backgrounded, as part of the Activity.onPause()
   // flow. After this method is invoked, Chrome may be killed without further
   // notification, so we send a pingback with data collected up to this point.
-  if (info.did_commit) {
-    SendPingback(timing, info, true /* app_background_occurred */);
-    RecordUKM(info);
+  if (GetDelegate().DidCommit()) {
+    SendPingback(timing, true /* app_background_occurred */);
+    RecordUKM();
   }
   return STOP_OBSERVING;
 }
 
 void DataReductionProxyMetricsObserverBase::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  SendPingback(timing, info, false /* app_background_occurred */);
-  RecordUKM(info);
+  SendPingback(timing, false /* app_background_occurred */);
+  RecordUKM();
 }
 
 // static
@@ -236,8 +234,7 @@
   return ukm::GetExponentialBucketMin(bytes, 1.16);
 }
 
-void DataReductionProxyMetricsObserverBase::RecordUKM(
-    const page_load_metrics::PageLoadExtraInfo& info) const {
+void DataReductionProxyMetricsObserverBase::RecordUKM() const {
   if (!data())
     return;
 
@@ -248,7 +245,7 @@
       insecure_original_network_bytes() + secure_original_network_bytes();
   uint64_t uuid = ComputeDataReductionProxyUUID(data());
 
-  ukm::builders::DataReductionProxy builder(info.source_id);
+  ukm::builders::DataReductionProxy builder(GetDelegate().GetSourceId());
 
   builder.SetEstimatedOriginalNetworkBytes(
       ExponentiallyBucketBytes(original_network_bytes));
@@ -259,7 +256,6 @@
 
 void DataReductionProxyMetricsObserverBase::SendPingback(
     const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info,
     bool app_background_occurred) {
   // TODO(ryansturm): Move to OnFirstBackgroundEvent to handle some fast
   // shutdown cases. crbug.com/618072
@@ -317,19 +313,20 @@
     main_frame_fetch_start =
         main_frame_fetch_start_.value() - GetDelegate().GetNavigationStart();
   }
-  if (info.started_in_foreground && info.page_end_time.has_value()) {
+  if (GetDelegate().StartedInForeground() &&
+      GetDelegate().GetPageEndTime().has_value()) {
     // This should be reported even when the app goes into the background which
     // is excluded in
     // |page_load_metrics::WasStartedInForegroundOptionalEventInForeground|.
-    page_end_time = info.page_end_time;
-  } else if (info.started_in_foreground) {
-    page_end_time = base::TimeTicks::Now() - info.navigation_start;
+    page_end_time = GetDelegate().GetPageEndTime();
+  } else if (GetDelegate().StartedInForeground()) {
+    page_end_time = base::TimeTicks::Now() - GetDelegate().GetNavigationStart();
   }
 
   // If a crash happens, report the host |render_process_host_id_| to the
   // pingback client. Otherwise report kInvalidUniqueID.
   int host_id = content::ChildProcessHost::kInvalidUniqueID;
-  if (info.page_end_reason ==
+  if (GetDelegate().GetPageEndReason() ==
       page_load_metrics::PageEndReason::END_RENDER_PROCESS_GONE) {
     host_id = render_process_host_id_;
   }
@@ -370,14 +367,13 @@
       main_frame_fetch_start, network_bytes, original_network_bytes,
       total_page_size_bytes, cached_fraction, app_background_occurred,
       opted_out_, renderer_memory_usage_kb_, host_id,
-      ConvertPLMPageEndReasonToProto(info.page_end_reason), touch_count_,
-      scroll_count_, redirect_count_);
+      ConvertPLMPageEndReasonToProto(GetDelegate().GetPageEndReason()),
+      touch_count_, scroll_count_, redirect_count_);
   GetPingbackClient()->SendPingback(*data_, data_reduction_proxy_timing);
 }
 
 void DataReductionProxyMetricsObserverBase::OnLoadEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (process_id_ != base::kNullProcessId) {
     auto callback = base::BindRepeating(
@@ -458,8 +454,7 @@
 
 void DataReductionProxyMetricsObserverBase::OnUserInput(
     const blink::WebInputEvent& event,
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (event.GetType() == blink::WebInputEvent::kMouseDown ||
       event.GetType() == blink::WebInputEvent::kGestureTap) {
     touch_count_++;
diff --git a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.h b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.h
index 3fd441ce..b63fb250 100644
--- a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.h
+++ b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.h
@@ -42,13 +42,11 @@
   ObservePolicy OnRedirect(
       content::NavigationHandle* navigation_handle) override;
   ObservePolicy FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnComplete(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnLoadEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnLoadedResource(const page_load_metrics::ExtraRequestCompleteInfo&
                             extra_request_compelte_info) override;
   void OnResourceDataUseObserved(
@@ -58,8 +56,7 @@
   void OnEventOccurred(const void* const event_key) override;
   void OnUserInput(
       const blink::WebInputEvent& event,
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
   // Exponentially bucket the number of bytes for privacy-implicated resources.
   // Input below 10KB returns 0.
@@ -105,11 +102,10 @@
                          ukm::SourceId source_id) final;
 
   // Records UKM for the data_reduction_proxy event.
-  void RecordUKM(const page_load_metrics::PageLoadExtraInfo& info) const;
+  void RecordUKM() const;
 
   // Sends the page load information to the pingback client.
   void SendPingback(const page_load_metrics::mojom::PageLoadTiming& timing,
-                    const page_load_metrics::PageLoadExtraInfo& info,
                     bool app_background_occurred);
 
   // Gets the default DataReductionProxyPingbackClient. Overridden in testing.
diff --git a/chrome/browser/page_load_metrics/observers/document_write_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/document_write_page_load_metrics_observer.cc
index 1a66fd71..c78b0da 100644
--- a/chrome/browser/page_load_metrics/observers/document_write_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/document_write_page_load_metrics_observer.cc
@@ -48,9 +48,8 @@
 }  // namespace internal
 
 void DocumentWritePageLoadMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  if (info.main_frame_metadata.behavior_flags &
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  if (GetDelegate().GetMainFrameMetadata().behavior_flags &
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorDocumentWriteBlock) {
     LogDocumentWriteBlockFirstContentfulPaint(timing);
   }
@@ -58,18 +57,16 @@
 
 void DocumentWritePageLoadMetricsObserver::
     OnFirstMeaningfulPaintInMainFrameDocument(
-        const page_load_metrics::mojom::PageLoadTiming& timing,
-        const page_load_metrics::PageLoadExtraInfo& info) {
-  if (info.main_frame_metadata.behavior_flags &
+        const page_load_metrics::mojom::PageLoadTiming& timing) {
+  if (GetDelegate().GetMainFrameMetadata().behavior_flags &
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorDocumentWriteBlock) {
     LogDocumentWriteBlockFirstMeaningfulPaint(timing);
   }
 }
 
 void DocumentWritePageLoadMetricsObserver::OnParseStop(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  if (info.main_frame_metadata.behavior_flags &
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  if (GetDelegate().GetMainFrameMetadata().behavior_flags &
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorDocumentWriteBlock) {
     LogDocumentWriteBlockParseStop(timing);
   }
@@ -96,32 +93,33 @@
 
 void DocumentWritePageLoadMetricsObserver::OnLoadingBehaviorObserved(
     content::RenderFrameHost* rfh,
-    int behavior_flags,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  if ((info.main_frame_metadata.behavior_flags &
+    int behavior_flags) {
+  if ((GetDelegate().GetMainFrameMetadata().behavior_flags &
        blink::WebLoadingBehaviorFlag::
            kWebLoadingBehaviorDocumentWriteBlockReload) &&
       !doc_write_block_reload_observed_) {
     DCHECK(!(
-        info.main_frame_metadata.behavior_flags &
+        GetDelegate().GetMainFrameMetadata().behavior_flags &
         blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorDocumentWriteBlock));
     UMA_HISTOGRAM_COUNTS_1M(internal::kHistogramDocWriteBlockReloadCount, 1);
-    LogLoadingBehaviorMetrics(LOADING_BEHAVIOR_RELOAD, info.source_id);
+    LogLoadingBehaviorMetrics(LOADING_BEHAVIOR_RELOAD,
+                              GetDelegate().GetSourceId());
     doc_write_block_reload_observed_ = true;
   }
-  if ((info.main_frame_metadata.behavior_flags &
+  if ((GetDelegate().GetMainFrameMetadata().behavior_flags &
        blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorDocumentWriteBlock) &&
       !doc_write_block_observed_) {
     UMA_HISTOGRAM_BOOLEAN(internal::kHistogramDocWriteBlockCount, true);
-    LogLoadingBehaviorMetrics(LOADING_BEHAVIOR_BLOCK, info.source_id);
+    LogLoadingBehaviorMetrics(LOADING_BEHAVIOR_BLOCK,
+                              GetDelegate().GetSourceId());
     doc_write_block_observed_ = true;
   }
-  if ((info.main_frame_metadata.behavior_flags &
+  if ((GetDelegate().GetMainFrameMetadata().behavior_flags &
        blink::WebLoadingBehaviorFlag::
            kWebLoadingBehaviorDocumentWriteBlockDifferentScheme) &&
       !doc_write_same_site_diff_scheme_) {
     LogLoadingBehaviorMetrics(LOADING_BEHAVIOR_SAME_SITE_DIFF_SCHEME,
-                              info.source_id);
+                              GetDelegate().GetSourceId());
     doc_write_same_site_diff_scheme_ = true;
   }
 }
diff --git a/chrome/browser/page_load_metrics/observers/document_write_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/document_write_page_load_metrics_observer.h
index a0c54eb..dfc6d315 100644
--- a/chrome/browser/page_load_metrics/observers/document_write_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/document_write_page_load_metrics_observer.h
@@ -25,21 +25,16 @@
 
   // page_load_metrics::PageLoadMetricsObserver implementation:
   void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
   void OnFirstMeaningfulPaintInMainFrameDocument(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
   void OnParseStop(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
-  void OnLoadingBehaviorObserved(
-      content::RenderFrameHost* rfh,
-      int behavior_flags,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+  void OnLoadingBehaviorObserved(content::RenderFrameHost* rfh,
+                                 int behavior_flags) override;
 
   enum DocumentWriteLoadingBehavior {
     LOADING_BEHAVIOR_BLOCK,
diff --git a/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.cc b/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.cc
index e8821960..81c5ee68 100644
--- a/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.cc
@@ -39,16 +39,14 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 ForegroundDurationUKMObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   RecordUkmIfInForeground(base::TimeTicks::Now());
   return CONTINUE_OBSERVING;
 }
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 ForegroundDurationUKMObserver::OnHidden(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   RecordUkmIfInForeground(base::TimeTicks::Now());
   return CONTINUE_OBSERVING;
 }
@@ -62,16 +60,15 @@
 }
 
 void ForegroundDurationUKMObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   // If we have a page_end_time, use it as our end time, else fall back to the
   // current time. Note that we expect page_end_time.has_value() to always be
   // true in OnComplete (the PageLoadTracker destructor is supposed to guarantee
   // it), but we use Now() as a graceful fallback just in case.
-  base::TimeTicks end_time =
-      info.page_end_time.has_value()
-          ? info.navigation_start + info.page_end_time.value()
-          : base::TimeTicks::Now();
+  base::TimeTicks end_time = GetDelegate().GetPageEndTime().has_value()
+                                 ? GetDelegate().GetNavigationStart() +
+                                       GetDelegate().GetPageEndTime().value()
+                                 : base::TimeTicks::Now();
   RecordUkmIfInForeground(end_time);
 }
 
diff --git a/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.h b/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.h
index 2ab8c48..aa24c97 100644
--- a/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.h
+++ b/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.h
@@ -24,14 +24,12 @@
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
   ObservePolicy FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   ObservePolicy OnHidden(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   ObservePolicy OnShown() override;
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
+  void OnComplete(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
  private:
   bool currently_in_foreground_ = false;
diff --git a/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.cc
index 898dda1..0f6c923 100644
--- a/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.cc
@@ -370,76 +370,64 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 FromGWSPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   logger_.FlushMetricsOnAppEnterBackground(timing, GetDelegate());
   return STOP_OBSERVING;
 }
 
 void FromGWSPageLoadMetricsObserver::OnDomContentLoadedEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   logger_.OnDomContentLoadedEventStart(timing, GetDelegate());
 }
 
 void FromGWSPageLoadMetricsObserver::OnLoadEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   logger_.OnLoadEventStart(timing, GetDelegate());
 }
 
 void FromGWSPageLoadMetricsObserver::OnFirstPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   logger_.OnFirstPaintInPage(timing, GetDelegate());
 }
 
 void FromGWSPageLoadMetricsObserver::OnFirstImagePaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   logger_.OnFirstImagePaintInPage(timing, GetDelegate());
 }
 
 void FromGWSPageLoadMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   logger_.OnFirstContentfulPaintInPage(timing, GetDelegate());
 }
 
 void FromGWSPageLoadMetricsObserver::OnFirstInputInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   logger_.OnFirstInputInPage(timing, GetDelegate());
 }
 
 void FromGWSPageLoadMetricsObserver::OnParseStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   logger_.OnParseStart(timing, GetDelegate());
 }
 
 void FromGWSPageLoadMetricsObserver::OnParseStop(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   logger_.OnParseStop(timing, GetDelegate());
 }
 
 void FromGWSPageLoadMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   logger_.OnComplete(timing, GetDelegate());
 }
 
 void FromGWSPageLoadMetricsObserver::OnFailedProvisionalLoad(
-    const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info) {
   logger_.OnFailedProvisionalLoad(failed_load_info, GetDelegate());
 }
 
 void FromGWSPageLoadMetricsObserver::OnUserInput(
     const blink::WebInputEvent& event,
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   logger_.OnUserInput(event, timing, GetDelegate());
 }
 
diff --git a/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.h
index a20c48e..3819ce9 100644
--- a/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.h
@@ -143,45 +143,34 @@
                          ukm::SourceId source_id) override;
 
   ObservePolicy FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
   void OnDomContentLoadedEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnLoadEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstImagePaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstInputInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnParseStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnParseStop(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
   void OnComplete(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFailedProvisionalLoad(
-      const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info)
+      override;
 
   void OnUserInput(
       const blink::WebInputEvent& event,
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
  private:
   FromGWSPageLoadMetricsLogger logger_;
diff --git a/chrome/browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer.cc
index 843912f..87ba0228 100644
--- a/chrome/browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer.cc
@@ -20,9 +20,8 @@
 }
 
 void HttpsEngagementPageLoadMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
-  if (!extra_info.did_commit || !extra_info.url.is_valid()) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  if (!GetDelegate().DidCommit() || !GetDelegate().GetUrl().is_valid()) {
     return;
   }
 
@@ -32,14 +31,14 @@
   if (foreground_time.is_zero())
     return;
 
-  if (extra_info.url.SchemeIs(url::kHttpsScheme)) {
+  if (GetDelegate().GetUrl().SchemeIs(url::kHttpsScheme)) {
     if (engagement_service_) {
       engagement_service_->RecordTimeOnPage(foreground_time,
                                             HttpsEngagementService::HTTPS);
     }
     UMA_HISTOGRAM_LONG_TIMES_100(internal::kHttpsEngagementHistogram,
                                  foreground_time);
-  } else if (extra_info.url.SchemeIs(url::kHttpScheme)) {
+  } else if (GetDelegate().GetUrl().SchemeIs(url::kHttpScheme)) {
     if (engagement_service_) {
       engagement_service_->RecordTimeOnPage(foreground_time,
                                             HttpsEngagementService::HTTP);
diff --git a/chrome/browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer.h
index 904a777..4d714ca 100644
--- a/chrome/browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer.h
@@ -27,8 +27,7 @@
 
   // page_load_metrics::PageLoadMetricsObserver:
   void OnComplete(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
  private:
   HttpsEngagementService* engagement_service_;
diff --git a/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.cc b/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.cc
index 60140a61..97f051e 100644
--- a/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.cc
+++ b/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h"
 
+#include "chrome/browser/page_load_metrics/page_load_metrics_observer_delegate.h"
 #include "chrome/common/page_load_metrics/page_load_metrics.mojom.h"
 #include "content/public/browser/render_frame_host.h"
 
@@ -196,7 +197,7 @@
 
 void LargestContentfulPaintHandler::OnDidFinishSubFrameNavigation(
     content::NavigationHandle* navigation_handle,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const PageLoadMetricsObserverDelegate& delegate) {
   if (!navigation_handle->HasCommitted())
     return;
 
@@ -205,10 +206,10 @@
   subframe_navigation_start_offset_.erase(
       navigation_handle->GetFrameTreeNodeId());
 
-  if (extra_info.navigation_start > navigation_handle->NavigationStart())
+  if (delegate.GetNavigationStart() > navigation_handle->NavigationStart())
     return;
   base::TimeDelta navigation_delta =
-      navigation_handle->NavigationStart() - extra_info.navigation_start;
+      navigation_handle->NavigationStart() - delegate.GetNavigationStart();
   subframe_navigation_start_offset_.insert(std::make_pair(
       navigation_handle->GetFrameTreeNodeId(), navigation_delta));
 }
diff --git a/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h b/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h
index 0bc3200..0aa0c61 100644
--- a/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h
+++ b/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h
@@ -98,7 +98,7 @@
   const ContentfulPaintTimingInfo& MergeMainFrameAndSubframes();
   void OnDidFinishSubFrameNavigation(
       content::NavigationHandle* navigation_handle,
-      const page_load_metrics::PageLoadExtraInfo& extra_info);
+      const PageLoadMetricsObserverDelegate& delegate);
 
  private:
   void RecordSubframeTiming(const mojom::PaintTimingPtr& timing,
diff --git a/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer.cc
index 5f71438..b3d1bbd 100644
--- a/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer.cc
@@ -56,8 +56,7 @@
 LiveTabCountPageLoadMetricsObserver::~LiveTabCountPageLoadMetricsObserver() {}
 
 void LiveTabCountPageLoadMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_contentful_paint, GetDelegate())) {
     const std::string histogram_prefix(
@@ -73,8 +72,7 @@
 
 void LiveTabCountPageLoadMetricsObserver::
     OnFirstMeaningfulPaintInMainFrameDocument(
-        const page_load_metrics::mojom::PageLoadTiming& timing,
-        const page_load_metrics::PageLoadExtraInfo& info) {
+        const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_meaningful_paint, GetDelegate())) {
     const std::string histogram_prefix(
@@ -89,8 +87,7 @@
 }
 
 void LiveTabCountPageLoadMetricsObserver::OnFirstInputInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.interactive_timing->first_input_timestamp, GetDelegate())) {
     const std::string histogram_prefix(
diff --git a/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer.h
index 5fbf3e45..6600c48 100644
--- a/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer.h
@@ -25,14 +25,11 @@
 
   // page_load_metrics::PageLoadMetricsObserver:
   void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstMeaningfulPaintInMainFrameDocument(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstInputInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
  protected:
   // Returns the number of live tabs, including the one that we're observing.
diff --git a/chrome/browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer.cc
index e79979d2..d76f8157 100644
--- a/chrome/browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer.cc
@@ -65,19 +65,17 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 LoadingPredictorPageLoadMetricsObserver::OnHidden(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   record_histogram_preconnectable_ = false;
   return CONTINUE_OBSERVING;
 }
 
 void LoadingPredictorPageLoadMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   predictors::NavigationID navigation_id(GetDelegate().GetWebContents());
 
   collector_->RecordFirstContentfulPaint(
-      navigation_id, extra_info.navigation_start +
+      navigation_id, GetDelegate().GetNavigationStart() +
                          timing.paint_timing->first_contentful_paint.value());
   if (record_histogram_preconnectable_) {
     PAGE_LOAD_HISTOGRAM(
@@ -88,8 +86,7 @@
 
 void LoadingPredictorPageLoadMetricsObserver::
     OnFirstMeaningfulPaintInMainFrameDocument(
-        const page_load_metrics::mojom::PageLoadTiming& timing,
-        const page_load_metrics::PageLoadExtraInfo& extra_info) {
+        const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (record_histogram_preconnectable_) {
     PAGE_LOAD_HISTOGRAM(
         internal::kHistogramLoadingPredictorFirstMeaningfulPaintPreconnectable,
diff --git a/chrome/browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer.h
index c78c293e..26c0b218 100644
--- a/chrome/browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer.h
@@ -49,14 +49,11 @@
                         const GURL& currently_commited_url,
                         bool started_in_foreground) override;
   ObservePolicy OnHidden(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstMeaningfulPaintInMainFrameDocument(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
  private:
   predictors::ResourcePrefetchPredictor* predictor_;
diff --git a/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.cc
index 55c569e4..c1bfa03 100644
--- a/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.cc
@@ -350,13 +350,12 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 LocalNetworkRequestsPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   // The browser may come back, but there is no guarantee. To be safe, we record
   // what we have now and treat changes to this navigation as new page loads.
-  if (extra_info.did_commit) {
+  if (GetDelegate().DidCommit()) {
     RecordHistograms();
-    RecordUkmMetrics(extra_info.source_id);
+    RecordUkmMetrics(GetDelegate().GetSourceId());
     ClearLocalState();
   }
 
@@ -395,11 +394,10 @@
 }
 
 void LocalNetworkRequestsPageLoadMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  if (info.did_commit) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  if (GetDelegate().DidCommit()) {
     RecordHistograms();
-    RecordUkmMetrics(info.source_id);
+    RecordUkmMetrics(GetDelegate().GetSourceId());
   }
 }
 
diff --git a/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.h
index e33548f7..b8bbd23 100644
--- a/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.h
@@ -69,12 +69,11 @@
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
   ObservePolicy FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnLoadedResource(const page_load_metrics::ExtraRequestCompleteInfo&
                             extra_request_info) override;
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
+  void OnComplete(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
  private:
   // Clears all local resource request counts. Only used if we decide to log
diff --git a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.cc
index 2ea45b9..49fe8946 100644
--- a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.cc
@@ -40,21 +40,19 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 MediaPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   // FlushMetricsOnAppEnterBackground is invoked on Android in cases where the
   // app is about to be backgrounded, as part of the Activity.onPause()
   // flow. After this method is invoked, Chrome may be killed without further
   // notification, so we record final metrics collected up to this point.
-  if (info.did_commit && played_media_) {
+  if (GetDelegate().DidCommit() && played_media_) {
     RecordByteHistograms();
   }
   return STOP_OBSERVING;
 }
 
 void MediaPageLoadMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!played_media_)
     return;
   RecordByteHistograms();
diff --git a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h
index 5d56e08..a4eec0a 100644
--- a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h
@@ -20,12 +20,11 @@
   ~MediaPageLoadMetricsObserver() override;
 
   // page_load_metrics::PageLoadMetricsObserver:
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
+  void OnComplete(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   page_load_metrics::PageLoadMetricsObserver::ObservePolicy
   FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnResourceDataUseObserved(
       content::RenderFrameHost* rfh,
       const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
diff --git a/chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer.cc
index 34f27f8..aa565e64 100644
--- a/chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer.cc
@@ -67,8 +67,7 @@
   } while (false)
 
 void MultiTabLoadingPageLoadMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_contentful_paint, GetDelegate())) {
     RECORD_HISTOGRAMS(internal::kHistogramFirstContentfulPaintSuffix,
@@ -80,14 +79,13 @@
     RECORD_HISTOGRAMS(
         internal::kHistogramForegroundToFirstContentfulPaintSuffix,
         timing.paint_timing->first_contentful_paint.value() -
-            info.first_foreground_time.value());
+            GetDelegate().GetFirstForegroundTime().value());
   }
 }
 
 void MultiTabLoadingPageLoadMetricsObserver::
     OnFirstMeaningfulPaintInMainFrameDocument(
-        const page_load_metrics::mojom::PageLoadTiming& timing,
-        const page_load_metrics::PageLoadExtraInfo& info) {
+        const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_meaningful_paint, GetDelegate())) {
     RECORD_HISTOGRAMS(internal::kHistogramFirstMeaningfulPaintSuffix,
@@ -98,13 +96,12 @@
     RECORD_HISTOGRAMS(
         internal::kHistogramForegroundToFirstMeaningfulPaintSuffix,
         timing.paint_timing->first_meaningful_paint.value() -
-            info.first_foreground_time.value());
+            GetDelegate().GetFirstForegroundTime().value());
   }
 }
 
 void MultiTabLoadingPageLoadMetricsObserver::OnDomContentLoadedEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.document_timing->dom_content_loaded_event_start,
           GetDelegate())) {
@@ -119,8 +116,7 @@
 }
 
 void MultiTabLoadingPageLoadMetricsObserver::OnLoadEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.document_timing->load_event_start, GetDelegate())) {
     RECORD_HISTOGRAMS(internal::kHistogramLoadEventFiredSuffix,
diff --git a/chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer.h
index fde3bad..47c02c9 100644
--- a/chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer.h
@@ -35,17 +35,13 @@
       const GURL& currently_committed_url,
       bool started_in_foreground) override;
   void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstMeaningfulPaintInMainFrameDocument(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnDomContentLoadedEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnLoadEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
  protected:
   // Overridden in testing. Returns the number of loading tabs, excluding
diff --git a/chrome/browser/page_load_metrics/observers/no_state_prefetch_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/no_state_prefetch_page_load_metrics_observer.cc
index 140c6f1b..c0cf829 100644
--- a/chrome/browser/page_load_metrics/observers/no_state_prefetch_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/no_state_prefetch_page_load_metrics_observer.cc
@@ -46,18 +46,16 @@
 }
 
 void NoStatePrefetchPageLoadMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK(timing.paint_timing->first_contentful_paint.has_value());
   prerender_manager_->RecordNoStateFirstContentfulPaint(
-      extra_info.start_url, is_no_store_, was_hidden_,
+      GetDelegate().GetStartUrl(), is_no_store_, was_hidden_,
       *timing.paint_timing->first_contentful_paint);
 }
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 NoStatePrefetchPageLoadMetricsObserver::OnHidden(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   was_hidden_ = true;
   return CONTINUE_OBSERVING;
 }
diff --git a/chrome/browser/page_load_metrics/observers/no_state_prefetch_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/no_state_prefetch_page_load_metrics_observer.h
index 29e4f66..1cdf731 100644
--- a/chrome/browser/page_load_metrics/observers/no_state_prefetch_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/no_state_prefetch_page_load_metrics_observer.h
@@ -48,11 +48,9 @@
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
   void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   ObservePolicy OnHidden(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
   bool is_no_store_;  // True if the main resource has a "no-store" HTTP header.
   bool was_hidden_;   // The page went to background while rendering.
diff --git a/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.cc
index 85374267..77315b2 100644
--- a/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.cc
@@ -19,17 +19,6 @@
 
 namespace previews {
 
-namespace {
-
-void RecordPageLoadExtraInfoMetrics(
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  UMA_HISTOGRAM_ENUMERATION(
-      internal::kHistogramOfflinePreviewsPageEndReason, info.page_end_reason,
-      page_load_metrics::PageEndReason::PAGE_END_REASON_COUNT);
-}
-
-}  // namespace
-
 namespace internal {
 
 const char kHistogramOfflinePreviewsDOMContentLoadedEventFired[] =
@@ -80,21 +69,18 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 OfflinePagePreviewsPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  RecordPageLoadExtraInfoMetrics(info);
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  RecordPageLoadMetrics();
   return STOP_OBSERVING;
 }
 
 void OfflinePagePreviewsPageLoadMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  RecordPageLoadExtraInfoMetrics(info);
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  RecordPageLoadMetrics();
 }
 
 void OfflinePagePreviewsPageLoadMetricsObserver::OnDomContentLoadedEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.document_timing->dom_content_loaded_event_start,
           GetDelegate())) {
@@ -106,8 +92,7 @@
 }
 
 void OfflinePagePreviewsPageLoadMetricsObserver::OnLoadEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.document_timing->load_event_start, GetDelegate())) {
     return;
@@ -117,8 +102,7 @@
 }
 
 void OfflinePagePreviewsPageLoadMetricsObserver::OnFirstLayout(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.document_timing->first_layout, GetDelegate())) {
     return;
@@ -128,8 +112,7 @@
 }
 
 void OfflinePagePreviewsPageLoadMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_contentful_paint, GetDelegate())) {
     return;
@@ -139,8 +122,7 @@
 }
 
 void OfflinePagePreviewsPageLoadMetricsObserver::OnParseStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.parse_timing->parse_start, GetDelegate())) {
     return;
@@ -160,4 +142,11 @@
 #endif  // BUILDFLAG(ENABLE_OFFLINE_PAGES)
 }
 
+void OfflinePagePreviewsPageLoadMetricsObserver::RecordPageLoadMetrics() {
+  UMA_HISTOGRAM_ENUMERATION(
+      internal::kHistogramOfflinePreviewsPageEndReason,
+      GetDelegate().GetPageEndReason(),
+      page_load_metrics::PageEndReason::PAGE_END_REASON_COUNT);
+}
+
 }  // namespace previews
diff --git a/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.h
index 73d7e0a..f7171a1 100644
--- a/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.h
@@ -42,23 +42,19 @@
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
   ObservePolicy FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnComplete(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnDomContentLoadedEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnLoadEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
-  void OnFirstLayout(const page_load_metrics::mojom::PageLoadTiming& timing,
-                     const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnFirstLayout(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
-  void OnParseStart(const page_load_metrics::mojom::PageLoadTiming& timing,
-                    const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnParseStart(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   ObservePolicy ShouldObserveMimeType(
       const std::string& mime_type) const override;
 
@@ -67,6 +63,9 @@
   // testing.
   virtual bool IsOfflinePreview(content::WebContents* web_contents) const;
 
+  // Records metrics derived from global page load state.
+  void RecordPageLoadMetrics();
+
   DISALLOW_COPY_AND_ASSIGN(OfflinePagePreviewsPageLoadMetricsObserver);
 };
 
diff --git a/chrome/browser/page_load_metrics/observers/omnibox_suggestion_used_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/omnibox_suggestion_used_page_load_metrics_observer.cc
index 3687ca55..e5819f3 100644
--- a/chrome/browser/page_load_metrics/observers/omnibox_suggestion_used_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/omnibox_suggestion_used_page_load_metrics_observer.cc
@@ -46,8 +46,7 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 OmniboxSuggestionUsedMetricsObserver::OnHidden(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   return STOP_OBSERVING;
 }
 
@@ -62,11 +61,10 @@
 }
 
 void OmniboxSuggestionUsedMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   base::TimeDelta fcp = timing.paint_timing->first_contentful_paint.value();
 
-  if (info.started_in_foreground) {
+  if (GetDelegate().StartedInForeground()) {
     if (ui::PageTransitionCoreTypeIs(transition_type_,
                                      ui::PAGE_TRANSITION_GENERATED)) {
       PAGE_LOAD_HISTOGRAM(kSearchFirstContentfulPaint, fcp);
@@ -79,30 +77,30 @@
   // Since a page is not supposed to paint in the background,
   // when this function gets called, first_foreground_time should be set.
   // We add this check just to be safe.
-  if (is_prerender_ && info.first_foreground_time) {
+  if (is_prerender_ && GetDelegate().GetFirstForegroundTime()) {
     base::TimeDelta perceived_fcp =
-        std::max(base::TimeDelta(), fcp - info.first_foreground_time.value());
+        std::max(base::TimeDelta(),
+                 fcp - GetDelegate().GetFirstForegroundTime().value());
     if (ui::PageTransitionCoreTypeIs(transition_type_,
                                      ui::PAGE_TRANSITION_GENERATED)) {
       PAGE_LOAD_HISTOGRAM(kPrerenderSearchFirstContentfulPaint, perceived_fcp);
       PAGE_LOAD_HISTOGRAM(kPrerenderSearchNavigationToFirstForeground,
-                          info.first_foreground_time.value());
+                          GetDelegate().GetFirstForegroundTime().value());
     } else if (ui::PageTransitionCoreTypeIs(transition_type_,
                                             ui::PAGE_TRANSITION_TYPED)) {
       PAGE_LOAD_HISTOGRAM(kPrerenderURLFirstContentfulPaint, perceived_fcp);
       PAGE_LOAD_HISTOGRAM(kPrerenderURLNavigationToFirstForeground,
-                          info.first_foreground_time.value());
+                          GetDelegate().GetFirstForegroundTime().value());
     }
   }
 }
 
 void OmniboxSuggestionUsedMetricsObserver::
     OnFirstMeaningfulPaintInMainFrameDocument(
-        const page_load_metrics::mojom::PageLoadTiming& timing,
-        const page_load_metrics::PageLoadExtraInfo& info) {
+        const page_load_metrics::mojom::PageLoadTiming& timing) {
   base::TimeDelta fmp = timing.paint_timing->first_meaningful_paint.value();
 
-  if (info.started_in_foreground) {
+  if (GetDelegate().StartedInForeground()) {
     if (ui::PageTransitionCoreTypeIs(transition_type_,
                                      ui::PAGE_TRANSITION_GENERATED)) {
       PAGE_LOAD_HISTOGRAM(kSearchFirstMeaningfulPaint, fmp);
@@ -110,9 +108,10 @@
                                             ui::PAGE_TRANSITION_TYPED)) {
       PAGE_LOAD_HISTOGRAM(kURLFirstMeaningfulPaint, fmp);
     }
-  } else if (is_prerender_ && info.first_foreground_time) {
+  } else if (is_prerender_ && GetDelegate().GetFirstForegroundTime()) {
     base::TimeDelta perceived_fmp =
-        std::max(base::TimeDelta(), fmp - info.first_foreground_time.value());
+        std::max(base::TimeDelta(),
+                 fmp - GetDelegate().GetFirstForegroundTime().value());
     if (ui::PageTransitionCoreTypeIs(transition_type_,
                                      ui::PAGE_TRANSITION_GENERATED)) {
       PAGE_LOAD_HISTOGRAM(kPrerenderSearchFirstMeaningfulPaint, perceived_fmp);
diff --git a/chrome/browser/page_load_metrics/observers/omnibox_suggestion_used_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/omnibox_suggestion_used_page_load_metrics_observer.h
index e481729..13a4ab8 100644
--- a/chrome/browser/page_load_metrics/observers/omnibox_suggestion_used_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/omnibox_suggestion_used_page_load_metrics_observer.h
@@ -18,16 +18,13 @@
 
   // page_load_metrics::PageLoadMetricsObserver:
   ObservePolicy OnHidden(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
   void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstMeaningfulPaintInMainFrameDocument(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
  private:
   // Indicates whether this page load comes from prerender.
diff --git a/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.cc
index fe5dc9f..10e8a0a 100644
--- a/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.cc
@@ -135,22 +135,20 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 PreviewsPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   // FlushMetricsOnAppEnterBackground is invoked on Android in cases where the
   // app is about to be backgrounded, as part of the Activity.onPause()
   // flow. After this method is invoked, Chrome may be killed without further
   // notification.
-  if (info.did_commit) {
+  if (GetDelegate().DidCommit()) {
     RecordPageSizeUMA();
-    RecordTimingMetrics(timing, info);
+    RecordTimingMetrics(timing);
   }
   return STOP_OBSERVING;
 }
 
 void PreviewsPageLoadMetricsObserver::OnLoadEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   // TODO(dougarnett): Determine if a different event makes more sense.
   // https://crbug.com/864720
   int64_t inflation_bytes = 0;
@@ -164,16 +162,15 @@
       (total_network_bytes_ * data_savings_inflation_percent_) / 100 +
       inflation_bytes;
 
-  DCHECK(info.url.SchemeIsHTTPOrHTTPS());
+  DCHECK(GetDelegate().GetUrl().SchemeIsHTTPOrHTTPS());
 
-  WriteToSavings(info.url, total_saved_bytes);
+  WriteToSavings(GetDelegate().GetUrl(), total_saved_bytes);
 }
 
 void PreviewsPageLoadMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   RecordPageSizeUMA();
-  RecordTimingMetrics(timing, info);
+  RecordTimingMetrics(timing);
 }
 
 void PreviewsPageLoadMetricsObserver::RecordPageSizeUMA() const {
@@ -182,8 +179,7 @@
 }
 
 void PreviewsPageLoadMetricsObserver::RecordTimingMetrics(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.document_timing->load_event_start, GetDelegate())) {
     RecordPageLoadHistogram(previews_type_,
diff --git a/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.h
index d44ea5b..3a7ed20 100644
--- a/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.h
@@ -32,13 +32,11 @@
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
   ObservePolicy FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnComplete(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnLoadEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnResourceDataUseObserved(
       content::RenderFrameHost* rfh,
       const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
@@ -50,8 +48,7 @@
 
  private:
   void RecordTimingMetrics(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info);
+      const page_load_metrics::mojom::PageLoadTiming& timing);
 
   // Records UMA of page size when the observer is about to be deleted.
   void RecordPageSizeUMA() const;
diff --git a/chrome/browser/page_load_metrics/observers/previews_ukm_observer.cc b/chrome/browser/page_load_metrics/observers/previews_ukm_observer.cc
index f70e1814..8130d1e 100644
--- a/chrome/browser/page_load_metrics/observers/previews_ukm_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/previews_ukm_observer.cc
@@ -177,48 +177,43 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 PreviewsUKMObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  RecordMetrics(info);
+  RecordMetrics();
   return STOP_OBSERVING;
 }
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 PreviewsUKMObserver::OnHidden(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  RecordMetrics(info);
+  RecordMetrics();
   return STOP_OBSERVING;
 }
 
 void PreviewsUKMObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  RecordMetrics(info);
+  RecordMetrics();
 }
 
-void PreviewsUKMObserver::RecordMetrics(
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  RecordPreviewsTypes(info);
-  RecordOptimizationGuideInfo(info);
+void PreviewsUKMObserver::RecordMetrics() {
+  RecordPreviewsTypes();
+  RecordOptimizationGuideInfo();
 }
 
-void PreviewsUKMObserver::RecordPreviewsTypes(
-    const page_load_metrics::PageLoadExtraInfo& info) {
+void PreviewsUKMObserver::RecordPreviewsTypes() {
   // Record the page end reason in UMA.
   if (committed_preview_ != PreviewsType::NONE) {
     UMA_HISTOGRAM_ENUMERATION(
-        "Previews.PageEndReason", info.page_end_reason,
+        "Previews.PageEndReason", GetDelegate().GetPageEndReason(),
         page_load_metrics::PageEndReason::PAGE_END_REASON_COUNT);
   }
   base::UmaHistogramExactLinear(
       base::StringPrintf(
           "Previews.PageEndReason.%s",
           previews::GetStringNameForType(committed_preview_).c_str()),
-      info.page_end_reason,
+      GetDelegate().GetPageEndReason(),
       page_load_metrics::PageEndReason::PAGE_END_REASON_COUNT);
 
   // Only record previews types when they are active.
@@ -232,7 +227,7 @@
     return;
   }
 
-  ukm::builders::Previews builder(info.source_id);
+  ukm::builders::Previews builder(GetDelegate().GetSourceId());
   builder.Setcoin_flip_result(static_cast<int>(coin_flip_result_));
   if (lite_page_seen_)
     builder.Setlite_page(1);
@@ -292,8 +287,7 @@
   builder.Record(ukm::UkmRecorder::Get());
 }
 
-void PreviewsUKMObserver::RecordOptimizationGuideInfo(
-    const page_load_metrics::PageLoadExtraInfo& info) {
+void PreviewsUKMObserver::RecordOptimizationGuideInfo() {
   if (!serialized_hint_version_string_.has_value()) {
     return;
   }
@@ -308,7 +302,7 @@
   if (!hint_version.ParseFromString(binary_version_pb))
     return;
 
-  ukm::builders::OptimizationGuide builder(info.source_id);
+  ukm::builders::OptimizationGuide builder(GetDelegate().GetSourceId());
   if (hint_version.has_generation_timestamp() &&
       hint_version.generation_timestamp().seconds() > 0) {
     builder.SetHintGenerationTimestamp(
diff --git a/chrome/browser/page_load_metrics/observers/previews_ukm_observer.h b/chrome/browser/page_load_metrics/observers/previews_ukm_observer.h
index 7213918..a320846 100644
--- a/chrome/browser/page_load_metrics/observers/previews_ukm_observer.h
+++ b/chrome/browser/page_load_metrics/observers/previews_ukm_observer.h
@@ -35,13 +35,11 @@
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
   ObservePolicy FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   ObservePolicy OnHidden(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnComplete(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnEventOccurred(const void* const event_key) override;
   ObservePolicy ShouldObserveMimeType(
       const std::string& mime_type) const override;
@@ -57,10 +55,9 @@
   virtual bool IsOfflinePreview(content::WebContents* web_contents) const;
 
  private:
-  void RecordMetrics(const page_load_metrics::PageLoadExtraInfo& info);
-  void RecordPreviewsTypes(const page_load_metrics::PageLoadExtraInfo& info);
-  void RecordOptimizationGuideInfo(
-      const page_load_metrics::PageLoadExtraInfo& info);
+  void RecordMetrics();
+  void RecordPreviewsTypes();
+  void RecordOptimizationGuideInfo();
 
   // The preview type that was actually committed and seen by the user.
   PreviewsType committed_preview_;
diff --git a/chrome/browser/page_load_metrics/observers/protocol_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/protocol_page_load_metrics_observer.cc
index aaa204ac..d2f7c446 100644
--- a/chrome/browser/page_load_metrics/observers/protocol_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/protocol_page_load_metrics_observer.cc
@@ -44,21 +44,18 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 ProtocolPageLoadMetricsObserver::OnHidden(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   return STOP_OBSERVING;
 }
 
 void ProtocolPageLoadMetricsObserver::OnParseStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   PROTOCOL_HISTOGRAM("ParseTiming.NavigationToParseStart", protocol_,
                      timing.parse_timing->parse_start.value());
 }
 
 void ProtocolPageLoadMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   PROTOCOL_HISTOGRAM("PaintTiming.NavigationToFirstContentfulPaint", protocol_,
                      timing.paint_timing->first_contentful_paint.value());
   PROTOCOL_HISTOGRAM("PaintTiming.ParseStartToFirstContentfulPaint", protocol_,
@@ -67,8 +64,7 @@
 }
 
 void ProtocolPageLoadMetricsObserver::OnFirstMeaningfulPaintInMainFrameDocument(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   PROTOCOL_HISTOGRAM(
       "Experimental.PaintTiming.NavigationToFirstMeaningfulPaint", protocol_,
       timing.paint_timing->first_meaningful_paint.value());
@@ -79,16 +75,14 @@
 }
 
 void ProtocolPageLoadMetricsObserver::OnDomContentLoadedEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   PROTOCOL_HISTOGRAM(
       "DocumentTiming.NavigationToDOMContentLoadedEventFired", protocol_,
       timing.document_timing->dom_content_loaded_event_start.value());
 }
 
 void ProtocolPageLoadMetricsObserver::OnLoadEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   PROTOCOL_HISTOGRAM("DocumentTiming.NavigationToLoadEventFired", protocol_,
                      timing.document_timing->load_event_start.value());
 }
diff --git a/chrome/browser/page_load_metrics/observers/protocol_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/protocol_page_load_metrics_observer.h
index cf39e9b3..441a771 100644
--- a/chrome/browser/page_load_metrics/observers/protocol_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/protocol_page_load_metrics_observer.h
@@ -23,23 +23,17 @@
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
   ObservePolicy OnHidden(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnParseStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstMeaningfulPaintInMainFrameDocument(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnDomContentLoadedEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnLoadEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
  private:
   friend class ProtocolPageLoadMetricsObserverTest;
diff --git a/chrome/browser/page_load_metrics/observers/resource_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/resource_metrics_observer.cc
index c243004..8b2184d 100644
--- a/chrome/browser/page_load_metrics/observers/resource_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/resource_metrics_observer.cc
@@ -34,21 +34,19 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 ResourceMetricsObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   // TODO(johnidel): This logic was maintained when resource metrics were moved
   // out of the AdsPageLoadMetricsObserver. These metrics don't need to stop
   // being reported when backgrounded.
-  if (extra_info.did_commit) {
-    OnComplete(timing, extra_info);
+  if (GetDelegate().DidCommit()) {
+    OnComplete(timing);
   }
 
   return STOP_OBSERVING;
 }
 
 void ResourceMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   for (auto const& kv :
        GetDelegate().GetResourceTracker().unfinished_resources())
     RecordResourceHistograms(kv.second);
diff --git a/chrome/browser/page_load_metrics/observers/resource_metrics_observer.h b/chrome/browser/page_load_metrics/observers/resource_metrics_observer.h
index 6b4245dc..52bedd4 100644
--- a/chrome/browser/page_load_metrics/observers/resource_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/resource_metrics_observer.h
@@ -27,10 +27,9 @@
       const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
           resources) override;
   ObservePolicy FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnComplete(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
  private:
   // Records per-resource histograms.
diff --git a/chrome/browser/page_load_metrics/observers/scheme_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/scheme_page_load_metrics_observer.cc
index 0976d44f..a2260e0 100644
--- a/chrome/browser/page_load_metrics/observers/scheme_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/scheme_page_load_metrics_observer.cc
@@ -55,19 +55,17 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 SchemePageLoadMetricsObserver::OnHidden(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   return STOP_OBSERVING;
 }
 
 void SchemePageLoadMetricsObserver::OnParseStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
-  if (extra_info.url.scheme() == url::kHttpScheme) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  if (GetDelegate().GetUrl().scheme() == url::kHttpScheme) {
     PAGE_LOAD_HISTOGRAM(
         "PageLoad.Clients.Scheme.HTTP.ParseTiming.NavigationToParseStart",
         timing.parse_timing->parse_start.value());
-  } else if (extra_info.url.scheme() == url::kHttpsScheme) {
+  } else if (GetDelegate().GetUrl().scheme() == url::kHttpsScheme) {
     PAGE_LOAD_HISTOGRAM(
         "PageLoad.Clients.Scheme.HTTPS.ParseTiming.NavigationToParseStart",
         timing.parse_timing->parse_start.value());
@@ -75,16 +73,15 @@
 }
 
 void SchemePageLoadMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
-  DCHECK(extra_info.url.scheme() == url::kHttpScheme ||
-         extra_info.url.scheme() == url::kHttpsScheme);
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  DCHECK(GetDelegate().GetUrl().scheme() == url::kHttpScheme ||
+         GetDelegate().GetUrl().scheme() == url::kHttpsScheme);
 
   base::TimeDelta fcp = timing.paint_timing->first_contentful_paint.value();
   base::TimeDelta parse_start_to_fcp =
       fcp - timing.parse_timing->parse_start.value();
 
-  if (extra_info.url.scheme() == url::kHttpScheme) {
+  if (GetDelegate().GetUrl().scheme() == url::kHttpScheme) {
     PAGE_LOAD_HISTOGRAM(
         "PageLoad.Clients.Scheme.HTTP.PaintTiming."
         "NavigationToFirstContentfulPaint",
@@ -115,8 +112,9 @@
       "PageLoad.Clients.Scheme.HTTPS.PaintTiming.UnderStat.UserInitiated."
       "NewNavigation";
 
-  bool is_user_initiated = extra_info.user_initiated_info.browser_initiated ||
-                           extra_info.user_initiated_info.user_gesture;
+  bool is_user_initiated =
+      GetDelegate().GetUserInitiatedInfo().browser_initiated ||
+      GetDelegate().GetUserInitiatedInfo().user_gesture;
   bool is_user_initiated_new_navigation =
       is_user_initiated && ui::PageTransitionIsNewNavigation(transition_);
 
@@ -128,15 +126,17 @@
                 " mismatch in  array length and enum size");
 
   // Record the total count bucket first.
-  base::UmaHistogramEnumeration(extra_info.url.scheme() == url::kHttpScheme
-                                    ? kUnderStatHistogramHttp
-                                    : kUnderStatHistogramHttps,
-                                PageLoadTimingUnderStat::kTotal);
+  base::UmaHistogramEnumeration(
+      GetDelegate().GetUrl().scheme() == url::kHttpScheme
+          ? kUnderStatHistogramHttp
+          : kUnderStatHistogramHttps,
+      PageLoadTimingUnderStat::kTotal);
   if (is_user_initiated_new_navigation) {
-    base::UmaHistogramEnumeration(extra_info.url.scheme() == url::kHttpScheme
-                                      ? kUnderStatHistogramHttpUserNewNav
-                                      : kUnderStatHistogramHttpsUserNewNav,
-                                  PageLoadTimingUnderStat::kTotal);
+    base::UmaHistogramEnumeration(
+        GetDelegate().GetUrl().scheme() == url::kHttpScheme
+            ? kUnderStatHistogramHttpUserNewNav
+            : kUnderStatHistogramHttpsUserNewNav,
+        PageLoadTimingUnderStat::kTotal);
   }
 
   for (size_t index = 0;
@@ -145,13 +145,13 @@
         kUnderStatRecordingIntervalsSeconds[index]));
     if (fcp <= threshold) {
       base::UmaHistogramEnumeration(
-          extra_info.url.scheme() == url::kHttpScheme
+          GetDelegate().GetUrl().scheme() == url::kHttpScheme
               ? kUnderStatHistogramHttp
               : kUnderStatHistogramHttps,
           static_cast<PageLoadTimingUnderStat>(index + 1));
       if (is_user_initiated_new_navigation) {
         base::UmaHistogramEnumeration(
-            extra_info.url.scheme() == url::kHttpScheme
+            GetDelegate().GetUrl().scheme() == url::kHttpScheme
                 ? kUnderStatHistogramHttpUserNewNav
                 : kUnderStatHistogramHttpsUserNewNav,
             static_cast<PageLoadTimingUnderStat>(index + 1));
@@ -161,14 +161,13 @@
 }
 
 void SchemePageLoadMetricsObserver::OnFirstMeaningfulPaintInMainFrameDocument(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
-  if (extra_info.url.scheme() == url::kHttpScheme) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  if (GetDelegate().GetUrl().scheme() == url::kHttpScheme) {
     PAGE_LOAD_HISTOGRAM(
         "PageLoad.Clients.Scheme.HTTP.Experimental.PaintTiming."
         "NavigationToFirstMeaningfulPaint",
         timing.paint_timing->first_meaningful_paint.value());
-  } else if (extra_info.url.scheme() == url::kHttpsScheme) {
+  } else if (GetDelegate().GetUrl().scheme() == url::kHttpsScheme) {
     PAGE_LOAD_HISTOGRAM(
         "PageLoad.Clients.Scheme.HTTPS.Experimental.PaintTiming."
         "NavigationToFirstMeaningfulPaint",
@@ -177,13 +176,12 @@
 }
 
 void SchemePageLoadMetricsObserver::OnPageInteractive(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
-  if (extra_info.url.scheme() == url::kHttpScheme) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  if (GetDelegate().GetUrl().scheme() == url::kHttpScheme) {
     PAGE_LOAD_HISTOGRAM(
         "PageLoad.Clients.Scheme.HTTP.Experimental.NavigationToInteractive",
         timing.interactive_timing->interactive.value());
-  } else if (extra_info.url.scheme() == url::kHttpsScheme) {
+  } else if (GetDelegate().GetUrl().scheme() == url::kHttpsScheme) {
     PAGE_LOAD_HISTOGRAM(
         "PageLoad.Clients.Scheme.HTTPS.Experimental.NavigationToInteractive",
         timing.interactive_timing->interactive.value());
diff --git a/chrome/browser/page_load_metrics/observers/scheme_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/scheme_page_load_metrics_observer.h
index 8f43ba42..d5ef8cb8 100644
--- a/chrome/browser/page_load_metrics/observers/scheme_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/scheme_page_load_metrics_observer.h
@@ -22,20 +22,15 @@
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
   ObservePolicy OnHidden(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnParseStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstMeaningfulPaintInMainFrameDocument(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnPageInteractive(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
  private:
   // The ui transition for the committed navigation.
diff --git a/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer.cc
index cb1c33c1..ecbb421 100644
--- a/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer.cc
@@ -120,14 +120,13 @@
 }
 
 void SecurityStatePageLoadMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
-  if (!extra_info.did_commit)
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  if (!GetDelegate().DidCommit())
     return;
 
   if (engagement_service_) {
     double final_engagement_score =
-        engagement_service_->GetScore(extra_info.url);
+        engagement_service_->GetScore(GetDelegate().GetUrl());
     // Round the engagement score down to the closest multiple of 10 to decrease
     // the granularity of the UKM collection.
     int64_t coarse_engagement_score =
@@ -156,9 +155,10 @@
   }
 
   base::UmaHistogramEnumeration(
-      security_state::GetSecurityLevelHistogramName(
-          kPageEndReasonPrefix, current_security_level_),
-      extra_info.page_end_reason, page_load_metrics::PAGE_END_REASON_COUNT);
+      security_state::GetSecurityLevelHistogramName(kPageEndReasonPrefix,
+                                                    current_security_level_),
+      GetDelegate().GetPageEndReason(),
+      page_load_metrics::PAGE_END_REASON_COUNT);
   base::UmaHistogramCustomTimes(
       security_state::GetSecurityLevelHistogramName(kTimeOnPagePrefix,
                                                     current_security_level_),
diff --git a/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer.h
index 17b460e2..69fd2ff7 100644
--- a/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer.h
@@ -54,8 +54,7 @@
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
   void OnComplete(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
   // content::WebContentsObserver:
   void DidChangeVisibleSecurityState() override;
diff --git a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.cc
index 8dfdcbe..73b5cbc 100644
--- a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.cc
@@ -6,6 +6,7 @@
 
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_observer_delegate.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
 #include "net/http/http_response_headers.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
@@ -124,13 +125,6 @@
 
 namespace {
 
-bool IsServiceWorkerControlled(
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  return (info.main_frame_metadata.behavior_flags &
-          blink::WebLoadingBehaviorFlag::
-              kWebLoadingBehaviorServiceWorkerControlled) != 0;
-}
-
 bool IsInboxSite(const GURL& url) {
   return url.host_piece() == "inbox.google.com";
 }
@@ -158,9 +152,8 @@
 }
 
 void ServiceWorkerPageLoadMetricsObserver::OnFirstPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
-  if (!IsServiceWorkerControlled(extra_info) ||
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  if (!IsServiceWorkerControlled() ||
       !page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_paint, GetDelegate())) {
     return;
@@ -170,12 +163,11 @@
 }
 
 void ServiceWorkerPageLoadMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  if (!IsServiceWorkerControlled(info)) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  if (!IsServiceWorkerControlled()) {
     if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
             timing.paint_timing->first_contentful_paint, GetDelegate()) ||
-        !page_load_metrics::IsGoogleSearchResultUrl(info.url)) {
+        !page_load_metrics::IsGoogleSearchResultUrl(GetDelegate().GetUrl())) {
       return;
     }
     PAGE_LOAD_HISTOGRAM(
@@ -214,7 +206,7 @@
     }
   }
 
-  if (IsInboxSite(info.url)) {
+  if (IsInboxSite(GetDelegate().GetUrl())) {
     PAGE_LOAD_HISTOGRAM(
         internal::kHistogramServiceWorkerFirstContentfulPaintInbox,
         timing.paint_timing->first_contentful_paint.value());
@@ -222,7 +214,8 @@
         internal::kHistogramServiceWorkerParseStartToFirstContentfulPaintInbox,
         timing.paint_timing->first_contentful_paint.value() -
             timing.parse_timing->parse_start.value());
-  } else if (page_load_metrics::IsGoogleSearchResultUrl(info.url)) {
+  } else if (page_load_metrics::IsGoogleSearchResultUrl(
+                 GetDelegate().GetUrl())) {
     PAGE_LOAD_HISTOGRAM(
         internal::kHistogramServiceWorkerFirstContentfulPaintSearch,
         timing.paint_timing->first_contentful_paint.value());
@@ -235,14 +228,13 @@
 
 void ServiceWorkerPageLoadMetricsObserver::
     OnFirstMeaningfulPaintInMainFrameDocument(
-        const page_load_metrics::mojom::PageLoadTiming& timing,
-        const page_load_metrics::PageLoadExtraInfo& info) {
+        const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_meaningful_paint, GetDelegate())) {
     return;
   }
-  if (!IsServiceWorkerControlled(info)) {
-    if (!page_load_metrics::IsGoogleSearchResultUrl(info.url))
+  if (!IsServiceWorkerControlled()) {
+    if (!page_load_metrics::IsGoogleSearchResultUrl(GetDelegate().GetUrl()))
       return;
     PAGE_LOAD_HISTOGRAM(
         internal::kHistogramNoServiceWorkerFirstMeaningfulPaintSearch,
@@ -261,7 +253,7 @@
       timing.paint_timing->first_meaningful_paint.value() -
           timing.parse_timing->parse_start.value());
 
-  if (IsInboxSite(info.url)) {
+  if (IsInboxSite(GetDelegate().GetUrl())) {
     PAGE_LOAD_HISTOGRAM(
         internal::kHistogramServiceWorkerFirstMeaningfulPaintInbox,
         timing.paint_timing->first_meaningful_paint.value());
@@ -269,7 +261,8 @@
         internal::kHistogramServiceWorkerParseStartToFirstMeaningfulPaintInbox,
         timing.paint_timing->first_meaningful_paint.value() -
             timing.parse_timing->parse_start.value());
-  } else if (page_load_metrics::IsGoogleSearchResultUrl(info.url)) {
+  } else if (page_load_metrics::IsGoogleSearchResultUrl(
+                 GetDelegate().GetUrl())) {
     PAGE_LOAD_HISTOGRAM(
         internal::kHistogramServiceWorkerFirstMeaningfulPaintSearch,
         timing.paint_timing->first_meaningful_paint.value());
@@ -281,15 +274,14 @@
 }
 
 void ServiceWorkerPageLoadMetricsObserver::OnDomContentLoadedEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.document_timing->dom_content_loaded_event_start,
           GetDelegate())) {
     return;
   }
-  if (!IsServiceWorkerControlled(info)) {
-    if (!page_load_metrics::IsGoogleSearchResultUrl(info.url))
+  if (!IsServiceWorkerControlled()) {
+    if (!page_load_metrics::IsGoogleSearchResultUrl(GetDelegate().GetUrl()))
       return;
     PAGE_LOAD_HISTOGRAM(
         internal::kHistogramNoServiceWorkerDomContentLoadedSearch,
@@ -299,11 +291,12 @@
   PAGE_LOAD_HISTOGRAM(
       internal::kHistogramServiceWorkerDomContentLoaded,
       timing.document_timing->dom_content_loaded_event_start.value());
-  if (IsInboxSite(info.url)) {
+  if (IsInboxSite(GetDelegate().GetUrl())) {
     PAGE_LOAD_HISTOGRAM(
         internal::kHistogramServiceWorkerDomContentLoadedInbox,
         timing.document_timing->dom_content_loaded_event_start.value());
-  } else if (page_load_metrics::IsGoogleSearchResultUrl(info.url)) {
+  } else if (page_load_metrics::IsGoogleSearchResultUrl(
+                 GetDelegate().GetUrl())) {
     PAGE_LOAD_HISTOGRAM(
         internal::kHistogramServiceWorkerDomContentLoadedSearch,
         timing.document_timing->dom_content_loaded_event_start.value());
@@ -311,13 +304,12 @@
 }
 
 void ServiceWorkerPageLoadMetricsObserver::OnLoadEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.document_timing->load_event_start, GetDelegate()))
     return;
-  if (!IsServiceWorkerControlled(info)) {
-    if (!page_load_metrics::IsGoogleSearchResultUrl(info.url))
+  if (!IsServiceWorkerControlled()) {
+    if (!page_load_metrics::IsGoogleSearchResultUrl(GetDelegate().GetUrl()))
       return;
     PAGE_LOAD_HISTOGRAM(internal::kHistogramNoServiceWorkerLoadSearch,
                         timing.document_timing->load_event_start.value());
@@ -325,19 +317,19 @@
   }
   PAGE_LOAD_HISTOGRAM(internal::kHistogramServiceWorkerLoad,
                       timing.document_timing->load_event_start.value());
-  if (IsInboxSite(info.url)) {
+  if (IsInboxSite(GetDelegate().GetUrl())) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramServiceWorkerLoadInbox,
                         timing.document_timing->load_event_start.value());
-  } else if (page_load_metrics::IsGoogleSearchResultUrl(info.url)) {
+  } else if (page_load_metrics::IsGoogleSearchResultUrl(
+                 GetDelegate().GetUrl())) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramServiceWorkerLoadSearch,
                         timing.document_timing->load_event_start.value());
   }
 }
 
 void ServiceWorkerPageLoadMetricsObserver::OnFirstInputInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  if (!IsServiceWorkerControlled(info))
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  if (!IsServiceWorkerControlled())
     return;
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.interactive_timing->first_input_timestamp, GetDelegate())) {
@@ -353,9 +345,8 @@
 }
 
 void ServiceWorkerPageLoadMetricsObserver::OnParseStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  if (!IsServiceWorkerControlled(info))
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  if (!IsServiceWorkerControlled())
     return;
 
   // TODO(falken): It may be cleaner to record page transition in OnCommit() but
@@ -373,10 +364,11 @@
     PAGE_LOAD_HISTOGRAM(internal::kHistogramServiceWorkerParseStart,
                         timing.parse_timing->parse_start.value());
 
-    if (IsInboxSite(info.url)) {
+    if (IsInboxSite(GetDelegate().GetUrl())) {
       PAGE_LOAD_HISTOGRAM(internal::kHistogramServiceWorkerParseStartInbox,
                           timing.parse_timing->parse_start.value());
-    } else if (page_load_metrics::IsGoogleSearchResultUrl(info.url)) {
+    } else if (page_load_metrics::IsGoogleSearchResultUrl(
+                   GetDelegate().GetUrl())) {
       PAGE_LOAD_HISTOGRAM(internal::kHistogramServiceWorkerParseStartSearch,
                           timing.parse_timing->parse_start.value());
     }
@@ -398,11 +390,16 @@
 
 void ServiceWorkerPageLoadMetricsObserver::OnLoadingBehaviorObserved(
     content::RenderFrameHost* rfh,
-    int behavior_flags,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  if (!IsServiceWorkerControlled(info) || logged_ukm_event_)
+    int behavior_flags) {
+  if (!IsServiceWorkerControlled() || logged_ukm_event_)
     return;
-  ukm::builders::PageLoad_ServiceWorkerControlled(info.source_id)
+  ukm::builders::PageLoad_ServiceWorkerControlled(GetDelegate().GetSourceId())
       .Record(ukm::UkmRecorder::Get());
   logged_ukm_event_ = true;
 }
+
+bool ServiceWorkerPageLoadMetricsObserver::IsServiceWorkerControlled() {
+  return (GetDelegate().GetMainFrameMetadata().behavior_flags &
+          blink::WebLoadingBehaviorFlag::
+              kWebLoadingBehaviorServiceWorkerControlled) != 0;
+}
diff --git a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h
index 02dfe80..8f38e39 100644
--- a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h
@@ -68,32 +68,25 @@
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
   void OnFirstInputInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnParseStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstMeaningfulPaintInMainFrameDocument(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnDomContentLoadedEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnLoadEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
-  void OnLoadingBehaviorObserved(
-      content::RenderFrameHost* rfh,
-      int behavior_flags,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnLoadingBehaviorObserved(content::RenderFrameHost* rfh,
+                                 int behavior_flags) override;
 
  private:
+  bool IsServiceWorkerControlled();
+
   ui::PageTransition transition_ = ui::PAGE_TRANSITION_LINK;
   bool was_no_store_main_resource_ = false;
   bool logged_ukm_event_ = false;
diff --git a/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.cc
index b6b4ae8..aba5f17 100644
--- a/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.cc
@@ -63,8 +63,7 @@
 }
 
 void SessionRestorePageLoadMetricsObserver::OnFirstPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_paint.value(), GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(
@@ -75,7 +74,7 @@
     // is no need to record again in FCP or FMP, because FP comes first.
     ukm::builders::
         TabManager_Experimental_SessionRestore_ForegroundTab_PageLoad(
-            extra_info.source_id)
+            GetDelegate().GetSourceId())
             .SetSessionRestoreTabCount(
                 g_browser_process->GetTabManager()->restored_tab_count())
             .SetSystemTabCount(
@@ -85,8 +84,7 @@
 }
 
 void SessionRestorePageLoadMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_contentful_paint.value(), GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(
@@ -97,8 +95,7 @@
 
 void SessionRestorePageLoadMetricsObserver::
     OnFirstMeaningfulPaintInMainFrameDocument(
-        const page_load_metrics::mojom::PageLoadTiming& timing,
-        const page_load_metrics::PageLoadExtraInfo& extra_info) {
+        const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_meaningful_paint.value(), GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(
diff --git a/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.h
index c786b03..ce07083 100644
--- a/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.h
@@ -31,14 +31,11 @@
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
   void OnFirstPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstMeaningfulPaintInMainFrameDocument(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SessionRestorePageLoadMetricsObserver);
diff --git a/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.cc
index 37448b2..d076d5f 100644
--- a/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.cc
@@ -85,8 +85,7 @@
 }
 
 void SignedExchangePageLoadMetricsObserver::OnFirstPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_paint, GetDelegate())) {
     return;
@@ -96,8 +95,7 @@
 }
 
 void SignedExchangePageLoadMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_contentful_paint, GetDelegate())) {
     return;
@@ -112,8 +110,7 @@
 
 void SignedExchangePageLoadMetricsObserver::
     OnFirstMeaningfulPaintInMainFrameDocument(
-        const page_load_metrics::mojom::PageLoadTiming& timing,
-        const page_load_metrics::PageLoadExtraInfo& info) {
+        const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_meaningful_paint, GetDelegate())) {
     return;
@@ -127,8 +124,7 @@
 }
 
 void SignedExchangePageLoadMetricsObserver::OnDomContentLoadedEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.document_timing->dom_content_loaded_event_start,
           GetDelegate())) {
@@ -141,8 +137,7 @@
 }
 
 void SignedExchangePageLoadMetricsObserver::OnLoadEventStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.document_timing->load_event_start, GetDelegate())) {
     return;
@@ -153,8 +148,7 @@
 }
 
 void SignedExchangePageLoadMetricsObserver::OnFirstInputInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.interactive_timing->first_input_timestamp, GetDelegate())) {
     return;
@@ -183,8 +177,7 @@
 }
 
 void SignedExchangePageLoadMetricsObserver::OnParseStart(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.parse_timing->parse_start, GetDelegate())) {
     return;
diff --git a/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h
index abed59a..cecb8c1f 100644
--- a/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h
@@ -58,26 +58,19 @@
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
   void OnFirstInputInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnParseStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFirstMeaningfulPaintInMainFrameDocument(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnDomContentLoadedEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnLoadEventStart(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
  private:
   // True iff the page main resource was served from disk cache.
diff --git a/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.cc
index 0ce6cc7..b7edc2e 100644
--- a/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.cc
@@ -56,21 +56,19 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 TabRestorePageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   // FlushMetricsOnAppEnterBackground is invoked on Android in cases where the
   // app is about to be backgrounded, as part of the Activity.onPause()
   // flow. After this method is invoked, Chrome may be killed without further
   // notification, so we record final metrics collected up to this point.
-  if (info.did_commit) {
+  if (GetDelegate().DidCommit()) {
     RecordByteHistograms();
   }
   return STOP_OBSERVING;
 }
 
 void TabRestorePageLoadMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   RecordByteHistograms();
 }
 
diff --git a/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.h
index b9f405e..6605e2d0 100644
--- a/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.h
@@ -23,8 +23,8 @@
   ~TabRestorePageLoadMetricsObserver() override;
 
   // page_load_metrics::PageLoadMetricsObserver:
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
+  void OnComplete(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   page_load_metrics::PageLoadMetricsObserver::ObservePolicy OnStart(
       content::NavigationHandle* navigation_handle,
       const GURL& currently_committed_url,
@@ -35,8 +35,7 @@
           resources) override;
   page_load_metrics::PageLoadMetricsObserver::ObservePolicy
   FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
  protected:
   // Whether the navigation handle is a tab restore.
diff --git a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.cc
index 19c5322..7791e23d 100644
--- a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.cc
@@ -36,8 +36,7 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 ThirdPartyMetricsObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   // The browser may come back, but there is no guarantee. To be safe, record
   // what we have now and ignore future changes to this navigation.
   RecordMetrics();
@@ -45,8 +44,7 @@
 }
 
 void ThirdPartyMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   RecordMetrics();
 }
 
diff --git a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h b/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h
index d58221a..a942d38d 100644
--- a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h
@@ -23,10 +23,9 @@
 
   // page_load_metrics::PageLoadMetricsObserver:
   ObservePolicy FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnComplete(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnCookiesRead(const GURL& url,
                      const GURL& first_party_url,
                      const net::CookieList& cookie_list,
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
index 5914dba..ec636aa5 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
@@ -170,42 +170,37 @@
 
 UkmPageLoadMetricsObserver::ObservePolicy
 UkmPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!was_hidden_) {
-    RecordPageLoadExtraInfoMetrics(info, base::TimeTicks::Now());
-    RecordTimingMetrics(timing, info);
+    RecordPageLoadMetrics(base::TimeTicks::Now());
+    RecordTimingMetrics(timing);
   }
-  ReportLayoutStability(info);
+  ReportLayoutStability();
   return STOP_OBSERVING;
 }
 
 UkmPageLoadMetricsObserver::ObservePolicy UkmPageLoadMetricsObserver::OnHidden(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!was_hidden_) {
-    RecordPageLoadExtraInfoMetrics(
-        info, base::TimeTicks() /* no app_background_time */);
-    RecordTimingMetrics(timing, info);
+    RecordPageLoadMetrics(base::TimeTicks() /* no app_background_time */);
+    RecordTimingMetrics(timing);
     was_hidden_ = true;
   }
   return CONTINUE_OBSERVING;
 }
 
 void UkmPageLoadMetricsObserver::OnFailedProvisionalLoad(
-    const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info) {
   if (was_hidden_)
     return;
-  RecordPageLoadExtraInfoMetrics(
-      extra_info, base::TimeTicks() /* no app_background_time */);
+  RecordPageLoadMetrics(base::TimeTicks() /* no app_background_time */);
 
   // Error codes have negative values, however we log net error code enum values
   // for UMA histograms using the equivalent positive value. For consistency in
   // UKM, we convert to a positive value here.
   int64_t net_error_code = static_cast<int64_t>(failed_load_info.error) * -1;
   DCHECK_GE(net_error_code, 0);
-  ukm::builders::PageLoad(extra_info.source_id)
+  ukm::builders::PageLoad(GetDelegate().GetSourceId())
       .SetNet_ErrorCode_OnFailedProvisionalLoad(net_error_code)
       .SetPageTiming_NavigationToFailedProvisionalLoad(
           failed_load_info.time_to_failed_provisional_load.InMilliseconds())
@@ -213,14 +208,12 @@
 }
 
 void UkmPageLoadMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (!was_hidden_) {
-    RecordPageLoadExtraInfoMetrics(
-        info, base::TimeTicks() /* no app_background_time */);
-    RecordTimingMetrics(timing, info);
+    RecordPageLoadMetrics(base::TimeTicks() /* no app_background_time */);
+    RecordTimingMetrics(timing);
   }
-  ReportLayoutStability(info);
+  ReportLayoutStability();
 }
 
 void UkmPageLoadMetricsObserver::OnResourceDataUseObserved(
@@ -252,18 +245,17 @@
 }
 
 void UkmPageLoadMetricsObserver::RecordTimingMetrics(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  ukm::builders::PageLoad builder(info.source_id);
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  ukm::builders::PageLoad builder(GetDelegate().GetSourceId());
 
   base::Optional<int64_t> rounded_site_engagement_score =
-      GetRoundedSiteEngagementScore(info);
+      GetRoundedSiteEngagementScore();
   if (rounded_site_engagement_score) {
     builder.SetSiteEngagementScore(rounded_site_engagement_score.value());
   }
 
   base::Optional<bool> third_party_cookie_blocking_enabled =
-      GetThirdPartyCookieBlockingEnabled(info);
+      GetThirdPartyCookieBlockingEnabled();
   if (third_party_cookie_blocking_enabled) {
     builder.SetThirdPartyCookieBlockingEnabledForSite(
         third_party_cookie_blocking_enabled.value());
@@ -409,10 +401,9 @@
   builder.Record(ukm::UkmRecorder::Get());
 }
 
-void UkmPageLoadMetricsObserver::RecordPageLoadExtraInfoMetrics(
-    const page_load_metrics::PageLoadExtraInfo& info,
+void UkmPageLoadMetricsObserver::RecordPageLoadMetrics(
     base::TimeTicks app_background_time) {
-  ukm::builders::PageLoad builder(info.source_id);
+  ukm::builders::PageLoad builder(GetDelegate().GetSourceId());
   base::Optional<base::TimeDelta> foreground_duration =
       page_load_metrics::GetInitialForegroundDuration(GetDelegate(),
                                                       app_background_time);
@@ -423,11 +414,11 @@
 
   bool is_user_initiated_navigation =
       // All browser initiated page loads are user-initiated.
-      info.user_initiated_info.browser_initiated ||
+      GetDelegate().GetUserInitiatedInfo().browser_initiated ||
 
       // Renderer-initiated navigations are user-initiated if there is an
       // associated input event.
-      info.user_initiated_info.user_input_event;
+      GetDelegate().GetUserInitiatedInfo().user_input_event;
 
   builder.SetExperimental_Navigation_UserInitiated(
       is_user_initiated_navigation);
@@ -461,19 +452,20 @@
   }
   // page_transition_ fits in a uint32_t, so we can safely cast to int64_t.
   builder.SetNavigation_PageTransition(static_cast<int64_t>(page_transition_));
-  // info.page_end_reason fits in a uint32_t, so we can safely cast to int64_t.
+  // GetDelegate().GetPageEndReason() fits in a uint32_t, so we can safely cast
+  // to int64_t.
   builder.SetNavigation_PageEndReason(
-      static_cast<int64_t>(info.page_end_reason));
-  if (info.did_commit && was_cached_) {
+      static_cast<int64_t>(GetDelegate().GetPageEndReason()));
+  if (GetDelegate().DidCommit() && was_cached_) {
     builder.SetWasCached(1);
   }
-  if (info.did_commit && is_signed_exchange_inner_response_) {
+  if (GetDelegate().DidCommit() && is_signed_exchange_inner_response_) {
     builder.SetIsSignedExchangeInnerResponse(1);
   }
-  if (info.did_commit && navigation_is_cross_process_) {
+  if (GetDelegate().DidCommit() && navigation_is_cross_process_) {
     builder.SetIsCrossProcessNavigation(navigation_is_cross_process_);
   }
-  if (info.did_commit) {
+  if (GetDelegate().DidCommit()) {
     builder.SetNavigationEntryOffset(navigation_entry_offset_);
     builder.SetMainDocumentSequenceNumber(main_document_sequence_number_);
   }
@@ -564,30 +556,31 @@
   }
 }
 
-void UkmPageLoadMetricsObserver::ReportLayoutStability(
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  ukm::builders::PageLoad(info.source_id)
-      .SetLayoutInstability_CumulativeShiftScore(
-          LayoutShiftUkmValue(info.page_render_data.layout_shift_score))
-      .SetLayoutInstability_CumulativeShiftScore_MainFrame(
-          LayoutShiftUkmValue(info.main_frame_render_data.layout_shift_score))
+void UkmPageLoadMetricsObserver::ReportLayoutStability() {
+  ukm::builders::PageLoad(GetDelegate().GetSourceId())
+      .SetLayoutInstability_CumulativeShiftScore(LayoutShiftUkmValue(
+          GetDelegate().GetPageRenderData().layout_shift_score))
+      .SetLayoutInstability_CumulativeShiftScore_MainFrame(LayoutShiftUkmValue(
+          GetDelegate().GetMainFrameRenderData().layout_shift_score))
       .SetLayoutInstability_CumulativeShiftScore_MainFrame_BeforeInputOrScroll(
-          LayoutShiftUkmValue(info.main_frame_render_data
+          LayoutShiftUkmValue(GetDelegate()
+                                  .GetMainFrameRenderData()
                                   .layout_shift_score_before_input_or_scroll))
       .Record(ukm::UkmRecorder::Get());
 
   UMA_HISTOGRAM_COUNTS_100(
       "PageLoad.LayoutInstability.CumulativeShiftScore",
-      LayoutShiftUmaValue(info.page_render_data.layout_shift_score));
+      LayoutShiftUmaValue(
+          GetDelegate().GetPageRenderData().layout_shift_score));
 
   UMA_HISTOGRAM_COUNTS_100(
       "PageLoad.LayoutInstability.CumulativeShiftScore.MainFrame",
-      LayoutShiftUmaValue(info.main_frame_render_data.layout_shift_score));
+      LayoutShiftUmaValue(
+          GetDelegate().GetMainFrameRenderData().layout_shift_score));
 }
 
 base::Optional<int64_t>
-UkmPageLoadMetricsObserver::GetRoundedSiteEngagementScore(
-    const page_load_metrics::PageLoadExtraInfo& info) const {
+UkmPageLoadMetricsObserver::GetRoundedSiteEngagementScore() const {
   if (!browser_context_)
     return base::nullopt;
 
@@ -598,8 +591,8 @@
   // UKM privacy requires the engagement score be rounded to nearest
   // value of 10.
   int64_t rounded_document_engagement_score =
-      static_cast<int>(
-          std::roundf(engagement_service->GetScore(info.url) / 10.0)) *
+      static_cast<int>(std::roundf(
+          engagement_service->GetScore(GetDelegate().GetUrl()) / 10.0)) *
       10;
 
   DCHECK(rounded_document_engagement_score >= 0 &&
@@ -610,8 +603,7 @@
 }
 
 base::Optional<bool>
-UkmPageLoadMetricsObserver::GetThirdPartyCookieBlockingEnabled(
-    const page_load_metrics::PageLoadExtraInfo& info) const {
+UkmPageLoadMetricsObserver::GetThirdPartyCookieBlockingEnabled() const {
   if (!browser_context_)
     return base::nullopt;
 
@@ -623,13 +615,12 @@
   if (!cookie_settings->IsCookieControlsEnabled())
     return base::nullopt;
 
-  return !cookie_settings->IsThirdPartyAccessAllowed(info.url);
+  return !cookie_settings->IsThirdPartyAccessAllowed(GetDelegate().GetUrl());
 }
 
 void UkmPageLoadMetricsObserver::OnTimingUpdate(
     content::RenderFrameHost* subframe_rfh,
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   largest_contentful_paint_handler_.RecordTiming(timing.paint_timing,
                                                  subframe_rfh);
   bool loading_enabled;
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.h
index 62184bc..2dcdaa4 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.h
@@ -57,19 +57,17 @@
       const std::string& mime_type) const override;
 
   ObservePolicy FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
   ObservePolicy OnHidden(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
   void OnFailedProvisionalLoad(
-      const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info)
+      override;
 
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
+  void OnComplete(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
   void OnResourceDataUseObserved(
       content::RenderFrameHost* content,
@@ -81,8 +79,7 @@
 
   void OnTimingUpdate(
       content::RenderFrameHost* subframe_rfh,
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
   void OnCpuTimingUpdate(
       content::RenderFrameHost* subframe_rfh,
@@ -96,33 +93,29 @@
   // Records page load timing related metrics available in PageLoadTiming, such
   // as first contentful paint.
   void RecordTimingMetrics(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info);
+      const page_load_metrics::mojom::PageLoadTiming& timing);
 
-  // Records metrics based on the PageLoadExtraInfo struct, as well as updating
-  // the URL. |app_background_time| should be set to a timestamp if the app was
-  // backgrounded, otherwise it should be set to a null TimeTicks.
-  void RecordPageLoadExtraInfoMetrics(
-      const page_load_metrics::PageLoadExtraInfo& info,
-      base::TimeTicks app_background_time);
+  // Records metrics based on the page load information exposed by the observer
+  // delegate, as well as updating the URL. |app_background_time| should be set
+  // to a timestamp if the app was backgrounded, otherwise it should be set to
+  // a null TimeTicks.
+  void RecordPageLoadMetrics(base::TimeTicks app_background_time);
 
   // Adds main resource timing metrics to |builder|.
   void ReportMainResourceTimingMetrics(
       const page_load_metrics::mojom::PageLoadTiming& timing,
       ukm::builders::PageLoad* builder);
 
-  void ReportLayoutStability(const page_load_metrics::PageLoadExtraInfo& info);
+  void ReportLayoutStability();
 
   // Captures the site engagement score for the commited URL and
   // returns the score rounded to the nearest 10.
-  base::Optional<int64_t> GetRoundedSiteEngagementScore(
-      const page_load_metrics::PageLoadExtraInfo& info) const;
+  base::Optional<int64_t> GetRoundedSiteEngagementScore() const;
 
   // Returns whether third party cookie blocking is enabled for the committed
   // URL. This is only recorded for users who have prefs::kCookieControlsEnabled
   // set to true.
-  base::Optional<bool> GetThirdPartyCookieBlockingEnabled(
-      const page_load_metrics::PageLoadExtraInfo& info) const;
+  base::Optional<bool> GetThirdPartyCookieBlockingEnabled() const;
 
   // Records the metrics for the nostate prefetch to an event with UKM source ID
   // |source_id|.
diff --git a/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.cc
index 2199fae..3d37e56d 100644
--- a/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.cc
@@ -129,8 +129,7 @@
 
 void UseCounterPageLoadMetricsObserver::OnFeaturesUsageObserved(
     content::RenderFrameHost* rfh,
-    const Features& features,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const Features& features) {
   for (WebFeature feature : features.features) {
     // Verify that kPageVisits is observed at most once per observer.
     if (feature == WebFeature::kPageVisits) {
@@ -217,29 +216,26 @@
 }
 
 void UseCounterPageLoadMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   RecordUkmFeatures(GetAllowedUkmFeatures(), features_recorded_,
                     main_frame_features_recorded_, &ukm_features_recorded_,
-                    extra_info.source_id);
+                    GetDelegate().GetSourceId());
 }
 
 void UseCounterPageLoadMetricsObserver::OnFailedProvisionalLoad(
     const page_load_metrics::FailedProvisionalLoadInfo&
-        failed_provisional_load_info,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+        failed_provisional_load_info) {
   RecordUkmFeatures(GetAllowedUkmFeatures(), features_recorded_,
                     main_frame_features_recorded_, &ukm_features_recorded_,
-                    extra_info.source_id);
+                    GetDelegate().GetSourceId());
 }
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 UseCounterPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   RecordUkmFeatures(GetAllowedUkmFeatures(), features_recorded_,
                     main_frame_features_recorded_, &ukm_features_recorded_,
-                    extra_info.source_id);
+                    GetDelegate().GetSourceId());
   return CONTINUE_OBSERVING;
 }
 
diff --git a/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.h
index 95d5580..01a01c83 100644
--- a/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.h
@@ -35,18 +35,14 @@
                          ukm::SourceId source_id) override;
   void OnFeaturesUsageObserved(
       content::RenderFrameHost* rfh,
-      const page_load_metrics::mojom::PageLoadFeatures&,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadFeatures&) override;
   void OnComplete(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   void OnFailedProvisionalLoad(
       const page_load_metrics::FailedProvisionalLoadInfo&
-          failed_provisional_load_info,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+          failed_provisional_load_info) override;
   ObservePolicy FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
   ObservePolicy ShouldObserveMimeType(
       const std::string& mime_type) const override;
 
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/page_load_metrics_observer.cc
index a8fda9b3..17bcf64b 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_observer.cc
@@ -8,61 +8,6 @@
 
 namespace page_load_metrics {
 
-PageLoadExtraInfo::PageLoadExtraInfo(
-    base::TimeTicks navigation_start,
-    const base::Optional<base::TimeDelta>& first_background_time,
-    const base::Optional<base::TimeDelta>& first_foreground_time,
-    bool started_in_foreground,
-    UserInitiatedInfo user_initiated_info,
-    const GURL& url,
-    const GURL& start_url,
-    bool did_commit,
-    PageEndReason page_end_reason,
-    UserInitiatedInfo page_end_user_initiated_info,
-    const base::Optional<base::TimeDelta>& page_end_time,
-    const mojom::PageLoadMetadata& main_frame_metadata,
-    const mojom::PageLoadMetadata& subframe_metadata,
-    const PageRenderData& page_render_data,
-    const PageRenderData& main_frame_render_data,
-    ukm::SourceId source_id)
-    : navigation_start(navigation_start),
-      first_background_time(first_background_time),
-      first_foreground_time(first_foreground_time),
-      started_in_foreground(started_in_foreground),
-      user_initiated_info(user_initiated_info),
-      url(url),
-      start_url(start_url),
-      did_commit(did_commit),
-      page_end_reason(page_end_reason),
-      page_end_user_initiated_info(page_end_user_initiated_info),
-      page_end_time(page_end_time),
-      main_frame_metadata(main_frame_metadata),
-      subframe_metadata(subframe_metadata),
-      page_render_data(page_render_data),
-      main_frame_render_data(main_frame_render_data),
-      source_id(source_id) {}
-
-PageLoadExtraInfo::PageLoadExtraInfo(const PageLoadExtraInfo& other) = default;
-
-PageLoadExtraInfo::~PageLoadExtraInfo() {}
-
-// static
-PageLoadExtraInfo PageLoadExtraInfo::CreateForTesting(
-    const GURL& url,
-    bool started_in_foreground) {
-  return PageLoadExtraInfo(
-      base::TimeTicks::Now() /* navigation_start */,
-      base::Optional<base::TimeDelta>() /* first_background_time */,
-      base::Optional<base::TimeDelta>() /* first_foreground_time */,
-      started_in_foreground /* started_in_foreground */,
-      UserInitiatedInfo::BrowserInitiated(), url, url, true /* did_commit */,
-      page_load_metrics::END_NONE,
-      page_load_metrics::UserInitiatedInfo::NotUserInitiated(),
-      base::TimeDelta(), page_load_metrics::mojom::PageLoadMetadata(),
-      page_load_metrics::mojom::PageLoadMetadata(), PageRenderData(),
-      PageRenderData(), 0 /* source_id */);
-}
-
 ExtraRequestCompleteInfo::ExtraRequestCompleteInfo(
     const GURL& url,
     const net::IPEndPoint& remote_endpoint,
@@ -132,8 +77,7 @@
 }
 
 PageLoadMetricsObserver::ObservePolicy PageLoadMetricsObserver::OnHidden(
-    const mojom::PageLoadTiming& timing,
-    const PageLoadExtraInfo& extra_info) {
+    const mojom::PageLoadTiming& timing) {
   return CONTINUE_OBSERVING;
 }
 
@@ -143,8 +87,7 @@
 
 PageLoadMetricsObserver::ObservePolicy
 PageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
-    const mojom::PageLoadTiming& timing,
-    const PageLoadExtraInfo& extra_info) {
+    const mojom::PageLoadTiming& timing) {
   return CONTINUE_OBSERVING;
 }
 
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_observer.h b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
index a108009..9a74b517 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
@@ -95,107 +95,6 @@
   float layout_shift_score_before_input_or_scroll;
 };
 
-// TODO(crbug/939403): Migrate usage of this struct in favor of observer
-// delegate. New user's of this data should use
-// PageLoadMetricsObserverDelegate's API instead.
-struct PageLoadExtraInfo {
-  PageLoadExtraInfo(
-      base::TimeTicks navigation_start,
-      const base::Optional<base::TimeDelta>& first_background_time,
-      const base::Optional<base::TimeDelta>& first_foreground_time,
-      bool started_in_foreground,
-      UserInitiatedInfo user_initiated_info,
-      const GURL& url,
-      const GURL& start_url,
-      bool did_commit,
-      PageEndReason page_end_reason,
-      UserInitiatedInfo page_end_user_initiated_info,
-      const base::Optional<base::TimeDelta>& page_end_time,
-      const mojom::PageLoadMetadata& main_frame_metadata,
-      const mojom::PageLoadMetadata& subframe_metadata,
-      const PageRenderData& page_render_data,
-      const PageRenderData& main_frame_render_data,
-      ukm::SourceId source_id);
-
-  // Simplified version of the constructor, intended for use in tests.
-  static PageLoadExtraInfo CreateForTesting(const GURL& url,
-                                            bool started_in_foreground);
-
-  PageLoadExtraInfo(const PageLoadExtraInfo& other);
-
-  ~PageLoadExtraInfo();
-
-  // The time the navigation was initiated.
-  const base::TimeTicks navigation_start;
-
-  // The first time that the page was backgrounded since the navigation started.
-  const base::Optional<base::TimeDelta> first_background_time;
-
-  // The first time that the page was foregrounded since the navigation started.
-  const base::Optional<base::TimeDelta> first_foreground_time;
-
-  // True if the page load started in the foreground.
-  const bool started_in_foreground;
-
-  // Whether the page load was initiated by a user.
-  const UserInitiatedInfo user_initiated_info;
-
-  // Most recent URL for this page. Can be updated at navigation start, upon
-  // redirection, and at commit time.
-  const GURL url;
-
-  // The URL that started the navigation, before redirects.
-  const GURL start_url;
-
-  // Whether the navigation for this page load committed.
-  const bool did_commit;
-
-  // The reason the page load ended. If the page is still active,
-  // |page_end_reason| will be |END_NONE|. |page_end_time| contains the duration
-  // of time until the cause of the page end reason was encountered.
-  const PageEndReason page_end_reason;
-
-  // Whether the end reason for this page load was user initiated. For example,
-  // if
-  // this page load was ended due to a new navigation, this field tracks whether
-  // that new navigation was user-initiated. This field is only useful if this
-  // page load's end reason is a value other than END_NONE. Note that this
-  // value is currently experimental, and is subject to change. In particular,
-  // this field is not currently set for some end reasons, such as stop and
-  // close, since we don't yet have sufficient instrumentation to know if a stop
-  // or close was caused by a user action.
-  //
-  // TODO(csharrison): If more metadata for end reasons is needed we should
-  // provide a
-  // better abstraction. Note that this is an approximation.
-  UserInitiatedInfo page_end_user_initiated_info;
-
-  // Total lifetime of the page from the user's standpoint, starting at
-  // navigation start. The page lifetime ends when the first of the following
-  // events happen:
-  // * the load of the main resource fails
-  // * the page load is stopped
-  // * the tab hosting the page is closed
-  // * the render process hosting the page goes away
-  // * a new navigation which later commits is initiated in the same tab
-  // This field will not be set if the page is still active and hasn't yet
-  // finished.
-  const base::Optional<base::TimeDelta> page_end_time;
-
-  // Extra information supplied to the page load metrics system from the
-  // renderer for the main frame.
-  const mojom::PageLoadMetadata main_frame_metadata;
-
-  // PageLoadMetadata for subframes of the current page load.
-  const mojom::PageLoadMetadata subframe_metadata;
-
-  const PageRenderData page_render_data;
-  const PageRenderData main_frame_render_data;
-
-  // UKM SourceId for the current page load.
-  const ukm::SourceId source_id;
-};
-
 // Container for various information about a completed request within a page
 // load.
 struct ExtraRequestCompleteInfo {
@@ -342,8 +241,7 @@
   // that |navigation_handle| will be destroyed soon after this call. Don't
   // hold a reference to it.
   virtual void OnDidFinishSubFrameNavigation(
-      content::NavigationHandle* navigation_handle,
-      const PageLoadExtraInfo& extra_info) {}
+      content::NavigationHandle* navigation_handle) {}
 
   // OnCommitSameDocumentNavigation is triggered when a same-document navigation
   // commits within the main frame of the current page. Note that
@@ -355,8 +253,7 @@
   // OnHidden is triggered when a page leaves the foreground. It does not fire
   // when a foreground page is permanently closed; for that, listen to
   // OnComplete instead.
-  virtual ObservePolicy OnHidden(const mojom::PageLoadTiming& timing,
-                                 const PageLoadExtraInfo& extra_info);
+  virtual ObservePolicy OnHidden(const mojom::PageLoadTiming& timing);
 
   // OnShown is triggered when a page is brought to the foreground. It does not
   // fire when the page first loads; for that, listen for OnStart instead.
@@ -385,16 +282,14 @@
   //
   // If |subframe_rfh| is nullptr, the update took place in the main frame.
   virtual void OnTimingUpdate(content::RenderFrameHost* subframe_rfh,
-                              const mojom::PageLoadTiming& timing,
-                              const PageLoadExtraInfo& extra_info) {}
+                              const mojom::PageLoadTiming& timing) {}
 
   // OnRenderDataUpdate is triggered when an updated PageRenderData is available
   // at the subframe level. This method may be called multiple times over the
   // course of the page load.
   virtual void OnSubFrameRenderDataUpdate(
       content::RenderFrameHost* subframe_rfh,
-      const mojom::FrameRenderDataUpdate& render_data,
-      const PageLoadExtraInfo& extra_info) {}
+      const mojom::FrameRenderDataUpdate& render_data) {}
 
   // Triggered when an updated CpuTiming is available at the page or subframe
   // level. This method is intended for monitoring cpu usage and load across
@@ -405,55 +300,42 @@
   // OnUserInput is triggered when a new user input is passed in to
   // web_contents.
   virtual void OnUserInput(const blink::WebInputEvent& event,
-                           const mojom::PageLoadTiming& timing,
-                           const PageLoadExtraInfo& extra_info) {}
+                           const mojom::PageLoadTiming& timing) {}
 
   // The following methods are invoked at most once, when the timing for the
   // associated event first becomes available.
   virtual void OnDomContentLoadedEventStart(
-      const mojom::PageLoadTiming& timing,
-      const PageLoadExtraInfo& extra_info) {}
-  virtual void OnLoadEventStart(const mojom::PageLoadTiming& timing,
-                                const PageLoadExtraInfo& extra_info) {}
-  virtual void OnFirstLayout(const mojom::PageLoadTiming& timing,
-                             const PageLoadExtraInfo& extra_info) {}
-  virtual void OnParseStart(const mojom::PageLoadTiming& timing,
-                            const PageLoadExtraInfo& extra_info) {}
-  virtual void OnParseStop(const mojom::PageLoadTiming& timing,
-                           const PageLoadExtraInfo& extra_info) {}
+      const mojom::PageLoadTiming& timing) {}
+  virtual void OnLoadEventStart(const mojom::PageLoadTiming& timing) {}
+  virtual void OnFirstLayout(const mojom::PageLoadTiming& timing) {}
+  virtual void OnParseStart(const mojom::PageLoadTiming& timing) {}
+  virtual void OnParseStop(const mojom::PageLoadTiming& timing) {}
 
   // On*PaintInPage(...) are invoked when the first relevant paint in the page,
   // across all frames, is observed.
-  virtual void OnFirstPaintInPage(const mojom::PageLoadTiming& timing,
-                                  const PageLoadExtraInfo& extra_info) {}
-  virtual void OnFirstImagePaintInPage(const mojom::PageLoadTiming& timing,
-                                       const PageLoadExtraInfo& extra_info) {}
+  virtual void OnFirstPaintInPage(const mojom::PageLoadTiming& timing) {}
+  virtual void OnFirstImagePaintInPage(const mojom::PageLoadTiming& timing) {}
   virtual void OnFirstContentfulPaintInPage(
-      const mojom::PageLoadTiming& timing,
-      const PageLoadExtraInfo& extra_info) {}
+      const mojom::PageLoadTiming& timing) {}
 
   // Unlike other paint callbacks, OnFirstMeaningfulPaintInMainFrameDocument is
   // tracked per document, and is reported for the main frame document only.
   virtual void OnFirstMeaningfulPaintInMainFrameDocument(
-      const mojom::PageLoadTiming& timing,
-      const PageLoadExtraInfo& extra_info) {}
+      const mojom::PageLoadTiming& timing) {}
 
-  virtual void OnPageInteractive(const mojom::PageLoadTiming& timing,
-                                 const PageLoadExtraInfo& extra_info) {}
+  virtual void OnPageInteractive(const mojom::PageLoadTiming& timing) {}
 
-  virtual void OnFirstInputInPage(const mojom::PageLoadTiming& timing,
-                                  const PageLoadExtraInfo& extra_info) {}
+  virtual void OnFirstInputInPage(const mojom::PageLoadTiming& timing) {}
 
   // Invoked when there is an update to the loading behavior_flags in the given
   // frame.
   virtual void OnLoadingBehaviorObserved(content::RenderFrameHost* rfh,
-                                         int behavior_flags,
-                                         const PageLoadExtraInfo& extra_info) {}
+                                         int behavior_flags) {}
 
   // Invoked when new use counter features are observed across all frames.
-  virtual void OnFeaturesUsageObserved(content::RenderFrameHost* rfh,
-                                       const mojom::PageLoadFeatures& features,
-                                       const PageLoadExtraInfo& extra_info) {}
+  virtual void OnFeaturesUsageObserved(
+      content::RenderFrameHost* rfh,
+      const mojom::PageLoadFeatures& features) {}
 
   // Invoked when there is data use for loading a resource on the page
   // for a given render frame host. This only contains resources that have had
@@ -482,22 +364,21 @@
   // the application may be killed at any time after this method is invoked
   // without further notification. Note that this may be called both for
   // provisional loads as well as committed loads. Implementations that only
-  // want to track committed loads should check whether extra_info.committed_url
-  // is empty to determine if the load had committed. If the implementation
-  // returns CONTINUE_OBSERVING, this method may be called multiple times per
-  // observer, once for each time that the application enters the backround.
+  // want to track committed loads should check GetDelegate().DidCommit()
+  // to determine if the load had committed. If the implementation returns
+  // CONTINUE_OBSERVING, this method may be called multiple times per observer,
+  // once for each time that the application enters the background.
   //
   // The default implementation does nothing, and returns CONTINUE_OBSERVING.
   virtual ObservePolicy FlushMetricsOnAppEnterBackground(
-      const mojom::PageLoadTiming& timing,
-      const PageLoadExtraInfo& extra_info);
+      const mojom::PageLoadTiming& timing);
 
   // One of OnComplete or OnFailedProvisionalLoad is invoked for tracked page
   // loads, immediately before the observer is deleted. These callbacks will not
   // be invoked for page loads that did not meet the criteria for being tracked
   // at the time the navigation completed. The PageLoadTiming struct contains
-  // timing data and the PageLoadExtraInfo struct contains other useful data
-  // collected over the course of the page load. Most observers should not need
+  // timing data. Other useful data collected over the course of the page load
+  // is exposed by the observer delegate API. Most observers should not need
   // to implement these callbacks, and should implement the On* timing callbacks
   // instead.
 
@@ -506,14 +387,12 @@
   // also want to implement FlushMetricsOnAppEnterBackground, to avoid loss of
   // data if the application is killed while in the background (this happens
   // frequently on Android).
-  virtual void OnComplete(const mojom::PageLoadTiming& timing,
-                          const PageLoadExtraInfo& extra_info) {}
+  virtual void OnComplete(const mojom::PageLoadTiming& timing) {}
 
   // OnFailedProvisionalLoad is invoked for tracked page loads that did not
   // commit, immediately before the observer is deleted.
   virtual void OnFailedProvisionalLoad(
-      const FailedProvisionalLoadInfo& failed_provisional_load_info,
-      const PageLoadExtraInfo& extra_info) {}
+      const FailedProvisionalLoadInfo& failed_provisional_load_info) {}
 
   // Called whenever a request is loaded for this page load. This is restricted
   // to requests with HTTP or HTTPS only schemes.
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_observer_delegate.h b/chrome/browser/page_load_metrics/page_load_metrics_observer_delegate.h
index c30839f..ade561e9 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_observer_delegate.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_observer_delegate.h
@@ -30,25 +30,77 @@
 class PageLoadMetricsObserverDelegate {
  public:
   virtual content::WebContents* GetWebContents() const = 0;
+
+  // The time the navigation was initiated.
   virtual base::TimeTicks GetNavigationStart() const = 0;
+
+  // The first time that the page was backgrounded since the navigation started.
   virtual const base::Optional<base::TimeDelta>& GetFirstBackgroundTime()
       const = 0;
+
+  // The first time that the page was foregrounded since the navigation started.
   virtual const base::Optional<base::TimeDelta>& GetFirstForegroundTime()
       const = 0;
+
+  // True if the page load started in the foreground.
   virtual bool StartedInForeground() const = 0;
+
+  // Whether the page load was initiated by a user.
   virtual const UserInitiatedInfo& GetUserInitiatedInfo() const = 0;
+
+  // Most recent URL for this page. Can be updated at navigation start, upon
+  // redirection, and at commit time.
   virtual const GURL& GetUrl() const = 0;
+
+  // The URL that started the navigation, before redirects.
   virtual const GURL& GetStartUrl() const = 0;
+
+  // Whether the navigation for this page load committed.
   virtual bool DidCommit() const = 0;
+
+  // The reason the page load ended. If the page is still active,
+  // |page_end_reason| will be |END_NONE|. |page_end_time| contains the duration
+  // of time until the cause of the page end reason was encountered.
   virtual PageEndReason GetPageEndReason() const = 0;
+
+  // Whether the end reason for this page load was user initiated. For example,
+  // if
+  // this page load was ended due to a new navigation, this field tracks whether
+  // that new navigation was user-initiated. This field is only useful if this
+  // page load's end reason is a value other than END_NONE. Note that this
+  // value is currently experimental, and is subject to change. In particular,
+  // this field is not currently set for some end reasons, such as stop and
+  // close, since we don't yet have sufficient instrumentation to know if a stop
+  // or close was caused by a user action.
+  //
+  // TODO(csharrison): If more metadata for end reasons is needed we should
+  // provide a better abstraction. Note that this is an approximation.
   virtual const UserInitiatedInfo& GetPageEndUserInitiatedInfo() const = 0;
+
+  // Total lifetime of the page from the user's standpoint, starting at
+  // navigation start. The page lifetime ends when the first of the following
+  // events happen:
+  // * the load of the main resource fails
+  // * the page load is stopped
+  // * the tab hosting the page is closed
+  // * the render process hosting the page goes away
+  // * a new navigation which later commits is initiated in the same tab
+  // This field will not be set if the page is still active and hasn't yet
+  // finished.
   virtual base::Optional<base::TimeDelta> GetPageEndTime() const = 0;
+
+  // Extra information supplied to the page load metrics system from the
+  // renderer for the main frame.
   virtual const mojom::PageLoadMetadata& GetMainFrameMetadata() const = 0;
+
+  // PageLoadMetadata for subframes of the current page load.
   virtual const mojom::PageLoadMetadata& GetSubframeMetadata() const = 0;
   virtual const PageRenderData& GetPageRenderData() const = 0;
   virtual const PageRenderData& GetMainFrameRenderData() const = 0;
   virtual const ScopedVisibilityTracker& GetVisibilityTracker() const = 0;
   virtual const ResourceTracker& GetResourceTracker() const = 0;
+
+  // UKM SourceId for the current page load.
   virtual ukm::SourceId GetSourceId() const = 0;
 };
 
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.cc b/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.cc
index 54949ed..3a9314c 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.cc
@@ -95,13 +95,12 @@
 
 void PageLoadMetricsTestWaiter::OnTimingUpdated(
     content::RenderFrameHost* subframe_rfh,
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (ExpectationsSatisfied())
     return;
   const page_load_metrics::mojom::PageLoadMetadata& metadata =
-      subframe_rfh ? extra_info.subframe_metadata
-                   : extra_info.main_frame_metadata;
+      subframe_rfh ? GetDelegateForCommittedLoad().GetSubframeMetadata()
+                   : GetDelegateForCommittedLoad().GetMainFrameMetadata();
   TimingFieldBitSet matched_bits = GetMatchedBits(timing, metadata);
   if (subframe_rfh) {
     subframe_expected_fields_.ClearMatching(matched_bits);
@@ -165,8 +164,7 @@
 
 void PageLoadMetricsTestWaiter::OnFeaturesUsageObserved(
     content::RenderFrameHost* rfh,
-    const mojom::PageLoadFeatures& features,
-    const PageLoadExtraInfo& extra_info) {
+    const mojom::PageLoadFeatures& features) {
   if (WebFeaturesExpectationsSatisfied())
     return;
 
@@ -182,8 +180,7 @@
 }
 
 void PageLoadMetricsTestWaiter::OnDidFinishSubFrameNavigation(
-    content::NavigationHandle* navigation_handle,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    content::NavigationHandle* navigation_handle) {
   if (SubframeNavigationExpectationsSatisfied())
     return;
 
@@ -301,10 +298,9 @@
 
 void PageLoadMetricsTestWaiter::WaiterMetricsObserver::OnTimingUpdate(
     content::RenderFrameHost* subframe_rfh,
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
   if (waiter_)
-    waiter_->OnTimingUpdated(subframe_rfh, timing, extra_info);
+    waiter_->OnTimingUpdated(subframe_rfh, timing);
 }
 
 void PageLoadMetricsTestWaiter::WaiterMetricsObserver::OnCpuTimingUpdate(
@@ -332,18 +328,16 @@
 
 void PageLoadMetricsTestWaiter::WaiterMetricsObserver::OnFeaturesUsageObserved(
     content::RenderFrameHost* rfh,
-    const mojom::PageLoadFeatures& features,
-    const PageLoadExtraInfo& extra_info) {
+    const mojom::PageLoadFeatures& features) {
   if (waiter_)
-    waiter_->OnFeaturesUsageObserved(nullptr, features, extra_info);
+    waiter_->OnFeaturesUsageObserved(nullptr, features);
 }
 
 void PageLoadMetricsTestWaiter::WaiterMetricsObserver::
     OnDidFinishSubFrameNavigation(
-        content::NavigationHandle* navigation_handle,
-        const page_load_metrics::PageLoadExtraInfo& extra_info) {
+        content::NavigationHandle* navigation_handle) {
   if (waiter_)
-    waiter_->OnDidFinishSubFrameNavigation(navigation_handle, extra_info);
+    waiter_->OnDidFinishSubFrameNavigation(navigation_handle);
 }
 
 void PageLoadMetricsTestWaiter::WaiterMetricsObserver::FrameSizeChanged(
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h b/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h
index 94bb9682..df6e1b26 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h
@@ -103,8 +103,7 @@
 
     void OnTimingUpdate(
         content::RenderFrameHost* subframe_rfh,
-        const page_load_metrics::mojom::PageLoadTiming& timing,
-        const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+        const page_load_metrics::mojom::PageLoadTiming& timing) override;
 
     void OnCpuTimingUpdate(
         content::RenderFrameHost* subframe_rfh,
@@ -120,12 +119,10 @@
 
     void OnFeaturesUsageObserved(
         content::RenderFrameHost* rfh,
-        const page_load_metrics::mojom::PageLoadFeatures&,
-        const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+        const page_load_metrics::mojom::PageLoadFeatures&) override;
 
     void OnDidFinishSubFrameNavigation(
-        content::NavigationHandle* navigation_handle,
-        const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+        content::NavigationHandle* navigation_handle) override;
     void FrameSizeChanged(content::RenderFrameHost* render_frame_host,
                           const gfx::Size& frame_size) override;
 
@@ -178,8 +175,7 @@
   // MetricsWebContentsObserver. Stops waiting if expectations are satsfied
   // after update.
   void OnTimingUpdated(content::RenderFrameHost* subframe_rfh,
-                       const page_load_metrics::mojom::PageLoadTiming& timing,
-                       const page_load_metrics::PageLoadExtraInfo& extra_info);
+                       const page_load_metrics::mojom::PageLoadTiming& timing);
 
   // Updates observed page fields when a timing update is received by the
   // MetricsWebContentsObserver. Stops waiting if expectations are satsfied
@@ -203,15 +199,13 @@
   // Updates |observed_web_features_| to record any new feature observed.
   // Stops waiting if expectations are satisfied after update.
   void OnFeaturesUsageObserved(content::RenderFrameHost* rfh,
-                               const mojom::PageLoadFeatures& features,
-                               const PageLoadExtraInfo& extra_info);
+                               const mojom::PageLoadFeatures& features);
 
   void FrameSizeChanged(content::RenderFrameHost* render_frame_host,
                         const gfx::Size& frame_size);
 
   void OnDidFinishSubFrameNavigation(
-      content::NavigationHandle* navigation_handle,
-      const page_load_metrics::PageLoadExtraInfo& extra_info);
+      content::NavigationHandle* navigation_handle);
 
   void OnTrackerCreated(page_load_metrics::PageLoadTracker* tracker) override;
 
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_util.cc b/chrome/browser/page_load_metrics/page_load_metrics_util.cc
index b27926a..3a27176b 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_util.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_util.cc
@@ -161,11 +161,11 @@
 }
 
 bool DidObserveLoadingBehaviorInAnyFrame(
-    const page_load_metrics::PageLoadExtraInfo& info,
+    const PageLoadMetricsObserverDelegate& delegate,
     blink::WebLoadingBehaviorFlag behavior) {
   const int all_frame_loading_behavior_flags =
-      info.main_frame_metadata.behavior_flags |
-      info.subframe_metadata.behavior_flags;
+      delegate.GetMainFrameMetadata().behavior_flags |
+      delegate.GetSubframeMetadata().behavior_flags;
 
   return (all_frame_loading_behavior_flags & behavior) != 0;
 }
diff --git a/chrome/browser/page_load_metrics/page_load_tracker.cc b/chrome/browser/page_load_metrics/page_load_tracker.cc
index 894646c..b755bfc 100644
--- a/chrome/browser/page_load_metrics/page_load_tracker.cc
+++ b/chrome/browser/page_load_metrics/page_load_tracker.cc
@@ -122,46 +122,44 @@
                         completed_after_background);
 }
 
-void DispatchObserverTimingCallbacks(
-    PageLoadMetricsObserver* observer,
-    const mojom::PageLoadTiming& last_timing,
-    const mojom::PageLoadTiming& new_timing,
-    const PageLoadExtraInfo& extra_info) {
+void DispatchObserverTimingCallbacks(PageLoadMetricsObserver* observer,
+                                     const mojom::PageLoadTiming& last_timing,
+                                     const mojom::PageLoadTiming& new_timing) {
   if (!last_timing.Equals(new_timing))
-    observer->OnTimingUpdate(nullptr, new_timing, extra_info);
+    observer->OnTimingUpdate(nullptr, new_timing);
   if (new_timing.document_timing->dom_content_loaded_event_start &&
       !last_timing.document_timing->dom_content_loaded_event_start)
-    observer->OnDomContentLoadedEventStart(new_timing, extra_info);
+    observer->OnDomContentLoadedEventStart(new_timing);
   if (new_timing.document_timing->load_event_start &&
       !last_timing.document_timing->load_event_start)
-    observer->OnLoadEventStart(new_timing, extra_info);
+    observer->OnLoadEventStart(new_timing);
   if (new_timing.document_timing->first_layout &&
       !last_timing.document_timing->first_layout)
-    observer->OnFirstLayout(new_timing, extra_info);
+    observer->OnFirstLayout(new_timing);
   if (new_timing.interactive_timing->first_input_delay &&
       !last_timing.interactive_timing->first_input_delay)
-    observer->OnFirstInputInPage(new_timing, extra_info);
+    observer->OnFirstInputInPage(new_timing);
   if (new_timing.paint_timing->first_paint &&
       !last_timing.paint_timing->first_paint)
-    observer->OnFirstPaintInPage(new_timing, extra_info);
+    observer->OnFirstPaintInPage(new_timing);
   if (new_timing.paint_timing->first_image_paint &&
       !last_timing.paint_timing->first_image_paint)
-    observer->OnFirstImagePaintInPage(new_timing, extra_info);
+    observer->OnFirstImagePaintInPage(new_timing);
   if (new_timing.paint_timing->first_contentful_paint &&
       !last_timing.paint_timing->first_contentful_paint)
-    observer->OnFirstContentfulPaintInPage(new_timing, extra_info);
+    observer->OnFirstContentfulPaintInPage(new_timing);
   if (new_timing.paint_timing->first_meaningful_paint &&
       !last_timing.paint_timing->first_meaningful_paint)
-    observer->OnFirstMeaningfulPaintInMainFrameDocument(new_timing, extra_info);
+    observer->OnFirstMeaningfulPaintInMainFrameDocument(new_timing);
   if (new_timing.interactive_timing->interactive &&
       !last_timing.interactive_timing->interactive)
-    observer->OnPageInteractive(new_timing, extra_info);
+    observer->OnPageInteractive(new_timing);
   if (new_timing.parse_timing->parse_start &&
       !last_timing.parse_timing->parse_start)
-    observer->OnParseStart(new_timing, extra_info);
+    observer->OnParseStart(new_timing);
   if (new_timing.parse_timing->parse_stop &&
       !last_timing.parse_timing->parse_stop)
-    observer->OnParseStop(new_timing, extra_info);
+    observer->OnParseStop(new_timing);
 }
 
 }  // namespace
@@ -242,12 +240,11 @@
     RecordInternalError(ERR_NO_IPCS_RECEIVED);
   }
 
-  const PageLoadExtraInfo info = ComputePageLoadExtraInfo();
   for (const auto& observer : observers_) {
     if (failed_provisional_load_info_) {
-      observer->OnFailedProvisionalLoad(*failed_provisional_load_info_, info);
+      observer->OnFailedProvisionalLoad(*failed_provisional_load_info_);
     } else if (did_commit_) {
-      observer->OnComplete(metrics_update_dispatcher_.timing(), info);
+      observer->OnComplete(metrics_update_dispatcher_.timing());
     }
   }
 }
@@ -317,9 +314,8 @@
     first_background_time_ = background_time - navigation_start_;
   }
   visibility_tracker_.OnHidden();
-  const PageLoadExtraInfo info = ComputePageLoadExtraInfo();
   INVOKE_AND_PRUNE_OBSERVERS(observers_, OnHidden,
-                             metrics_update_dispatcher_.timing(), info);
+                             metrics_update_dispatcher_.timing());
 }
 
 void PageLoadTracker::WebContentsShown() {
@@ -390,9 +386,8 @@
 
 void PageLoadTracker::DidFinishSubFrameNavigation(
     content::NavigationHandle* navigation_handle) {
-  PageLoadExtraInfo extra_info(ComputePageLoadExtraInfo());
   for (const auto& observer : observers_) {
-    observer->OnDidFinishSubFrameNavigation(navigation_handle, extra_info);
+    observer->OnDidFinishSubFrameNavigation(navigation_handle);
   }
 }
 
@@ -411,9 +406,8 @@
 }
 
 void PageLoadTracker::OnInputEvent(const blink::WebInputEvent& event) {
-  const PageLoadExtraInfo info = ComputePageLoadExtraInfo();
   for (const auto& observer : observers_) {
-    observer->OnUserInput(event, metrics_update_dispatcher_.timing(), info);
+    observer->OnUserInput(event, metrics_update_dispatcher_.timing());
   }
 }
 
@@ -423,9 +417,8 @@
     app_entered_background_ = true;
   }
 
-  const PageLoadExtraInfo info = ComputePageLoadExtraInfo();
   INVOKE_AND_PRUNE_OBSERVERS(observers_, FlushMetricsOnAppEnterBackground,
-                             metrics_update_dispatcher_.timing(), info);
+                             metrics_update_dispatcher_.timing());
 }
 
 void PageLoadTracker::NotifyClientRedirectTo(
@@ -542,31 +535,6 @@
   }
 }
 
-PageLoadExtraInfo PageLoadTracker::ComputePageLoadExtraInfo() const {
-  base::Optional<base::TimeDelta> page_end_time;
-
-  if (page_end_reason_ != END_NONE) {
-    DCHECK_GE(page_end_time_, navigation_start_);
-    page_end_time = page_end_time_ - navigation_start_;
-  } else {
-    DCHECK(page_end_time_.is_null());
-  }
-
-  // page_end_reason_ == END_NONE implies page_end_user_initiated_info_ is not
-  // user initiated.
-  DCHECK(page_end_reason_ != END_NONE ||
-         (!page_end_user_initiated_info_.browser_initiated &&
-          !page_end_user_initiated_info_.user_gesture));
-  return PageLoadExtraInfo(
-      navigation_start_, first_background_time_, first_foreground_time_,
-      started_in_foreground_, user_initiated_info_, url(), start_url_,
-      did_commit_, page_end_reason_, page_end_user_initiated_info_,
-      page_end_time, metrics_update_dispatcher_.main_frame_metadata(),
-      metrics_update_dispatcher_.subframe_metadata(),
-      metrics_update_dispatcher_.page_render_data(),
-      metrics_update_dispatcher_.main_frame_render_data(), source_id_);
-}
-
 bool PageLoadTracker::HasMatchingNavigationRequestID(
     const content::GlobalRequestID& request_id) const {
   DCHECK(request_id != content::GlobalRequestID());
@@ -670,11 +638,10 @@
   DCHECK(!last_dispatched_merged_page_timing_->Equals(
       metrics_update_dispatcher_.timing()));
 
-  PageLoadExtraInfo extra_info(ComputePageLoadExtraInfo());
   for (const auto& observer : observers_) {
-    DispatchObserverTimingCallbacks(
-        observer.get(), *last_dispatched_merged_page_timing_,
-        metrics_update_dispatcher_.timing(), extra_info);
+    DispatchObserverTimingCallbacks(observer.get(),
+                                    *last_dispatched_merged_page_timing_,
+                                    metrics_update_dispatcher_.timing());
   }
   last_dispatched_merged_page_timing_ =
       metrics_update_dispatcher_.timing().Clone();
@@ -683,38 +650,33 @@
 void PageLoadTracker::OnSubFrameTimingChanged(
     content::RenderFrameHost* rfh,
     const mojom::PageLoadTiming& timing) {
-  PageLoadExtraInfo extra_info(ComputePageLoadExtraInfo());
   DCHECK(rfh->GetParent());
   for (const auto& observer : observers_) {
-    observer->OnTimingUpdate(rfh, timing, extra_info);
+    observer->OnTimingUpdate(rfh, timing);
   }
 }
 
 void PageLoadTracker::OnSubFrameRenderDataChanged(
     content::RenderFrameHost* rfh,
     const mojom::FrameRenderDataUpdate& render_data) {
-  PageLoadExtraInfo extra_info(ComputePageLoadExtraInfo());
   DCHECK(rfh->GetParent());
   for (const auto& observer : observers_) {
-    observer->OnSubFrameRenderDataUpdate(rfh, render_data, extra_info);
+    observer->OnSubFrameRenderDataUpdate(rfh, render_data);
   }
 }
 
 void PageLoadTracker::OnMainFrameMetadataChanged() {
-  PageLoadExtraInfo extra_info(ComputePageLoadExtraInfo());
   for (const auto& observer : observers_) {
-    observer->OnLoadingBehaviorObserved(
-        nullptr, extra_info.main_frame_metadata.behavior_flags, extra_info);
+    observer->OnLoadingBehaviorObserved(nullptr,
+                                        GetMainFrameMetadata().behavior_flags);
   }
 }
 
 void PageLoadTracker::OnSubframeMetadataChanged(
     content::RenderFrameHost* rfh,
     const mojom::PageLoadMetadata& metadata) {
-  PageLoadExtraInfo extra_info(ComputePageLoadExtraInfo());
   for (const auto& observer : observers_) {
-    observer->OnLoadingBehaviorObserved(rfh, metadata.behavior_flags,
-                                        extra_info);
+    observer->OnLoadingBehaviorObserved(rfh, metadata.behavior_flags);
   }
 }
 
@@ -727,9 +689,8 @@
 void PageLoadTracker::UpdateFeaturesUsage(
     content::RenderFrameHost* rfh,
     const mojom::PageLoadFeatures& new_features) {
-  PageLoadExtraInfo extra_info(ComputePageLoadExtraInfo());
   for (const auto& observer : observers_) {
-    observer->OnFeaturesUsageObserved(rfh, new_features, extra_info);
+    observer->OnFeaturesUsageObserved(rfh, new_features);
   }
 }
 
diff --git a/chrome/browser/page_load_metrics/page_load_tracker.h b/chrome/browser/page_load_metrics/page_load_tracker.h
index fca4839..8eec1acd 100644
--- a/chrome/browser/page_load_metrics/page_load_tracker.h
+++ b/chrome/browser/page_load_metrics/page_load_tracker.h
@@ -324,8 +324,6 @@
 
   base::TimeTicks navigation_start() const { return navigation_start_; }
 
-  PageLoadExtraInfo ComputePageLoadExtraInfo() const;
-
   ui::PageTransition page_transition() const { return page_transition_; }
 
   UserInitiatedInfo user_initiated_info() const { return user_initiated_info_; }
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index e997c07..acadbc3 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -204,8 +204,7 @@
   saving_and_filling_passwords_enabled_.Init(
       password_manager::prefs::kCredentialsEnableService, GetPrefs());
   static base::NoDestructor<password_manager::StoreMetricsReporter> reporter(
-      *saving_and_filling_passwords_enabled_, this, GetSyncService(profile_),
-      GetIdentityManager(), GetPrefs());
+      this, GetSyncService(profile_), GetIdentityManager(), GetPrefs());
   driver_factory_->RequestSendLoggingAvailability();
 }
 
@@ -509,8 +508,9 @@
     password_manager::CredentialLeakType leak_type,
     const GURL& origin) {
 #if defined(OS_ANDROID)
-  (new CredentialLeakControllerAndroid(leak_type, origin))
-      ->ShowDialog(web_contents()->GetTopLevelNativeWindow());
+  (new CredentialLeakControllerAndroid(
+       leak_type, origin, web_contents()->GetTopLevelNativeWindow()))
+      ->ShowDialog();
 #else   // !defined(OS_ANDROID)
   PasswordsClientUIDelegate* manage_passwords_ui_controller =
       PasswordsClientUIDelegateFromWebContents(web_contents());
diff --git a/chrome/browser/password_manager/credential_leak_controller_android.cc b/chrome/browser/password_manager/credential_leak_controller_android.cc
index 2037b69..255fc05 100644
--- a/chrome/browser/password_manager/credential_leak_controller_android.cc
+++ b/chrome/browser/password_manager/credential_leak_controller_android.cc
@@ -4,21 +4,25 @@
 
 #include "chrome/browser/password_manager/credential_leak_controller_android.h"
 
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "chrome/android/chrome_jni_headers/PasswordCheckupLauncher_jni.h"
 #include "chrome/browser/ui/android/passwords/credential_leak_dialog_view_android.h"
 #include "chrome/browser/ui/passwords/credential_leak_dialog_utils.h"
+#include "chrome/common/url_constants.h"
 #include "ui/android/window_android.h"
 
 CredentialLeakControllerAndroid::CredentialLeakControllerAndroid(
     password_manager::CredentialLeakType leak_type,
-    const GURL& origin)
-    : leak_type_(leak_type), origin_(origin) {}
+    const GURL& origin,
+    ui::WindowAndroid* window_android)
+    : leak_type_(leak_type), origin_(origin), window_android_(window_android) {}
 
 CredentialLeakControllerAndroid::~CredentialLeakControllerAndroid() = default;
 
-void CredentialLeakControllerAndroid::ShowDialog(
-    ui::WindowAndroid* window_android) {
+void CredentialLeakControllerAndroid::ShowDialog() {
   dialog_view_.reset(new CredentialLeakDialogViewAndroid(this));
-  dialog_view_->Show(window_android);
+  dialog_view_->Show(window_android_);
 }
 
 void CredentialLeakControllerAndroid::OnDialogDismissRequested() {
@@ -26,7 +30,14 @@
 }
 
 void CredentialLeakControllerAndroid::OnPasswordCheckTriggered() {
-  // TODO(crbug.com/986317): Navigate to the password check site.
+  if (ShouldCheckPasswords()) {
+    JNIEnv* env = base::android::AttachCurrentThread();
+    Java_PasswordCheckupLauncher_launchCheckup(
+        env,
+        base::android::ConvertUTF8ToJavaString(
+            env, leak_dialog_utils::GetPasswordCheckupURL().spec()),
+        window_android_->GetJavaObject());
+  }
   delete this;
 }
 
diff --git a/chrome/browser/password_manager/credential_leak_controller_android.h b/chrome/browser/password_manager/credential_leak_controller_android.h
index 7bc1742..c1f3462 100644
--- a/chrome/browser/password_manager/credential_leak_controller_android.h
+++ b/chrome/browser/password_manager/credential_leak_controller_android.h
@@ -22,11 +22,12 @@
  public:
   CredentialLeakControllerAndroid(
       password_manager::CredentialLeakType leak_type,
-      const GURL& origin);
+      const GURL& origin,
+      ui::WindowAndroid* window_android);
   ~CredentialLeakControllerAndroid();
 
   // Called when a leaked credential was detected.
-  void ShowDialog(ui::WindowAndroid* window_android);
+  void ShowDialog();
 
   // Called from the UI when the dialog dismissal was requested.
   // Will destroy the controller.
@@ -60,6 +61,8 @@
 
   const GURL origin_;
 
+  ui::WindowAndroid* window_android_;
+
   std::unique_ptr<CredentialLeakDialogViewAndroid> dialog_view_;
 
   DISALLOW_COPY_AND_ASSIGN(CredentialLeakControllerAndroid);
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index db61559..39410e4 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -2407,13 +2407,44 @@
  private:
   using PropertyFilter = content::AccessibilityTreeFormatter::PropertyFilter;
 
+  //  See chrome/test/data/pdf/accessibility/readme.md for more info.
+  void ParsePdfForExtraDirectives(
+      const std::string& pdf_contents,
+      content::AccessibilityTreeFormatter* formatter,
+      std::vector<PropertyFilter>* property_filters) {
+    const char kCommentMark = '%';
+    for (const std::string& line : base::SplitString(
+             pdf_contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
+      if (line.size() > 1 && line[0] == kCommentMark) {
+        // Remove first character since it's the comment mark.
+        std::string trimmed_line = line.substr(1);
+        const std::string& allow_str = formatter->GetAllowString();
+        if (base::StartsWith(trimmed_line, allow_str,
+                             base::CompareCase::SENSITIVE)) {
+          property_filters->push_back(PropertyFilter(
+              base::UTF8ToUTF16(trimmed_line.substr(allow_str.size())),
+              PropertyFilter::ALLOW));
+        }
+      }
+    }
+  }
+
   void RunTest(const base::FilePath& test_file_path, const char* file_dir) {
-    // Set up the tree formatter.
+    std::string pdf_contents;
+    {
+      base::ScopedAllowBlockingForTesting allow_blocking;
+      ASSERT_TRUE(base::ReadFileToString(test_file_path, &pdf_contents));
+    }
+
+    // Set up the tree formatter. Parse filters and other directives in the test
+    // file.
     std::unique_ptr<content::AccessibilityTreeFormatter> formatter =
         test_pass_.create_formatter();
     std::vector<PropertyFilter> property_filters;
     formatter->AddDefaultFilters(&property_filters);
     AddDefaultFilters(&property_filters);
+    ParsePdfForExtraDirectives(pdf_contents, formatter.get(),
+                               &property_filters);
     formatter->SetPropertyFilters(property_filters);
 
     // Exit without running the test if we can't find an expectation file or if
@@ -2532,3 +2563,7 @@
                        DirectionalTextRuns) {
   RunPDFTest(FILE_PATH_LITERAL("directional-text-runs.pdf"));
 }
+
+IN_PROC_BROWSER_TEST_P(PDFExtensionAccessibilityTreeDumpTest, TextDirection) {
+  RunPDFTest(FILE_PATH_LITERAL("text-direction.pdf"));
+}
\ No newline at end of file
diff --git a/chrome/browser/previews/defer_all_script_browsertest.cc b/chrome/browser/previews/defer_all_script_browsertest.cc
index e20934f..70f7c53 100644
--- a/chrome/browser/previews/defer_all_script_browsertest.cc
+++ b/chrome/browser/previews/defer_all_script_browsertest.cc
@@ -312,6 +312,8 @@
                                       true);
 }
 
+// Disable flake on Linux too (via only Android) until crbug/997697 resolved.
+#if defined(OS_ANDROID)
 IN_PROC_BROWSER_TEST_F(
     DeferAllScriptBrowserTest,
     DISABLE_ON_WIN_MAC_CHROMESOS(DeferAllScriptClientRedirectLoopStopped)) {
@@ -340,3 +342,4 @@
       "Navigation.ClientRedirectCycle.RedirectToReferrer", 2);
   histogram_tester.ExpectTotalCount("Previews.PageEndReason.DeferAllScript", 3);
 }
+#endif  // defined(OS_ANDROID)
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
index 58d9b68c7..0fb72fe 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
@@ -1222,6 +1222,9 @@
       chrome.send(
           'completeAdAuthentication',
           [credentials.username, credentials.password]);
+    } else if (credentials.publicSAML) {
+      this.email_ = credentials.email;
+      chrome.send('launchSAMLPublicSession', [credentials.email]);
     } else if (credentials.useOffline) {
       this.email_ = credentials.email;
       chrome.send(
diff --git a/chrome/browser/resources/gaia_auth_host/authenticator.js b/chrome/browser/resources/gaia_auth_host/authenticator.js
index f0f106e4..67c0e35 100644
--- a/chrome/browser/resources/gaia_auth_host/authenticator.js
+++ b/chrome/browser/resources/gaia_auth_host/authenticator.js
@@ -112,6 +112,9 @@
     // If the authentication is done via external IdP, 'startsOnSamlPage'
     // indicates whether the flow should start on the IdP page.
     'startsOnSamlPage',
+    // SAML assertion consumer URL, used to detect when Gaia-less SAML flows end
+    // (e.g. for SAML managed guest sessions).
+    'samlAclUrl',
   ];
 
 
@@ -258,6 +261,7 @@
        * @private
        */
       this.isSamlUserPasswordless_ = null;
+      this.samlAclUrl_ = null;
 
       window.addEventListener(
           'message', this.onMessageFromWebview_.bind(this), false);
@@ -458,6 +462,12 @@
 
       this.initialFrameUrl_ = this.constructInitialFrameUrl_(data);
       this.reloadUrl_ = data.frameUrl || this.initialFrameUrl_;
+      this.samlAclUrl_ = data.samlAclUrl;
+      // The email field is repurposed as public session email in SAML guest
+      // mode, ie when frameUrl is not empty.
+      if (data.samlAclUrl) {
+        this.email_ = data.email;
+      }
 
       if (data.startsOnSamlPage) {
         this.samlHandler_.startsOnSamlPage = true;
@@ -968,6 +978,7 @@
               gaiaId: this.gaiaId_ || '',
               password: this.password_ || '',
               usingSAML: this.authFlow == AuthFlow.SAML,
+              publicSAML: this.samlAclUrl_ || false,
               chooseWhatToSync: this.chooseWhatToSync_,
               skipForNow: this.skipForNow_,
               sessionIndex: this.sessionIndex_ || '',
@@ -1093,6 +1104,9 @@
         this.webview_.focus();
       } else if (currentUrl == BLANK_PAGE_URL) {
         this.fireReadyEvent_();
+      } else if (currentUrl == this.samlAclUrl_) {
+        this.skipForNow_ = true;
+        this.onAuthCompleted_();
       }
     }
 
diff --git a/chrome/browser/resources/gaia_auth_host/saml_handler.js b/chrome/browser/resources/gaia_auth_host/saml_handler.js
index 0792e9d..789f6fec 100644
--- a/chrome/browser/resources/gaia_auth_host/saml_handler.js
+++ b/chrome/browser/resources/gaia_auth_host/saml_handler.js
@@ -150,7 +150,7 @@
       this.apiPasswordBytes_ = null;
 
       /**
-       * Whether to abort the authentication flow and show an error messagen
+       * Whether to abort the authentication flow and show an error message
        * when content served over an unencrypted connection is detected.
        * @type {boolean}
        */
diff --git a/chrome/browser/resources/local_ntp/local_ntp.css b/chrome/browser/resources/local_ntp/local_ntp.css
index e28a7c32..31250ee 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.css
+++ b/chrome/browser/resources/local_ntp/local_ntp.css
@@ -830,7 +830,7 @@
   height: 32px;
   line-height: 32px;
   margin-inline-start: 16px;
-  max-width: 126px;
+  max-width: 125px;
   overflow: hidden;
   text-overflow: ellipsis;
   user-select: none;
diff --git a/chrome/browser/resources/media_router/extension/BUILD.gn b/chrome/browser/resources/media_router/extension/BUILD.gn
index 8728b3b..a1b4524 100644
--- a/chrome/browser/resources/media_router/extension/BUILD.gn
+++ b/chrome/browser/resources/media_router/extension/BUILD.gn
@@ -1,11 +1,5 @@
 import("src/files.gni")
 
-group("all") {
-  deps = [
-    ":media_router",
-  ]
-}
-
 declare_args() {
   # Determines whether JSCompiler should be used to typecheck
   # JavaScript code for the Media Router extension.
@@ -72,7 +66,7 @@
   outputs = [
     "$target_gen_dir/manifest.json",
   ]
-  deps = [
+  data_deps = [
     ":copy_prelude",
     ":media_router_modules",
   ]
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_main/os_settings_main.html b/chrome/browser/resources/settings/chromeos/os_settings_main/os_settings_main.html
index c716145..1f2afe0 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_main/os_settings_main.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_main/os_settings_main.html
@@ -58,7 +58,7 @@
     <template is="dom-if"
         if="[[showManagedHeader_(inSearchMode_, showingSubpage_,
         showPages_.about)]]">
-      <managed-footnote></managed-footnote>
+      <managed-footnote show-device-info></managed-footnote>
     </template>
     <template is="dom-if" if="[[showPages_.settings]]">
       <os-settings-page prefs="{{prefs}}"
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.html b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.html
index 1e2b6f7..7f2bddc 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.html
@@ -120,7 +120,8 @@
         show-menu="[[isNarrow]]">
     </os-toolbar>
     <cr-drawer id="drawer" on-close="onMenuClose_" heading="$i18n{settings}"
-        align="$i18n{textdirection}">
+        align="$i18n{textdirection}" icon-name="cr20:menu"
+        icon-title="$i18n{close}">
       <div class="drawer-content">
         <template is="dom-if" id="drawerTemplate">
           <os-settings-menu show-apps="[[showApps_]]"
diff --git a/chrome/browser/resources/settings/internet_page/BUILD.gn b/chrome/browser/resources/settings/internet_page/BUILD.gn
index 0a379a31b..ed2f559 100644
--- a/chrome/browser/resources/settings/internet_page/BUILD.gn
+++ b/chrome/browser/resources/settings/internet_page/BUILD.gn
@@ -143,9 +143,9 @@
 
 js_library("tether_connection_dialog") {
   deps = [
-    "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types",
     "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog",
     "//ui/webui/resources/js:cr",
     "//ui/webui/resources/js:i18n_behavior",
+    "//ui/webui/resources/js/chromeos:onc_mojo",
   ]
 }
diff --git a/chrome/browser/resources/settings/internet_page/internet_detail_page.html b/chrome/browser/resources/settings/internet_page/internet_detail_page.html
index b72fb51..10ae002 100644
--- a/chrome/browser/resources/settings/internet_page/internet_detail_page.html
+++ b/chrome/browser/resources/settings/internet_page/internet_detail_page.html
@@ -341,11 +341,13 @@
       </template>
     </template>
 
-    <tether-connection-dialog id="tetherDialog"
-        network-properties="[[networkProperties_]]"
-        on-tether-connect="onTetherConnect_"
-        out-of-range="[[outOfRange_]]">
-    </tether-connection-dialog>
+    <template is="dom-if" if="[[isTether_(managedProperties_)]]" restamp>
+      <tether-connection-dialog id="tetherDialog"
+          managed-properties="[[managedProperties_]]"
+          on-tether-connect="onTetherConnect_"
+          out-of-range="[[outOfRange_]]">
+      </tether-connection-dialog>
+    </template>
   </template>
   <script src="internet_detail_page.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/internet_page/internet_detail_page.js b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
index 3b52f31..d27af7b 100644
--- a/chrome/browser/resources/settings/internet_page/internet_detail_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
@@ -384,7 +384,8 @@
       // Set |this.shouldShowConfigureWhenNetworkLoaded_| back to false to
       // ensure that the Tether dialog is only shown once.
       this.shouldShowConfigureWhenNetworkLoaded_ = false;
-      this.showTetherDialog_();
+      // Async call to ensure dialog is stamped.
+      setTimeout(() => this.showTetherDialog_());
     }
   },
 
@@ -750,6 +751,16 @@
 
   /**
    * @param {!OncMojo.ManagedProperties} managedProperties
+   * @return {boolean}
+   * @private
+   */
+  isTether_: function(managedProperties) {
+    return !!managedProperties &&
+        managedProperties.type == mojom.NetworkType.kTether;
+  },
+
+  /**
+   * @param {!OncMojo.ManagedProperties} managedProperties
    * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy
    * @param {boolean} managedNetworkAvailable
    * @return {boolean}
@@ -1056,11 +1067,8 @@
 
   /** @private */
   onConnectTap_: function() {
-    // TODO(stevenjb): Add ManagedTetherProperties for hasConnectedToHost.
-    // For Tether networks that have not connected to a host, show a dialog.
-    if (this.networkProperties_.Type == CrOnc.Type.TETHER &&
-        (!this.networkProperties_.Tether ||
-         !this.networkProperties_.Tether.HasConnectedToHost)) {
+    if (this.managedProperties_.type == mojom.NetworkType.kTether &&
+        (!this.managedProperties_.tether.hasConnectedToHost)) {
       this.showTetherDialog_();
       return;
     }
diff --git a/chrome/browser/resources/settings/internet_page/tether_connection_dialog.html b/chrome/browser/resources/settings/internet_page/tether_connection_dialog.html
index b9d257a..22d07f9 100644
--- a/chrome/browser/resources/settings/internet_page/tether_connection_dialog.html
+++ b/chrome/browser/resources/settings/internet_page/tether_connection_dialog.html
@@ -1,6 +1,5 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_network_icon.html">
-<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
@@ -75,14 +74,14 @@
         </span>
         <div id="host-device-container">
           <!-- TODO(hsuregan): Add an a11y label. -->
-          <iron-icon icon="[[getSignalStrengthIconName_(networkProperties)]]">
+          <iron-icon icon="[[getSignalStrengthIconName_(managedProperties)]]">
           </iron-icon>
           <div id="host-device-text-container">
             <span id="host-device-text-name">
-              [[getDeviceName_(networkProperties)]]
+              [[getDeviceName_(managedProperties)]]
             </span>
             <span id="host-device-text-battery" class="secondary">
-              [[getBatteryPercentageString_(networkProperties)]]
+              [[getBatteryPercentageString_(managedProperties)]]
             </span>
           </div>
           <div class="flex"></div>
@@ -93,18 +92,18 @@
           </div>
         </div>
         <div id="tether-explanation">
-          [[getExplanation_(networkProperties)]]
+          [[getExplanation_(managedProperties)]]
         </div>
         <div id="tether-carrier-warning">
           $i18n{tetherConnectionCarrierWarning}
         </div>
         <div id="tether-description-title">
-          [[getDescriptionTitle_(networkProperties)]]
+          [[getDescriptionTitle_(managedProperties)]]
         </div>
         <ul id="tether-description-list">
           <li>$i18n{tetherConnectionDescriptionMobileData}</li>
-          <li>[[getBatteryDescription_(networkProperties)]]</li>
-          <li hidden$="[[!shouldShowDisconnectFromWifi_(networkProperties)]]">
+          <li>[[getBatteryDescription_(managedProperties)]]</li>
+          <li hidden$="[[!shouldShowDisconnectFromWifi_(managedProperties)]]">
             $i18n{tetherConnectionDescriptionWiFi}
           </li>
         </ul>
diff --git a/chrome/browser/resources/settings/internet_page/tether_connection_dialog.js b/chrome/browser/resources/settings/internet_page/tether_connection_dialog.js
index 2eeb332..5a7c6be 100644
--- a/chrome/browser/resources/settings/internet_page/tether_connection_dialog.js
+++ b/chrome/browser/resources/settings/internet_page/tether_connection_dialog.js
@@ -2,15 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/**
- * @typedef {{
- *   tetherHostDeviceName: string,
- *   batteryPercentage: number,
- *   connectionStrength: number,
- *   isTetherHostCurrentlyOnWifi: boolean
- * }}
- */
-let TetherConnectionData;
+(function() {
+'use strict';
+
+const mojom = chromeos.networkConfig.mojom;
 
 Polymer({
   is: 'tether-connection-dialog',
@@ -18,13 +13,8 @@
   behaviors: [I18nBehavior],
 
   properties: {
-    /**
-     * The current properties for the network matching |guid|.
-     * @type {!CrOnc.NetworkProperties|undefined}
-     */
-    networkProperties: {
-      type: Object,
-    },
+    /** @private {!chromeos.networkConfig.mojom.ManagedProperties|undefined} */
+    managedProperties: Object,
 
     /**
      * Whether the network has been lost (e.g., has gone out of range).
@@ -71,12 +61,11 @@
   },
 
   /**
-   * @param {!CrOnc.NetworkProperties} networkProperties The network
-   *     properties.
+   * @param {!mojom.ManagedProperties} managedProperties
    * @return {boolean}
    * @private
    */
-  shouldShowDisconnectFromWifi_: function(networkProperties) {
+  shouldShowDisconnectFromWifi_: function(managedProperties) {
     // TODO(khorimoto): Pipe through a new network property which describes
     // whether the tether host is currently connected to a Wi-Fi network. Return
     // whether it is here.
@@ -84,13 +73,13 @@
   },
 
   /**
-   * @param {!CrOnc.NetworkProperties} networkProperties The network properties.
+   * @param {!mojom.ManagedProperties} managedProperties
    * @return {string} The battery percentage integer value converted to a
    *     string. Note that this will not return a string with a "%" suffix.
    * @private
    */
-  getBatteryPercentageAsString_: function(networkProperties) {
-    const percentage = this.get('Tether.BatteryPercentage', networkProperties);
+  getBatteryPercentageAsString_: function(managedProperties) {
+    const percentage = this.get('tether.batteryPercentage', managedProperties);
     if (percentage === undefined) {
       return '';
     }
@@ -102,12 +91,12 @@
    * Custom icons are used here instead of a <cr-network-icon> because this
    * dialog uses a special color scheme.
    *
-   * @param {!CrOnc.NetworkProperties} networkProperties The network properties.
+   * @param {!mojom.ManagedProperties} managedProperties
    * @return {string} The name of the icon to be used to represent the network's
    * signal strength.
    */
-  getSignalStrengthIconName_: function(networkProperties) {
-    let signalStrength = this.get('Tether.SignalStrength', networkProperties);
+  getSignalStrengthIconName_: function(managedProperties) {
+    let signalStrength = this.get('tether.signalStrength', managedProperties);
     if (signalStrength === undefined) {
       signalStrength = 4;
     }
@@ -116,55 +105,56 @@
   },
 
   /**
-   * @param {!CrOnc.NetworkProperties} networkProperties The network properties.
+   * @param {!mojom.ManagedProperties} managedProperties
    * @return {string}
    * @private
    */
-  getDeviceName_: function(networkProperties) {
-    return CrOnc.getNetworkName(networkProperties);
+  getDeviceName_: function(managedProperties) {
+    return managedProperties ? OncMojo.getNetworkName(managedProperties) : '';
   },
 
   /**
-   * @param {!CrOnc.NetworkProperties} networkProperties The network properties.
+   * @param {!mojom.ManagedProperties} managedProperties
    * @return {string}
    * @private
    */
-  getBatteryPercentageString_: function(networkProperties) {
+  getBatteryPercentageString_: function(managedProperties) {
     return this.i18n(
         'tetherConnectionBatteryPercentage',
-        this.getBatteryPercentageAsString_(networkProperties));
+        this.getBatteryPercentageAsString_(managedProperties));
   },
 
   /**
-   * @param {!CrOnc.NetworkProperties} networkProperties The network properties.
+   * @param {!mojom.ManagedProperties} managedProperties
    * @return {string}
    * @private
    */
-  getExplanation_: function(networkProperties) {
+  getExplanation_: function(managedProperties) {
     return this.i18n(
         'tetherConnectionExplanation',
-        CrOnc.getEscapedNetworkName(networkProperties));
+        HTMLEscape(OncMojo.getNetworkName(managedProperties)));
   },
 
   /**
-   * @param {!CrOnc.NetworkProperties} networkProperties The network properties.
+   * @param {!mojom.ManagedProperties} managedProperties
    * @return {string}
    * @private
    */
-  getDescriptionTitle_: function(networkProperties) {
+  getDescriptionTitle_: function(managedProperties) {
     return this.i18n(
         'tetherConnectionDescriptionTitle',
-        CrOnc.getEscapedNetworkName(networkProperties));
+        HTMLEscape(OncMojo.getNetworkName(managedProperties)));
   },
 
   /**
-   * @param {!CrOnc.NetworkProperties} networkProperties The network properties.
+   * @param {!mojom.ManagedProperties} managedProperties
    * @return {string}
    * @private
    */
-  getBatteryDescription_: function(networkProperties) {
+  getBatteryDescription_: function(managedProperties) {
     return this.i18n(
         'tetherConnectionDescriptionBattery',
-        this.getBatteryPercentageAsString_(networkProperties));
+        this.getBatteryPercentageAsString_(managedProperties));
   },
 });
+})();
diff --git a/chrome/browser/safe_browsing/download_protection/binary_upload_service.cc b/chrome/browser/safe_browsing/download_protection/binary_upload_service.cc
index ac54e806..ac0bb53 100644
--- a/chrome/browser/safe_browsing/download_protection/binary_upload_service.cc
+++ b/chrome/browser/safe_browsing/download_protection/binary_upload_service.cc
@@ -115,20 +115,23 @@
         policy {
           cookies_allowed: YES
           cookies_store: "Safe Browsing Cookie Store"
-          setting: "No user setting."
+          setting: "This is disabled by default an can only be enabled by "
+            "policy."
           chrome_policy {
             SendFilesForMalwareCheck {
-              policy_options {mode: MANDATORY}
               SendFilesForMalwareCheck: 0
             }
           }
           chrome_policy {
             SendFilesForMalwareCheck {
-              policy_options {mode: MANDATORY}
               SendFilesForMalwareCheck: 1
             }
           }
-        })");
+        }
+        comments: "Setting SendFilesForMalwareCheck to 0 (Do not scan "
+          "downloads) or 1 (Forbid the scanning of downloads) will disable "
+          "this feature"
+        )");
 
   std::string metadata;
   request->deep_scanning_request().SerializeToString(&metadata);
diff --git a/chrome/browser/search/ntp_features.cc b/chrome/browser/search/ntp_features.cc
index 0ee86e8..b4b076c7 100644
--- a/chrome/browser/search/ntp_features.cc
+++ b/chrome/browser/search/ntp_features.cc
@@ -35,4 +35,10 @@
 const base::Feature kFirstRunDefaultSearchShortcut{
     "FirstRunDefaultSearchShortcut", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// If enabled, the search box in the middle of the NTP will accept input
+// directly (i.e. not be a "fake" box) and search results will show directly
+// below the non-fake input ("realbox").
+const base::Feature kNtpRealbox{"NtpRealbox",
+                                base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
diff --git a/chrome/browser/search/ntp_features.h b/chrome/browser/search/ntp_features.h
index 9c4e8d0b..fffd6bd 100644
--- a/chrome/browser/search/ntp_features.h
+++ b/chrome/browser/search/ntp_features.h
@@ -20,6 +20,8 @@
 
 extern const base::Feature kFirstRunDefaultSearchShortcut;
 
+extern const base::Feature kNtpRealbox;
+
 }  // namespace features
 
 #endif  // CHROME_BROWSER_SEARCH_NTP_FEATURES_H_
diff --git a/chrome/browser/ssl/insecure_sensitive_input_driver.cc b/chrome/browser/ssl/insecure_sensitive_input_driver.cc
index f87b4bc9..ff97026 100644
--- a/chrome/browser/ssl/insecure_sensitive_input_driver.cc
+++ b/chrome/browser/ssl/insecure_sensitive_input_driver.cc
@@ -16,9 +16,9 @@
 
 InsecureSensitiveInputDriver::~InsecureSensitiveInputDriver() {}
 
-void InsecureSensitiveInputDriver::BindInsecureInputServiceRequest(
-    blink::mojom::InsecureInputServiceRequest request) {
-  insecure_input_bindings_.AddBinding(this, std::move(request));
+void InsecureSensitiveInputDriver::BindInsecureInputServiceReceiver(
+    mojo::PendingReceiver<blink::mojom::InsecureInputService> receiver) {
+  insecure_input_receivers_.Add(this, std::move(receiver));
 }
 
 void InsecureSensitiveInputDriver::DidEditFieldInInsecureContext() {
diff --git a/chrome/browser/ssl/insecure_sensitive_input_driver.h b/chrome/browser/ssl/insecure_sensitive_input_driver.h
index 109f657..f70b939c 100644
--- a/chrome/browser/ssl/insecure_sensitive_input_driver.h
+++ b/chrome/browser/ssl/insecure_sensitive_input_driver.h
@@ -5,7 +5,8 @@
 #ifndef CHROME_BROWSER_SSL_INSECURE_SENSITIVE_INPUT_DRIVER_H_
 #define CHROME_BROWSER_SSL_INSECURE_SENSITIVE_INPUT_DRIVER_H_
 
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "third_party/blink/public/mojom/insecure_input/insecure_input_service.mojom.h"
 
 namespace content {
@@ -24,8 +25,8 @@
       content::RenderFrameHost* render_frame_host);
   ~InsecureSensitiveInputDriver() override;
 
-  void BindInsecureInputServiceRequest(
-      blink::mojom::InsecureInputServiceRequest request);
+  void BindInsecureInputServiceReceiver(
+      mojo::PendingReceiver<blink::mojom::InsecureInputService> receiver);
 
   // blink::mojom::InsecureInputService:
   void DidEditFieldInInsecureContext() override;
@@ -33,7 +34,8 @@
  private:
   content::RenderFrameHost* render_frame_host_;
 
-  mojo::BindingSet<blink::mojom::InsecureInputService> insecure_input_bindings_;
+  mojo::ReceiverSet<blink::mojom::InsecureInputService>
+      insecure_input_receivers_;
 
   DISALLOW_COPY_AND_ASSIGN(InsecureSensitiveInputDriver);
 };
diff --git a/chrome/browser/ssl/insecure_sensitive_input_driver_factory.cc b/chrome/browser/ssl/insecure_sensitive_input_driver_factory.cc
index d601a02..721977a3 100644
--- a/chrome/browser/ssl/insecure_sensitive_input_driver_factory.cc
+++ b/chrome/browser/ssl/insecure_sensitive_input_driver_factory.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/ssl/insecure_sensitive_input_driver_factory.h"
 
 #include <utility>
-#include <vector>
 
 #include "base/stl_util.h"
 #include "chrome/browser/ssl/insecure_sensitive_input_driver.h"
@@ -57,7 +56,7 @@
 
 // static
 void InsecureSensitiveInputDriverFactory::BindDriver(
-    blink::mojom::InsecureInputServiceRequest request,
+    mojo::PendingReceiver<blink::mojom::InsecureInputService> receiver,
     content::RenderFrameHost* render_frame_host) {
   content::WebContents* web_contents =
       content::WebContents::FromRenderFrameHost(render_frame_host);
@@ -70,7 +69,7 @@
   InsecureSensitiveInputDriver* driver =
       factory->GetOrCreateDriverForFrame(render_frame_host);
 
-  driver->BindInsecureInputServiceRequest(std::move(request));
+  driver->BindInsecureInputServiceReceiver(std::move(receiver));
 }
 
 InsecureSensitiveInputDriver*
diff --git a/chrome/browser/ssl/insecure_sensitive_input_driver_factory.h b/chrome/browser/ssl/insecure_sensitive_input_driver_factory.h
index 06a04b7..9c720ee 100644
--- a/chrome/browser/ssl/insecure_sensitive_input_driver_factory.h
+++ b/chrome/browser/ssl/insecure_sensitive_input_driver_factory.h
@@ -11,6 +11,7 @@
 
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "third_party/blink/public/mojom/insecure_input/insecure_input_service.mojom.h"
 
 namespace content {
@@ -37,8 +38,9 @@
 
   // Finds or creates a factory for the |web_contents| and creates an
   // |InsecureSensitiveInputDriver| for the target |render_frame_host|.
-  static void BindDriver(blink::mojom::InsecureInputServiceRequest request,
-                         content::RenderFrameHost* render_frame_host);
+  static void BindDriver(
+      mojo::PendingReceiver<blink::mojom::InsecureInputService> receiver,
+      content::RenderFrameHost* render_frame_host);
 
   // Creates a |InsecureSensitiveInputDriver| for the specified
   // |render_frame_host| and adds it to the |frame_driver_map_|.
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 88528e64..977ef539 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
@@ -941,8 +941,9 @@
 
 // Wallet data should get cleared from the database when the wallet sync type
 // flag is disabled.
+// Test is flaky: https://crbug.com/997786
 IN_PROC_BROWSER_TEST_P(SingleClientWalletSyncTestWithDefaultFeatures,
-                       ClearOnDisableWalletSync) {
+                       DISABLED_ClearOnDisableWalletSync) {
   GetFakeServer()->SetWalletData({CreateDefaultSyncWalletAddress(),
                                   CreateDefaultSyncWalletCard(),
                                   CreateDefaultSyncPaymentsCustomerData()});
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 8c663fcd..4a227cd 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
@@ -13,6 +13,7 @@
 #include "base/rand_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
+#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/passwords_helper.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
@@ -39,60 +40,18 @@
 
 static const char* kValidPassphrase = "passphrase!";
 
-class TwoClientPasswordsSyncTest
-    : public testing::WithParamInterface<std::tuple<bool, bool>>,
-      public SyncTest {
+class TwoClientPasswordsSyncTest : public FeatureToggler, public SyncTest {
  public:
-  TwoClientPasswordsSyncTest() : SyncTest(TWO_CLIENT) {}
+  TwoClientPasswordsSyncTest()
+      : FeatureToggler(switches::kSyncUSSPasswords), SyncTest(TWO_CLIENT) {}
 
   ~TwoClientPasswordsSyncTest() override {}
 
- 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
-  // after it has been used is needed for this test (by setting up each client
-  // with a different ScopedFeatureList).
-  void BeforeSetupClient(int index) override {
-    const bool should_enable_uss =
-        index == 0 ? std::get<0>(GetParam()) : std::get<1>(GetParam());
-
-    // In order to avoid test flakiness, for any client other than the first, we
-    // need to make sure the feature toggle has been fully read by PasswordStore
-    // before overriding it again. The way to achieve that, for the current
-    // implementation of PasswordStore, is to make a round trip to the backend
-    // sequence, which guarantees that initialization has completed.
-    if (index != 0) {
-      // We ignore the returned value since all we want is to wait for the
-      // round trip to be completed.
-      GetPasswordCount(index - 1);
-    }
-
-    // The value of the feature kSyncUSSPasswords only matters during the
-    // setup of each client, when the profile is created, ProfileSyncService
-    // instantiated as well as the datatype controllers. By overriding the
-    // feature, we can influence whether client |index| is running with the new
-    // codepath or the legacy one.
-    override_features_ = std::make_unique<base::test::ScopedFeatureList>();
-    if (should_enable_uss) {
-      override_features_->InitAndEnableFeature(switches::kSyncUSSPasswords);
-    } else {
-      override_features_->InitAndDisableFeature(switches::kSyncUSSPasswords);
-    }
-  }
-
  private:
-  std::unique_ptr<base::test::ScopedFeatureList> override_features_;
-
   DISALLOW_COPY_AND_ASSIGN(TwoClientPasswordsSyncTest);
 };
 
-// Flaky on TSAN: crbug.com/915219
-#if defined(THREAD_SANITIZER)
-#define MAYBE_Add DISABLED_Add
-#else
-#define MAYBE_Add Add
-#endif
-IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, E2E_ENABLED(MAYBE_Add)) {
+IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, E2E_ENABLED(Add)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(SamePasswordFormsChecker().Wait());
 
@@ -104,13 +63,7 @@
   ASSERT_EQ(1, GetPasswordCount(1));
 }
 
-// Flaky on TSAN: crbug.com/915219
-#if defined(THREAD_SANITIZER)
-#define MAYBE_Race DISABLED_Race
-#else
-#define MAYBE_Race Race
-#endif
-IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, E2E_ENABLED(MAYBE_Race)) {
+IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, E2E_ENABLED(Race)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllProfilesContainSamePasswordForms());
 
@@ -124,14 +77,7 @@
   ASSERT_TRUE(SamePasswordFormsChecker().Wait());
 }
 
-// Flaky on TSAN: crbug.com/915219
-#if defined(THREAD_SANITIZER)
-#define MAYBE_MergeWithTheMostRecent DISABLED_MergeWithTheMostRecent
-#else
-#define MAYBE_MergeWithTheMostRecent MergeWithTheMostRecent
-#endif
-IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest,
-                       MAYBE_MergeWithTheMostRecent) {
+IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, 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.
@@ -175,14 +121,8 @@
   }
 }
 
-// Flaky on TSAN: crbug.com/915219
-#if defined(THREAD_SANITIZER)
-#define MAYBE_SetPassphraseAndAddPassword DISABLED_SetPassphraseAndAddPassword
-#else
-#define MAYBE_SetPassphraseAndAddPassword SetPassphraseAndAddPassword
-#endif
 IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest,
-                       E2E_ENABLED(MAYBE_SetPassphraseAndAddPassword)) {
+                       E2E_ENABLED(SetPassphraseAndAddPassword)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
   GetSyncService(0)->GetUserSettings()->SetEncryptionPassphrase(
@@ -201,13 +141,7 @@
   ASSERT_TRUE(SamePasswordFormsChecker().Wait());
 }
 
-// Flaky on TSAN: crbug.com/915219
-#if defined(THREAD_SANITIZER)
-#define MAYBE_Update DISABLED_Update
-#else
-#define MAYBE_Update Update
-#endif
-IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, MAYBE_Update) {
+IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, Update) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
 
@@ -228,13 +162,7 @@
   ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
 }
 
-// Flaky on TSAN: crbug.com/915219
-#if defined(THREAD_SANITIZER)
-#define MAYBE_AddTwice DISABLED_AddTwice
-#else
-#define MAYBE_AddTwice AddTwice
-#endif
-IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, MAYBE_AddTwice) {
+IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, AddTwice) {
   // Password store supports adding the same form twice, so this is testing this
   // behaviour.
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
@@ -258,13 +186,7 @@
   ASSERT_EQ(1, GetPasswordCount(1));
 }
 
-// Flaky on TSAN: crbug.com/915219
-#if defined(THREAD_SANITIZER)
-#define MAYBE_Delete DISABLED_Delete
-#else
-#define MAYBE_Delete Delete
-#endif
-IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, MAYBE_Delete) {
+IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, Delete) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
 
@@ -287,15 +209,8 @@
   ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
 }
 
-#if defined(THREAD_SANITIZER)
-// Flaky on TSAN: crbug.com/915219
-#define MAYBE_SetPassphraseAndThenSetupSync \
-  DISABLED_SetPassphraseAndThenSetupSync
-#else
-#define MAYBE_SetPassphraseAndThenSetupSync SetPassphraseAndThenSetupSync
-#endif
 IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest,
-                       MAYBE_SetPassphraseAndThenSetupSync) {
+                       SetPassphraseAndThenSetupSync) {
   ASSERT_TRUE(SetupClients());
 
   ASSERT_TRUE(GetClient(0)->SetupSync());
@@ -327,13 +242,7 @@
   ASSERT_TRUE(SamePasswordFormsChecker().Wait());
 }
 
-// Flaky on TSAN: crbug.com/915219
-#if defined(THREAD_SANITIZER)
-#define MAYBE_DeleteTwo DISABLED_DeleteTwo
-#else
-#define MAYBE_DeleteTwo DeleteTwo
-#endif
-IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, E2E_ONLY(MAYBE_DeleteTwo)) {
+IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, E2E_ONLY(DeleteTwo)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllProfilesContainSamePasswordForms());
 
@@ -361,13 +270,7 @@
   ASSERT_EQ(init_password_count - 2, GetPasswordCount(0));
 }
 
-// Flaky on TSAN: crbug.com/915219
-#if defined(THREAD_SANITIZER)
-#define MAYBE_DeleteAll DISABLED_DeleteAll
-#else
-#define MAYBE_DeleteAll DeleteAll
-#endif
-IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, MAYBE_DeleteAll) {
+IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, DeleteAll) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
 
@@ -387,13 +290,7 @@
   ASSERT_EQ(0, GetVerifierPasswordCount());
 }
 
-// Flaky on TSAN: crbug.com/915219
-#if defined(THREAD_SANITIZER)
-#define MAYBE_Merge DISABLED_Merge
-#else
-#define MAYBE_Merge Merge
-#endif
-IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, E2E_ENABLED(MAYBE_Merge)) {
+IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, E2E_ENABLED(Merge)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllProfilesContainSamePasswordForms());
 
@@ -408,14 +305,7 @@
   ASSERT_EQ(3, GetPasswordCount(0));
 }
 
-// Flaky on TSAN: crbug.com/915219
-#if defined(THREAD_SANITIZER)
-#define MAYBE_TwoClientAddPass DISABLED_TwoClientAddPass
-#else
-#define MAYBE_TwoClientAddPass TwoClientAddPass
-#endif
-IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest,
-                       E2E_ONLY(MAYBE_TwoClientAddPass)) {
+IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest, E2E_ONLY(TwoClientAddPass)) {
   ASSERT_TRUE(SetupSync()) <<  "SetupSync() failed.";
   // All profiles should sync same passwords.
   ASSERT_TRUE(SamePasswordFormsChecker().Wait())
@@ -439,10 +329,6 @@
   }
 }
 
-// We instantiate every test 4 times, for every combination of USS being enabled
-// in individual clients. This verifies backward-compatibility between the two
-// implementations.
 INSTANTIATE_TEST_SUITE_P(USS,
                          TwoClientPasswordsSyncTest,
-                         ::testing::Combine(::testing::Values(false, true),
-                                            ::testing::Values(false, true)));
+                         ::testing::Values(false, true));
diff --git a/chrome/browser/ui/android/overlay/overlay_window_android.cc b/chrome/browser/ui/android/overlay/overlay_window_android.cc
index 9be471b..8c7f09a 100644
--- a/chrome/browser/ui/android/overlay/overlay_window_android.cc
+++ b/chrome/browser/ui/android/overlay/overlay_window_android.cc
@@ -32,15 +32,15 @@
 
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_PictureInPictureActivity_createActivity(
-      env, reinterpret_cast<long>(this),
+      env, reinterpret_cast<intptr_t>(this),
       TabAndroid::FromWebContents(controller_->GetInitiatorWebContents())
           ->GetJavaObject());
 }
 
 OverlayWindowAndroid::~OverlayWindowAndroid() {
   JNIEnv* env = base::android::AttachCurrentThread();
-  Java_PictureInPictureActivity_onWindowDestroyed(env,
-                                                  reinterpret_cast<long>(this));
+  Java_PictureInPictureActivity_onWindowDestroyed(
+      env, reinterpret_cast<intptr_t>(this));
 }
 
 void OverlayWindowAndroid::OnActivityStart(
diff --git a/chrome/browser/ui/android/passwords/credential_leak_dialog_view_android.cc b/chrome/browser/ui/android/passwords/credential_leak_dialog_view_android.cc
index 5ade703..e00b1f7b 100644
--- a/chrome/browser/ui/android/passwords/credential_leak_dialog_view_android.cc
+++ b/chrome/browser/ui/android/passwords/credential_leak_dialog_view_android.cc
@@ -12,7 +12,6 @@
 #include "chrome/android/chrome_jni_headers/CredentialLeakDialogBridge_jni.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/password_manager/credential_leak_controller_android.h"
-#include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/password_manager/core/browser/password_form_manager_for_ui.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
diff --git a/chrome/browser/ui/android/passwords/onboarding_dialog_view.cc b/chrome/browser/ui/android/passwords/onboarding_dialog_view.cc
index f69a566b..f962a1d 100644
--- a/chrome/browser/ui/android/passwords/onboarding_dialog_view.cc
+++ b/chrome/browser/ui/android/passwords/onboarding_dialog_view.cc
@@ -33,7 +33,7 @@
   ui::WindowAndroid* window_android =
       client_->web_contents()->GetTopLevelNativeWindow();
   java_object_.Reset(Java_OnboardingDialogBridge_create(
-      env, window_android->GetJavaObject(), reinterpret_cast<long>(this)));
+      env, window_android->GetJavaObject(), reinterpret_cast<intptr_t>(this)));
 
   base::string16 onboarding_title =
       l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_ONBOARDING_TITLE);
diff --git a/chrome/browser/ui/android/passwords/password_generation_dialog_view_android.cc b/chrome/browser/ui/android/passwords/password_generation_dialog_view_android.cc
index 5ecc729..845c456 100644
--- a/chrome/browser/ui/android/passwords/password_generation_dialog_view_android.cc
+++ b/chrome/browser/ui/android/passwords/password_generation_dialog_view_android.cc
@@ -29,7 +29,7 @@
   DCHECK(window_android);
   java_object_.Reset(Java_PasswordGenerationDialogBridge_create(
       base::android::AttachCurrentThread(), window_android->GetJavaObject(),
-      reinterpret_cast<long>(this)));
+      reinterpret_cast<intptr_t>(this)));
 }
 
 PasswordGenerationDialogViewAndroid::~PasswordGenerationDialogViewAndroid() {
diff --git a/chrome/browser/ui/autofill/payments/autofill_ui_util.cc b/chrome/browser/ui/autofill/payments/autofill_ui_util.cc
index 03c81431..38aba3e3 100644
--- a/chrome/browser/ui/autofill/payments/autofill_ui_util.cc
+++ b/chrome/browser/ui/autofill/payments/autofill_ui_util.cc
@@ -54,15 +54,7 @@
             ->GetOmniboxPageActionIconContainer()
             ->UpdatePageActionIcon(icon_type);
         break;
-      case PageActionIconType::kClickToCall:
-      case PageActionIconType::kFind:
-      case PageActionIconType::kIntentPicker:
-      case PageActionIconType::kNativeFileSystemAccess:
-      case PageActionIconType::kPwaInstall:
-      case PageActionIconType::kReaderMode:
-      case PageActionIconType::kSendTabToSelf:
-      case PageActionIconType::kTranslate:
-      case PageActionIconType::kZoom:
+      default:
         NOTREACHED();
     }
   }
diff --git a/chrome/browser/ui/bluetooth/bluetooth_chooser_controller.cc b/chrome/browser/ui/bluetooth/bluetooth_chooser_controller.cc
index 7112521..83687a7 100644
--- a/chrome/browser/ui/bluetooth/bluetooth_chooser_controller.cc
+++ b/chrome/browser/ui/bluetooth/bluetooth_chooser_controller.cc
@@ -19,6 +19,11 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/ui/settings_window_manager_chromeos.h"
+#include "chrome/common/webui_url_constants.h"
+#endif  // defined(OS_CHROMEOS)
+
 namespace {
 
 Browser* GetBrowser() {
@@ -101,10 +106,17 @@
 }
 
 void BluetoothChooserController::OpenAdapterOffHelpUrl() const {
+#if defined(OS_CHROMEOS)
+  // Chrome OS can directly link to the OS setting to turn on the adapter.
+  chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
+      GetBrowser()->profile(), chrome::kBluetoothSubPage);
+#else
+  // For other operating systems, show a help center page in a tab.
   GetBrowser()->OpenURL(content::OpenURLParams(
       GURL(chrome::kBluetoothAdapterOffHelpURL), content::Referrer(),
       WindowOpenDisposition::NEW_FOREGROUND_TAB,
       ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false /* is_renderer_initialized */));
+#endif
 }
 
 base::string16 BluetoothChooserController::GetStatus() const {
diff --git a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
index 4225b7f..2acda4a3 100644
--- a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
@@ -205,11 +205,6 @@
   return false;
 }
 
-bool HostedAppBrowserController::HasTitlebarToolbar() const {
-  // System Web Apps don't have a toolbar.
-  return IsForWebAppBrowser(browser()) && !IsForSystemWebApp();
-}
-
 gfx::ImageSkia HostedAppBrowserController::GetWindowAppIcon() const {
   // TODO(calamity): Use the app name to retrieve the app icon without using the
   // extensions tab helper to make icon load more immediate.
diff --git a/chrome/browser/ui/extensions/hosted_app_browser_controller.h b/chrome/browser/ui/extensions/hosted_app_browser_controller.h
index cb80e12..a642f8a 100644
--- a/chrome/browser/ui/extensions/hosted_app_browser_controller.h
+++ b/chrome/browser/ui/extensions/hosted_app_browser_controller.h
@@ -43,7 +43,6 @@
   base::Optional<std::string> GetAppId() const override;
   bool CreatedForInstalledPwa() const override;
   bool ShouldShowCustomTabBar() const override;
-  bool HasTitlebarToolbar() const override;
   gfx::ImageSkia GetWindowAppIcon() const override;
   gfx::ImageSkia GetWindowIcon() const override;
   base::Optional<SkColor> GetThemeColor() const override;
diff --git a/chrome/browser/ui/managed_ui.cc b/chrome/browser/ui/managed_ui.cc
index cc0fec5..366d941 100644
--- a/chrome/browser/ui/managed_ui.cc
+++ b/chrome/browser/ui/managed_ui.cc
@@ -93,9 +93,6 @@
 
   std::vector<base::string16> replacements;
   replacements.push_back(base::UTF8ToUTF16(chrome::kChromeUIManagementURL));
-#if defined(OS_CHROMEOS)
-  replacements.push_back(ui::GetChromeOSDeviceName());
-#endif
   if (!management_domain.empty()) {
     string_id = IDS_MANAGED_BY_WITH_HYPERLINK;
     replacements.push_back(base::UTF8ToUTF16(management_domain));
@@ -104,4 +101,23 @@
   return l10n_util::GetStringFUTF16(string_id, replacements, nullptr);
 }
 
+#if defined(OS_CHROMEOS)
+base::string16 GetDeviceManagedUiWebUILabel(Profile* profile) {
+  std::string management_domain =
+      ManagementUIHandler::GetAccountDomain(profile);
+
+  int string_id = IDS_DEVICE_MANAGED_WITH_HYPERLINK;
+
+  std::vector<base::string16> replacements;
+  replacements.push_back(base::UTF8ToUTF16(chrome::kChromeUIManagementURL));
+  replacements.push_back(ui::GetChromeOSDeviceName());
+  if (!management_domain.empty()) {
+    string_id = IDS_DEVICE_MANAGED_BY_WITH_HYPERLINK;
+    replacements.push_back(base::UTF8ToUTF16(management_domain));
+  }
+
+  return l10n_util::GetStringFUTF16(string_id, replacements, nullptr);
+}
+#endif
+
 }  // namespace chrome
diff --git a/chrome/browser/ui/managed_ui.h b/chrome/browser/ui/managed_ui.h
index 2fc7db70..7943559 100644
--- a/chrome/browser/ui/managed_ui.h
+++ b/chrome/browser/ui/managed_ui.h
@@ -26,10 +26,16 @@
 // The label for the App Menu item for Managed UI.
 base::string16 GetManagedUiMenuItemLabel(Profile* profile);
 
-// The label for the WebUI footnote for Managed UI. These strings contain HTML
-// for an <a> element.
+// The label for the WebUI footnote for Managed UI indicating that the browser
+// is managed. These strings contain HTML for an <a> element.
 base::string16 GetManagedUiWebUILabel(Profile* profile);
 
+#if defined(OS_CHROMEOS)
+// The label for the WebUI footnote for Managed UI indicating that the device
+// is mananged. These strings contain HTML for an <a> element.
+base::string16 GetDeviceManagedUiWebUILabel(Profile* profile);
+#endif
+
 }  // namespace chrome
 
 #endif  // CHROME_BROWSER_UI_MANAGED_UI_H_
diff --git a/chrome/browser/ui/managed_ui_browsertest.cc b/chrome/browser/ui/managed_ui_browsertest.cc
index 2a30ddd..4a91ac0 100644
--- a/chrome/browser/ui/managed_ui_browsertest.cc
+++ b/chrome/browser/ui/managed_ui_browsertest.cc
@@ -76,7 +76,6 @@
   builder_with_domain.SetProfileName("foobar@example.com");
   auto profile_with_domain = builder_with_domain.Build();
 
-#if !defined(OS_CHROMEOS)
   EXPECT_EQ(
       base::UTF8ToUTF16(
           "Your <a href=\"chrome://management\">browser is managed</a> by your "
@@ -87,14 +86,14 @@
           "Your <a href=\"chrome://management\">browser is managed</a> by "
           "example.com"),
       chrome::GetManagedUiWebUILabel(profile_with_domain.get()));
-#else
-  EXPECT_EQ(
-      base::UTF8ToUTF16("Your <a href=\"chrome://management\">Chrome device is "
-                        "managed</a> by your organization"),
-      chrome::GetManagedUiWebUILabel(profile.get()));
-  EXPECT_EQ(
-      base::UTF8ToUTF16("Your <a href=\"chrome://management\">Chrome device is "
-                        "managed</a> by example.com"),
-      chrome::GetManagedUiWebUILabel(profile_with_domain.get()));
+#if defined(OS_CHROMEOS)
+  EXPECT_EQ(base::UTF8ToUTF16("Your <a target=\"_blank\" "
+                              "href=\"chrome://management\">Chrome device is "
+                              "managed</a> by your organization"),
+            chrome::GetDeviceManagedUiWebUILabel(profile.get()));
+  EXPECT_EQ(base::UTF8ToUTF16("Your <a target=\"_blank\" "
+                              "href=\"chrome://management\">Chrome device is "
+                              "managed</a> by example.com"),
+            chrome::GetDeviceManagedUiWebUILabel(profile_with_domain.get()));
 #endif
 }
diff --git a/chrome/browser/ui/manifest_web_app_browser_controller.cc b/chrome/browser/ui/manifest_web_app_browser_controller.cc
index b75e2e7..59bd327 100644
--- a/chrome/browser/ui/manifest_web_app_browser_controller.cc
+++ b/chrome/browser/ui/manifest_web_app_browser_controller.cc
@@ -52,10 +52,6 @@
   return false;
 }
 
-bool ManifestWebAppBrowserController::HasTitlebarToolbar() const {
-  return true;
-}
-
 gfx::ImageSkia ManifestWebAppBrowserController::GetWindowAppIcon() const {
   gfx::ImageSkia page_icon = browser()->GetCurrentPageIcon().AsImageSkia();
   if (!page_icon.isNull())
diff --git a/chrome/browser/ui/manifest_web_app_browser_controller.h b/chrome/browser/ui/manifest_web_app_browser_controller.h
index 9fc30d2..9d3a795 100644
--- a/chrome/browser/ui/manifest_web_app_browser_controller.h
+++ b/chrome/browser/ui/manifest_web_app_browser_controller.h
@@ -32,7 +32,6 @@
   // web_app::AppBrowserController:
   base::Optional<std::string> GetAppId() const override;
   bool ShouldShowCustomTabBar() const override;
-  bool HasTitlebarToolbar() const override;
   gfx::ImageSkia GetWindowAppIcon() const override;
   gfx::ImageSkia GetWindowIcon() const override;
   std::string GetAppShortName() const override;
diff --git a/chrome/browser/ui/media_router/presentation_receiver_window_controller_browsertest.cc b/chrome/browser/ui/media_router/presentation_receiver_window_controller_browsertest.cc
index 92e9bf4e..0d8f9a34 100644
--- a/chrome/browser/ui/media_router/presentation_receiver_window_controller_browsertest.cc
+++ b/chrome/browser/ui/media_router/presentation_receiver_window_controller_browsertest.cc
@@ -27,7 +27,10 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/browser/script_executor.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/filename_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom.h"
@@ -88,11 +91,11 @@
 class FakeControllerConnection final
     : public blink::mojom::PresentationConnection {
  public:
-  FakeControllerConnection() : binding_(this) {}
+  FakeControllerConnection() {}
 
   void SendTextMessage(const std::string& message) {
-    ASSERT_TRUE(receiver_connection_.is_bound());
-    receiver_connection_->OnMessage(
+    ASSERT_TRUE(receiver_connection_remote_.is_bound());
+    receiver_connection_remote_->OnMessage(
         blink::mojom::PresentationConnectionMessage::NewMessage(message));
   }
 
@@ -104,18 +107,22 @@
   void DidClose(
       blink::mojom::PresentationConnectionCloseReason reason) override {}
 
-  blink::mojom::PresentationConnectionRequest MakeConnectionRequest() {
-    return mojo::MakeRequest(&receiver_connection_);
+  mojo::PendingReceiver<blink::mojom::PresentationConnection>
+  MakeConnectionRequest() {
+    return receiver_connection_remote_.BindNewPipeAndPassReceiver();
   }
-  blink::mojom::PresentationConnectionPtr Bind() {
-    blink::mojom::PresentationConnectionPtr connection;
-    binding_.Bind(mojo::MakeRequest(&connection));
+  mojo::PendingRemote<blink::mojom::PresentationConnection> Bind() {
+    mojo::PendingRemote<blink::mojom::PresentationConnection> connection;
+    receiver_connection_receiver_.Bind(
+        connection.InitWithNewPipeAndPassReceiver());
     return connection;
   }
 
  private:
-  mojo::Binding<blink::mojom::PresentationConnection> binding_;
-  blink::mojom::PresentationConnectionPtr receiver_connection_;
+  mojo::Receiver<blink::mojom::PresentationConnection>
+      receiver_connection_receiver_{this};
+  mojo::Remote<blink::mojom::PresentationConnection>
+      receiver_connection_remote_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeControllerConnection);
 };
diff --git a/chrome/browser/ui/passwords/credential_leak_dialog_utils.cc b/chrome/browser/ui/passwords/credential_leak_dialog_utils.cc
index 599d8c50..69626bce 100644
--- a/chrome/browser/ui/passwords/credential_leak_dialog_utils.cc
+++ b/chrome/browser/ui/passwords/credential_leak_dialog_utils.cc
@@ -4,11 +4,15 @@
 
 #include "chrome/browser/ui/passwords/credential_leak_dialog_utils.h"
 
+#include "base/metrics/field_trial_params.h"
+#include "chrome/common/url_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
+#include "components/password_manager/core/common/password_manager_features.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/url_formatter/elide_url.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
 #include "url/origin.h"
 
 using password_manager::CredentialLeakFlags;
@@ -59,4 +63,11 @@
   return ShouldCheckPasswords(leak_type);
 }
 
+GURL GetPasswordCheckupURL() {
+  std::string value = base::GetFieldTrialParamValueByFeature(
+      password_manager::features::kLeakDetection, "leak-check-url");
+  if (value.empty())
+    return GURL(chrome::kPasswordCheckupURL);
+  return GURL(value);
+}
 }  // namespace leak_dialog_utils
diff --git a/chrome/browser/ui/passwords/credential_leak_dialog_utils.h b/chrome/browser/ui/passwords/credential_leak_dialog_utils.h
index db8b50e7..6734bf9 100644
--- a/chrome/browser/ui/passwords/credential_leak_dialog_utils.h
+++ b/chrome/browser/ui/passwords/credential_leak_dialog_utils.h
@@ -8,8 +8,7 @@
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
-
-class GURL;
+#include "url/gurl.h"
 
 namespace leak_dialog_utils {
 
@@ -33,6 +32,9 @@
 // Checks whether the leak dialog should show cancel button.
 bool ShouldShowCancelButton(password_manager::CredentialLeakType leak_type);
 
+// Returns the URL used to launch the password checkup.
+GURL GetPasswordCheckupURL();
+
 }  // namespace leak_dialog_utils
 
 #endif  // CHROME_BROWSER_UI_PASSWORDS_CREDENTIAL_LEAK_DIALOG_UTILS_H_
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
index 55fb5cd4..3e87eb4 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
@@ -85,7 +85,6 @@
 ManagePasswordsUIController::ManagePasswordsUIController(
     content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents),
-      bubble_status_(NOT_SHOWN),
       are_passwords_revealed_when_next_bubble_is_opened_(false) {
   passwords_data_.set_client(
       ChromePasswordManagerClient::FromWebContents(web_contents));
@@ -101,7 +100,7 @@
     std::unique_ptr<PasswordFormManagerForUI> form_manager) {
   // If the save bubble is already shown (possibly manual fallback for saving)
   // then ignore the changes because the user may interact with it right now.
-  if (bubble_status_ == SHOWN &&
+  if (bubble_status_ == BubbleStatus::SHOWN &&
       GetState() == password_manager::ui::PENDING_PASSWORD_STATE)
     return;
   bool show_bubble = !form_manager->IsBlacklisted();
@@ -117,7 +116,7 @@
       show_bubble = false;
   }
   if (show_bubble)
-    bubble_status_ = SHOULD_POP_UP;
+    bubble_status_ = BubbleStatus::SHOULD_POP_UP;
   UpdateBubbleAndIconVisibility();
 }
 
@@ -126,7 +125,7 @@
   DestroyAccountChooser();
   save_fallback_timer_.Stop();
   passwords_data_.OnUpdatePassword(std::move(form_manager));
-  bubble_status_ = SHOULD_POP_UP;
+  bubble_status_ = BubbleStatus::SHOULD_POP_UP;
   UpdateBubbleAndIconVisibility();
 }
 
@@ -155,7 +154,7 @@
     return;
   }
   // Don't hide the fallback if the bubble is open.
-  if (bubble_status_ == SHOWN || bubble_status_ == SHOWN_PENDING_ICON_UPDATE)
+  if (IsShowingBubble())
     return;
 
   save_fallback_timer_.Stop();
@@ -200,7 +199,7 @@
   DCHECK(!local_forms.empty());
   DestroyAccountChooser();
   passwords_data_.OnAutoSignin(std::move(local_forms), origin);
-  bubble_status_ = SHOULD_POP_UP;
+  bubble_status_ = BubbleStatus::SHOULD_POP_UP;
   UpdateBubbleAndIconVisibility();
 }
 
@@ -219,7 +218,7 @@
   DestroyAccountChooser();
   save_fallback_timer_.Stop();
   passwords_data_.OnAutomaticPasswordSave(std::move(form_manager));
-  bubble_status_ = SHOULD_POP_UP;
+  bubble_status_ = BubbleStatus::SHOULD_POP_UP;
   UpdateBubbleAndIconVisibility();
 }
 
@@ -236,9 +235,9 @@
     passwords_data_.OnPasswordAutofilled(password_form_map, origin,
                                          federated_matches);
     // Don't close the existing bubble. Update the icon later.
-    if (bubble_status_ == SHOWN)
-      bubble_status_ = SHOWN_PENDING_ICON_UPDATE;
-    if (bubble_status_ != SHOWN_PENDING_ICON_UPDATE)
+    if (bubble_status_ == BubbleStatus::SHOWN)
+      bubble_status_ = BubbleStatus::SHOWN_PENDING_ICON_UPDATE;
+    if (bubble_status_ != BubbleStatus::SHOWN_PENDING_ICON_UPDATE)
       UpdateBubbleAndIconVisibility();
   }
 }
@@ -247,9 +246,15 @@
     const password_manager::CredentialLeakType leak_type,
     const GURL& origin) {
   // Existing dialog shouldn't be closed.
-  if (dialog_controller_) {
+  if (dialog_controller_)
     return;
-  }
+
+  // Hide the manage passwords bubble if currently shown.
+  if (IsShowingBubble())
+    TabDialogs::FromWebContents(web_contents())->HideManagePasswordsBubble();
+  else
+    ClearPopUpFlagForBubble();
+
   auto* raw_controller =
       new CredentialLeakDialogControllerImpl(this, leak_type, origin);
   dialog_controller_.reset(raw_controller);
@@ -359,12 +364,13 @@
 
 void ManagePasswordsUIController::OnBubbleShown() {
   are_passwords_revealed_when_next_bubble_is_opened_ = false;
-  bubble_status_ = SHOWN;
+  bubble_status_ = BubbleStatus::SHOWN;
 }
 
 void ManagePasswordsUIController::OnBubbleHidden() {
-  bool update_icon = (bubble_status_ == SHOWN_PENDING_ICON_UPDATE);
-  bubble_status_ = NOT_SHOWN;
+  bool update_icon =
+      (bubble_status_ == BubbleStatus::SHOWN_PENDING_ICON_UPDATE);
+  bubble_status_ = BubbleStatus::NOT_SHOWN;
   if (GetState() == password_manager::ui::CONFIRMATION_STATE ||
       GetState() == password_manager::ui::AUTO_SIGNIN_STATE) {
     passwords_data_.TransitionToState(password_manager::ui::MANAGE_STATE);
@@ -430,7 +436,7 @@
   passwords_data_.TransitionToState(password_manager::ui::MANAGE_STATE);
   // The icon is to be updated after the bubble (either "Save password" or "Sign
   // in to Chrome") is closed.
-  bubble_status_ = SHOWN_PENDING_ICON_UPDATE;
+  bubble_status_ = BubbleStatus::SHOWN_PENDING_ICON_UPDATE;
 }
 
 void ManagePasswordsUIController::ChooseCredential(
@@ -565,8 +571,7 @@
 
   // Keep the state if the bubble is currently open or the fallback for saving
   // should be still available.
-  if (bubble_status_ == SHOWN || bubble_status_ == SHOWN_PENDING_ICON_UPDATE ||
-      save_fallback_timer_.IsRunning()) {
+  if (IsShowingBubble() || save_fallback_timer_.IsRunning()) {
     return;
   }
 
@@ -601,7 +606,7 @@
 
 void ManagePasswordsUIController::ClearPopUpFlagForBubble() {
   if (ShouldBubblePopUp())
-    bubble_status_ = NOT_SHOWN;
+    bubble_status_ = BubbleStatus::NOT_SHOWN;
 }
 
 void ManagePasswordsUIController::DestroyAccountChooser() {
@@ -638,7 +643,7 @@
     return;
   if (auth_is_successful)
     are_passwords_revealed_when_next_bubble_is_opened_ = true;
-  bubble_status_ = SHOULD_POP_UP_AFTER_REAUTH;
+  bubble_status_ = BubbleStatus::SHOULD_POP_UP_AFTER_REAUTH;
   UpdateBubbleAndIconVisibility();
 }
 
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
index 19ada7d..38a34edf 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
@@ -102,7 +102,7 @@
 
   // True iff the bubble is to be opened automatically.
   bool IsAutomaticallyOpeningBubble() const {
-    return bubble_status_ == SHOULD_POP_UP;
+    return bubble_status_ == BubbleStatus::SHOULD_POP_UP;
   }
 
   base::WeakPtr<PasswordsModelDelegate> GetModelDelegateProxy();
@@ -181,13 +181,11 @@
   // True if the bubble is to be opened automatically or after re-auth.
   bool ShouldBubblePopUp() const {
     return IsAutomaticallyOpeningBubble() ||
-           bubble_status_ == SHOULD_POP_UP_AFTER_REAUTH;
+           bubble_status_ == BubbleStatus::SHOULD_POP_UP_AFTER_REAUTH;
   }
 
   // Returns whether the bubble is currently open.
-  bool IsShowingBubbleForTest() const {
-    return bubble_status_ != BubbleStatus::NOT_SHOWN;
-  }
+  bool IsShowingBubbleForTest() const { return IsShowingBubble(); }
 
   // content::WebContentsObserver:
   void DidFinishNavigation(
@@ -201,7 +199,7 @@
   void NavigateToPasswordCheckup() override;
   void OnLeakDialogHidden() override;
 
-  enum BubbleStatus {
+  enum class BubbleStatus {
     NOT_SHOWN,
     // The bubble is to be popped up in the next call to
     // UpdateBubbleAndIconVisibility().
@@ -213,6 +211,11 @@
     SHOWN_PENDING_ICON_UPDATE,
   };
 
+  bool IsShowingBubble() const {
+    return bubble_status_ == BubbleStatus::SHOWN ||
+           bubble_status_ == BubbleStatus::SHOWN_PENDING_ICON_UPDATE;
+  }
+
   // Returns the timeout for the manual save fallback.
   static base::TimeDelta GetTimeoutForSaveFallback();
 
@@ -250,7 +253,7 @@
   // The controller for the blocking dialogs.
   std::unique_ptr<PasswordBaseDialogController> dialog_controller_;
 
-  BubbleStatus bubble_status_;
+  BubbleStatus bubble_status_ = BubbleStatus::NOT_SHOWN;
 
   // The timer that controls whether the fallback for saving should be
   // available. Should be reset once the fallback is not needed (an automatic
diff --git a/chrome/browser/ui/passwords/manage_passwords_view_utils.cc b/chrome/browser/ui/passwords/manage_passwords_view_utils.cc
index 10125ef..dfbd6c20 100644
--- a/chrome/browser/ui/passwords/manage_passwords_view_utils.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_view_utils.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/passwords/credential_leak_dialog_utils.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/chromium_strings.h"
@@ -256,8 +257,7 @@
 }
 
 void NavigateToPasswordCheckupPage(Profile* profile) {
-  NavigateParams params(profile,
-                        GURL(password_manager::kPasswordManagerCheckupURL),
+  NavigateParams params(profile, leak_dialog_utils::GetPasswordCheckupURL(),
                         ui::PAGE_TRANSITION_LINK);
   params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
   Navigate(&params);
diff --git a/chrome/browser/ui/views/constrained_window_views_browsertest.cc b/chrome/browser/ui/views/constrained_window_views_browsertest.cc
index e164de9..ebe3b61e 100644
--- a/chrome/browser/ui/views/constrained_window_views_browsertest.cc
+++ b/chrome/browser/ui/views/constrained_window_views_browsertest.cc
@@ -134,7 +134,13 @@
 
 // Tests that the tab-modal window is hidden when an other tab is selected and
 // shown when its tab is selected again.
-IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, TabSwitchTest) {
+// Flaky on ASAN builds (https://crbug.com/997634)
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_TabSwitchTest DISABLED_TabSwitchTest
+#else
+#define MAYBE_TabSwitchTest TabSwitchTest
+#endif
+IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, MAYBE_TabSwitchTest) {
   std::unique_ptr<TestDialog> dialog =
       ShowModalDialog(browser()->tab_strip_model()->GetActiveWebContents());
   EXPECT_TRUE(dialog->GetWidget()->IsVisible());
diff --git a/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc b/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc
index c28d044..70c2173 100644
--- a/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc
+++ b/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc
@@ -747,8 +747,8 @@
   DISALLOW_COPY_AND_ASSIGN(DragAndDropBrowserTest);
 };
 
-#if defined(OS_CHROMEOS) || defined(OS_WIN)
-// Flaky: https://crbug.com/835774, https://crbug.com/988938
+#if defined(OS_WIN)
+// Flaky: https://crbug.com/988938
 #define MAYBE_DropTextFromOutside DISABLED_DropTextFromOutside
 #else
 #define MAYBE_DropTextFromOutside DropTextFromOutside
@@ -789,8 +789,8 @@
   }
 }
 
-#if defined(OS_CHROMEOS) || !defined(NDEBUG) || defined(OS_WIN)
-// Flaky: https://crbug.com/835774, https://crbug.com/988938
+#if !defined(NDEBUG) || defined(OS_WIN)
+// Flaky: https://crbug.com/988938
 #define MAYBE_DragStartInFrame DISABLED_DragStartInFrame
 #else
 #define MAYBE_DragStartInFrame DragStartInFrame
@@ -860,8 +860,7 @@
 // There is no known way to execute test-controlled tasks during
 // a drag-and-drop loop run by Windows OS.
 #define MAYBE_DragImageBetweenFrames DISABLED_DragImageBetweenFrames
-#elif defined(OS_CHROMEOS) || defined(OS_LINUX)
-// Flakiness on CrOS tracked by https://crbug.com/835573.
+#elif defined(OS_LINUX)
 #define MAYBE_DragImageBetweenFrames DISABLED_DragImageBetweenFrames
 #else
 #define MAYBE_DragImageBetweenFrames DragImageBetweenFrames
@@ -1079,10 +1078,6 @@
 // a drag-and-drop loop run by Windows OS.
 #define MAYBE_DragImageFromDisappearingFrame \
   DISABLED_DragImageFromDisappearingFrame
-#elif defined(OS_CHROMEOS)
-// Flakiness on CrOS tracked by https://crbug.com/835572.
-#define MAYBE_DragImageFromDisappearingFrame \
-  DISABLED_DragImageFromDisappearingFrame
 #else
 #define MAYBE_DragImageFromDisappearingFrame DragImageFromDisappearingFrame
 #endif
@@ -1198,9 +1193,6 @@
 // a drag-and-drop loop run by Windows OS.
 #if defined(OS_WIN)
 #define MAYBE_CrossSiteDrag DISABLED_CrossSiteDrag
-#elif defined(OS_CHROMEOS)
-// Flaky: https://crbug.com/835774
-#define MAYBE_CrossSiteDrag DISABLED_CrossSiteDrag
 #else
 #define MAYBE_CrossSiteDrag CrossSiteDrag
 #endif
diff --git a/chrome/browser/ui/views/frame/hosted_app_button_container.cc b/chrome/browser/ui/views/frame/hosted_app_button_container.cc
index eda720b6..68daa62 100644
--- a/chrome/browser/ui/views/frame/hosted_app_button_container.cc
+++ b/chrome/browser/ui/views/frame/hosted_app_button_container.cc
@@ -202,13 +202,18 @@
   layout.set_cross_axis_alignment(
       views::BoxLayout::CrossAxisAlignment::kCenter);
 
-  hosted_app_origin_text_ = AddChildView(
-      std::make_unique<HostedAppOriginText>(browser_view->browser()));
+  auto* app_controller = browser_view_->browser()->app_controller();
+  if (app_controller->HasTitlebarAppOriginText()) {
+    hosted_app_origin_text_ = AddChildView(
+        std::make_unique<HostedAppOriginText>(browser_view->browser()));
+  }
 
-  content_settings_container_ =
-      AddChildView(std::make_unique<ContentSettingsContainer>(this));
-  views::SetHitTestComponent(content_settings_container_,
-                             static_cast<int>(HTCLIENT));
+  if (app_controller->HasTitlebarContentSettings()) {
+    content_settings_container_ =
+        AddChildView(std::make_unique<ContentSettingsContainer>(this));
+    views::SetHitTestComponent(content_settings_container_,
+                               static_cast<int>(HTCLIENT));
+  }
 
   OmniboxPageActionIconContainerView::Params params;
   params.types_enabled.push_back(PageActionIconType::kFind);
@@ -259,7 +264,8 @@
 }
 
 void HostedAppButtonContainer::UpdateStatusIconsVisibility() {
-  content_settings_container_->UpdateContentSettingViewsVisibility();
+  if (content_settings_container_)
+    content_settings_container_->UpdateContentSettingViewsVisibility();
   omnibox_page_action_icon_container_view_->UpdateAll();
 }
 
@@ -351,7 +357,8 @@
 void HostedAppButtonContainer::OnImmersiveRevealStarted() {
   // Don't wait for the fade in animation to make content setting icons visible
   // once in immersive mode.
-  content_settings_container_->EnsureVisible();
+  if (content_settings_container_)
+    content_settings_container_->EnsureVisible();
 }
 
 SkColor HostedAppButtonContainer::GetPageActionInkDropColor() const {
@@ -427,7 +434,8 @@
     return;
   pending_widget_visibility_ = false;
   if (ShouldAnimate()) {
-    content_settings_container_->SetUpForFadeIn();
+    if (content_settings_container_)
+      content_settings_container_->SetUpForFadeIn();
     animation_start_delay_.Start(
         FROM_HERE, kTitlebarAnimationDelay, this,
         &HostedAppButtonContainer::StartTitlebarAnimation);
@@ -462,7 +470,8 @@
   if (!ShouldAnimate())
     return;
 
-  hosted_app_origin_text_->StartFadeAnimation();
+  if (hosted_app_origin_text_)
+    hosted_app_origin_text_->StartFadeAnimation();
   app_menu_button_->StartHighlightAnimation();
   icon_fade_in_delay_.Start(
       FROM_HERE, OriginTotalDuration(), this,
@@ -470,7 +479,8 @@
 }
 
 void HostedAppButtonContainer::FadeInContentSettingIcons() {
-  content_settings_container_->FadeIn();
+  if (content_settings_container_)
+    content_settings_container_->FadeIn();
 }
 
 void HostedAppButtonContainer::DisableAnimationForTesting() {
@@ -492,8 +502,10 @@
 
 void HostedAppButtonContainer::UpdateChildrenColor() {
   SkColor icon_color = GetCaptionColor();
-  hosted_app_origin_text_->SetTextColor(icon_color);
-  content_settings_container_->SetIconColor(icon_color);
+  if (hosted_app_origin_text_)
+    hosted_app_origin_text_->SetTextColor(icon_color);
+  if (content_settings_container_)
+    content_settings_container_->SetIconColor(icon_color);
   omnibox_page_action_icon_container_view_->SetIconColor(icon_color);
   app_menu_button_->SetColor(icon_color);
 }
diff --git a/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc b/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc
index dd14fb8..1f3947e1 100644
--- a/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc
+++ b/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc
@@ -15,6 +15,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/passwords/manage_passwords_test.h"
+#include "chrome/browser/ui/passwords/passwords_client_ui_delegate.h"
 #include "chrome/browser/ui/passwords/passwords_model_delegate.h"
 #include "chrome/browser/ui/tab_dialogs.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -382,3 +383,16 @@
 
   EXPECT_FALSE(IsBubbleShowing());
 }
+
+// Test that triggering the leak detection dialog successfully hides a showing
+// bubble.
+IN_PROC_BROWSER_TEST_F(PasswordBubbleInteractiveUiTest, LeakPromptHidesBubble) {
+  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
+  SetupPendingPassword();
+  EXPECT_TRUE(IsBubbleShowing());
+
+  GetController()->OnCredentialLeak(
+      password_manager::CredentialLeakFlags::kPasswordSaved,
+      GURL("https://example.com"));
+  EXPECT_FALSE(IsBubbleShowing());
+}
diff --git a/chrome/browser/ui/web_applications/app_browser_controller.cc b/chrome/browser/ui/web_applications/app_browser_controller.cc
index a975ce5e..db1fe7e 100644
--- a/chrome/browser/ui/web_applications/app_browser_controller.cc
+++ b/chrome/browser/ui/web_applications/app_browser_controller.cc
@@ -96,6 +96,27 @@
                                  SystemAppType::TERMINAL) == GetAppId();
 }
 
+bool AppBrowserController::HasTitlebarToolbar() const {
+  // Show titlebar toolbar for Terminal System App, but not other system apps.
+  // TODO(crbug.com/846546): Generalise this as a SystemWebApp capability.
+  if (IsForSystemWebApp()) {
+    return GetAppIdForSystemWebApp(browser()->profile(),
+                                   SystemAppType::TERMINAL) == GetAppId();
+  }
+  // Show for all other apps.
+  return true;
+}
+
+bool AppBrowserController::HasTitlebarAppOriginText() const {
+  // Do not show origin text for System Apps.
+  return !IsForSystemWebApp();
+}
+
+bool AppBrowserController::HasTitlebarContentSettings() const {
+  // Do not show content settings for System Apps.
+  return !IsForSystemWebApp();
+}
+
 bool AppBrowserController::IsInstalled() const {
   return false;
 }
diff --git a/chrome/browser/ui/web_applications/app_browser_controller.h b/chrome/browser/ui/web_applications/app_browser_controller.h
index e289454a..77574ecc 100644
--- a/chrome/browser/ui/web_applications/app_browser_controller.h
+++ b/chrome/browser/ui/web_applications/app_browser_controller.h
@@ -62,7 +62,13 @@
 
   // Whether the browser toolbar is present.
   // Note: web app windows have their browser toolbar inline in their titlebar.
-  virtual bool HasTitlebarToolbar() const = 0;
+  virtual bool HasTitlebarToolbar() const;
+
+  // Whether to show app origin text in the titlebar toolbar.
+  virtual bool HasTitlebarAppOriginText() const;
+
+  // Whether to show content settings in the titlebar toolbar.
+  virtual bool HasTitlebarContentSettings() const;
 
   // Returns the app icon for the window to use in the task list.
   virtual gfx::ImageSkia GetWindowAppIcon() const = 0;
diff --git a/chrome/browser/ui/web_applications/web_app_browser_controller.cc b/chrome/browser/ui/web_applications/web_app_browser_controller.cc
index 3f923b6..6c53a4b 100644
--- a/chrome/browser/ui/web_applications/web_app_browser_controller.cc
+++ b/chrome/browser/ui/web_applications/web_app_browser_controller.cc
@@ -38,10 +38,6 @@
   return false;
 }
 
-bool WebAppBrowserController::HasTitlebarToolbar() const {
-  return true;
-}
-
 gfx::ImageSkia WebAppBrowserController::GetWindowAppIcon() const {
   // TODO(https://crbug.com/966290): Complete implementation.
   return gfx::ImageSkia();
diff --git a/chrome/browser/ui/web_applications/web_app_browser_controller.h b/chrome/browser/ui/web_applications/web_app_browser_controller.h
index 277d0ad..ae4251d 100644
--- a/chrome/browser/ui/web_applications/web_app_browser_controller.h
+++ b/chrome/browser/ui/web_applications/web_app_browser_controller.h
@@ -38,7 +38,6 @@
   base::Optional<AppId> GetAppId() const override;
   bool CreatedForInstalledPwa() const override;
   bool ShouldShowCustomTabBar() const override;
-  bool HasTitlebarToolbar() const override;
   gfx::ImageSkia GetWindowAppIcon() const override;
   gfx::ImageSkia GetWindowIcon() const override;
   base::Optional<SkColor> GetThemeColor() const override;
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index af5f30e..a7af86e 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -451,15 +451,21 @@
     const user_manager::User* const user =
         user_manager::UserManager::Get()->FindUser(account_id);
     if (user && user->using_saml() &&
-        user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT) {
-      if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-              switches::kPublicAccountsSamlUrl)) {
-        std::string saml_url =
-            base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-                switches::kPublicAccountsSamlUrl);
-        params.SetBoolean("startsOnSamlPage", true);
-        params.SetString("frameUrl", saml_url);
-      }
+        user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT &&
+        base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kPublicAccountsSamlUrl)) {
+      std::string saml_url =
+          base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+              switches::kPublicAccountsSamlUrl);
+      params.SetBoolean("startsOnSamlPage", true);
+      params.SetString("frameUrl", saml_url);
+      params.SetString("email", account_id.GetUserEmail());
+      CHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kPublicAccountsSamlAclUrl));
+      std::string saml_acl_url =
+          base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+              switches::kPublicAccountsSamlAclUrl);
+      params.SetString("samlAclUrl", saml_acl_url);
     }
   }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index 3bdf30d..99b21fec 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -440,6 +440,8 @@
   AddCallback("launchIncognito", &SigninScreenHandler::HandleLaunchIncognito);
   AddCallback("launchPublicSession",
               &SigninScreenHandler::HandleLaunchPublicSession);
+  AddCallback("launchSAMLPublicSession",
+              &SigninScreenHandler::HandleLaunchSAMLPublicSession);
   AddRawCallback("offlineLogin", &SigninScreenHandler::HandleOfflineLogin);
   AddCallback("rebootSystem", &SigninScreenHandler::HandleRebootSystem);
   AddCallback("removeUser", &SigninScreenHandler::HandleRemoveUser);
@@ -1111,6 +1113,14 @@
     delegate_->Login(context, SigninSpecifics());
 }
 
+void SigninScreenHandler::HandleLaunchSAMLPublicSession(
+    const std::string& email) {
+  const AccountId account_id = user_manager::known_user::GetAccountId(
+      email, std::string() /* id */, AccountType::UNKNOWN);
+  SigninScreenHandler::HandleLaunchPublicSession(account_id, std::string(),
+                                                 std::string());
+}
+
 void SigninScreenHandler::HandleLaunchPublicSession(
     const AccountId& account_id,
     const std::string& locale,
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
index 258099e7..6195aa7c 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
@@ -327,6 +327,7 @@
                                            const std::string& password);
   void HandleAttemptUnlock(const std::string& username);
   void HandleLaunchIncognito();
+  void HandleLaunchSAMLPublicSession(const std::string& email);
   void HandleLaunchPublicSession(const AccountId& account_id,
                                  const std::string& locale,
                                  const std::string& input_method);
diff --git a/chrome/browser/ui/webui/managed_ui_handler.cc b/chrome/browser/ui/webui/managed_ui_handler.cc
index 5fd6841e..3d4e6125b 100644
--- a/chrome/browser/ui/webui/managed_ui_handler.cc
+++ b/chrome/browser/ui/webui/managed_ui_handler.cc
@@ -110,8 +110,12 @@
 std::unique_ptr<base::DictionaryValue> ManagedUIHandler::GetDataSourceUpdate()
     const {
   auto update = std::make_unique<base::DictionaryValue>();
-  update->SetKey("managedByOrg",
+  update->SetKey("browserManagedByOrg",
                  base::Value(chrome::GetManagedUiWebUILabel(profile_)));
+#if defined(OS_CHROMEOS)
+  update->SetKey("deviceManagedByOrg",
+                 base::Value(chrome::GetDeviceManagedUiWebUILabel(profile_)));
+#endif
   update->SetKey("isManaged", base::Value(managed_));
   return update;
 }
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 30574ec..4660dfb 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -780,10 +780,6 @@
 const base::Feature kQuickUnlockFingerprint{"QuickUnlockFingerprint",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables or disables the bulk printer policies on Chrome OS.
-const base::Feature kBulkPrinters{"BulkPrinters",
-                                  base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enables or disables flash component updates on Chrome OS.
 const base::Feature kCrosCompUpdates{"CrosCompUpdates",
                                      base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 3d7a4c7..49b6a5c 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -490,8 +490,6 @@
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kQuickUnlockFingerprint;
 
-COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kBulkPrinters;
-
 COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kCrosCompUpdates;
 
 COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kTPMFirmwareUpdate;
diff --git a/chrome/common/media_router/mojom/media_router.mojom b/chrome/common/media_router/mojom/media_router.mojom
index c2ea00f..6573857 100644
--- a/chrome/common/media_router/mojom/media_router.mojom
+++ b/chrome/common/media_router/mojom/media_router.mojom
@@ -244,12 +244,12 @@
 // result.  An MRP may return this as part of any route creation callback to
 // allow direct communication with the controlling page.
 struct RoutePresentationConnection {
-  // Interface pointer to send messages to the MRP.
-  blink.mojom.PresentationConnection connection_ptr;
+  // Remote to send messages to the MRP.
+  pending_remote<blink.mojom.PresentationConnection> connection_remote;
 
-  // Interface request which the controlling page should bind to receive
-  // messages from the MRP.
-  blink.mojom.PresentationConnection& connection_request;
+  // Receiver which the controlling page should bind to receive messages from
+  // the MRP.
+  pending_receiver<blink.mojom.PresentationConnection> connection_receiver;
 };
 
 // Modeled after the MediaRouter interface defined in
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 0ea60e3..d24500c 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -18,11 +18,7 @@
     "https://support.google.com/accounts/?p=ap_faq";
 
 const char kBluetoothAdapterOffHelpURL[] =
-#if defined(OS_CHROMEOS)
-    "chrome://settings/?search=bluetooth";
-#else
     "https://support.google.com/chrome?p=bluetooth";
-#endif
 
 const char kCastCloudServicesHelpURL[] =
     "https://support.google.com/chromecast/?p=casting_cloud_services";
@@ -160,6 +156,17 @@
 
 const char kGooglePasswordManagerURL[] = "https://passwords.google.com";
 
+const char kPasswordCheckupURL[] =
+#if defined(OS_ANDROID)
+    "https://passwords.google.com/checkup/"
+    "start?utm_source=chrome&utm_medium=android&utm_campaign=leak_dialog&crch="
+    "true";
+#else
+    "https://passwords.google.com/checkup/"
+    "start?utm_source=chrome&utm_medium=desktop&utm_campaign=leak_dialog&crch="
+    "true";
+#endif
+
 const char kLearnMoreReportingURL[] =
     "https://support.google.com/chrome/?p=ui_usagestat";
 
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index fa4d8bc..2871d67 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -150,6 +150,9 @@
 // URL of the Google Password Manager.
 extern const char kGooglePasswordManagerURL[];
 
+// URL for Password Checkup.
+extern const char kPasswordCheckupURL[];
+
 // The URL for the "Learn more" page for the usage/crash reporting option in the
 // first run dialog.
 extern const char kLearnMoreReportingURL[];
diff --git a/chrome/installer/mac/sign_chrome.py b/chrome/installer/mac/sign_chrome.py
index 04dbbef..a1de962 100755
--- a/chrome/installer/mac/sign_chrome.py
+++ b/chrome/installer/mac/sign_chrome.py
@@ -94,17 +94,14 @@
     parser.add_argument(
         '--output',
         required=True,
-        help='Path to the output directory. The signed DMG products and '
-        'installer tools will be placed here.')
-
-    group = parser.add_mutually_exclusive_group(required=False)
-    group.add_argument(
-        '--dmg',
-        dest='dmg',
+        help='Path to the output directory. The signed (possibly packaged) '
+        'products and installer tools will be placed here.')
+    parser.add_argument(
+        '--disable-packaging',
+        dest='disable_packaging',
         action='store_true',
-        help='Defaults to True. Package the signed application into a DMG, '
-        'and sign the result.')
-    group.add_argument('--no-dmg', dest='dmg', action='store_false')
+        help='Disable creating any packaging (.dmg/.pkg) specified by the '
+        'configuration.')
 
     group = parser.add_mutually_exclusive_group(required=False)
     group.add_argument(
@@ -115,7 +112,7 @@
         'Apple for notarization.')
     group.add_argument('--no-notarize', dest='notarize', action='store_false')
 
-    parser.set_defaults(dmg=True, notarize=False)
+    parser.set_defaults(notarize=False)
     args = parser.parse_args()
 
     if args.notarize:
@@ -132,7 +129,10 @@
         os.mkdir(paths.output)
 
     pipeline.sign_all(
-        paths, config, package_dmg=args.dmg, do_notarization=args.notarize)
+        paths,
+        config,
+        disable_packaging=args.disable_packaging,
+        do_notarization=args.notarize)
 
 
 if __name__ == '__main__':
diff --git a/chrome/installer/mac/signing/README.md b/chrome/installer/mac/signing/README.md
index 70a577a0..9ce676f 100644
--- a/chrome/installer/mac/signing/README.md
+++ b/chrome/installer/mac/signing/README.md
@@ -1,23 +1,28 @@
 # Signing Scripts for Chrome on macOS
 
-This directory contains Python modules that modify the Chrome application bundle for various
-release channels, sign the resulting bundle, package it into a DMG for distribution, and sign the
-resulting DMG.
+This directory contains Python modules that modify the Chrome application bundle
+for various release channels, sign the resulting bundle, package it into
+`.dmg`/`.pkg` files for distribution, and sign those resulting `.dmg`/`.pkg`
+files.
 
 ## Invoking
 
-The scripts are invoked using the driver located at `//chrome/installer/mac/sign_chrome.py`.
+The scripts are invoked using the driver located at
+`//chrome/installer/mac/sign_chrome.py`.
 
-The DMG packaging aspect of the process require assets that are only available in a src-internal
-checkout, so these scripts will not fully succeed if invoked from a Chromium-only checkout. The
-signing-only aspects can be run without the DMG packaging by passing `--no-dmg`.
+The `.dmg` packaging aspect of the process require assets that are only available
+in a `src-internal` checkout, so these scripts will not fully succeed if invoked
+from a Chromium-only checkout. The signing-only aspects can be run without the
+`.dmg` packaging by passing `--disable-packaging`.
 
 ## Running Tests
 
-Simply run the wrapper script at `//chrome/installer/mac/signing/run_mac_signing_tests.py`.
+Simply run the wrapper script at
+`//chrome/installer/mac/signing/run_mac_signing_tests.py`.
 
-You can pass `--coverage` or `-c` to show coverage information. To generate a HTML coverage report
-and Python coverage package is available (via `pip install coverage`), run:
+You can pass `--coverage` or `-c` to show coverage information. To generate a
+HTML coverage report and Python coverage package is available (via `pip install
+coverage`), run:
 
     coverage3 run -m unittest discover -p '*_test.py'
     coverage3 html
diff --git a/chrome/installer/mac/signing/config.py.in b/chrome/installer/mac/signing/config.py.in
index 9e6f29c7..40fc81c1 100644
--- a/chrome/installer/mac/signing/config.py.in
+++ b/chrome/installer/mac/signing/config.py.in
@@ -147,8 +147,8 @@
         return None
 
     @property
-    def dmg_basename(self):
-        """Returns the file basename of the packaged DMG."""
+    def packaging_basename(self):
+        """Returns the file basename of the packaged output files."""
         return '{}-{}'.format(self.app_product.replace(' ', ''), self.version)
 
     @property
diff --git a/chrome/installer/mac/signing/model.py b/chrome/installer/mac/signing/model.py
index 5107735..2c694d1 100644
--- a/chrome/installer/mac/signing/model.py
+++ b/chrome/installer/mac/signing/model.py
@@ -166,10 +166,12 @@
                  channel=None,
                  branding_code=None,
                  app_name_fragment=None,
-                 dmg_name_fragment=None,
+                 packaging_name_fragment=None,
                  product_dirname=None,
                  creator_code=None,
-                 channel_customize=False):
+                 channel_customize=False,
+                 package_as_dmg=True,
+                 package_as_pkg=False):
         """Creates a new Distribution object. All arguments are optional.
 
         Args:
@@ -179,8 +181,8 @@
             app_name_fragment: If present, this string fragment is appended to
                 the |config.CodeSignConfig.app_product|. This renames the binary
                 and outer app bundle.
-            dmg_name_fragment: If present, this is appended to the
-                |config.CodeSignConfig.dmg_basename| to help differentiate
+            packaging_name_fragment: If present, this is appended to the
+                |config.CodeSignConfig.packaging_basename| to help differentiate
                 different |branding_code|s.
             product_dirname: If present, this string value is set in the app's
                 Info.plist with the key "CrProductDirName". This key influences
@@ -195,14 +197,20 @@
                   |config.CodeSignConfig.base_bundle_id|.
                 - The product will be renamed with |app_name_fragment|.
                 - Different assets will be used for icons in the app.
+            package_as_dmg: If True, then a .dmg file will be created containing
+                the product.
+            package_as_pkg: If True, then a .pkg file will be created containing
+                the product.
         """
         self.channel = channel
         self.branding_code = branding_code
         self.app_name_fragment = app_name_fragment
-        self.dmg_name_fragment = dmg_name_fragment
+        self.packaging_name_fragment = packaging_name_fragment
         self.product_dirname = product_dirname
         self.creator_code = creator_code
         self.channel_customize = channel_customize
+        self.package_as_dmg = package_as_dmg
+        self.package_as_pkg = package_as_pkg
 
     def to_config(self, base_config):
         """Produces a derived |config.CodeSignConfig| for the Distribution.
@@ -248,12 +256,13 @@
                 return profile
 
             @property
-            def dmg_basename(self):
-                if this.dmg_name_fragment:
+            def packaging_basename(self):
+                if this.packaging_name_fragment:
                     return '{}-{}-{}'.format(
                         self.app_product.replace(' ', ''), self.version,
-                        this.dmg_name_fragment)
-                return super(DistributionCodeSignConfig, self).dmg_basename
+                        this.packaging_name_fragment)
+                return super(DistributionCodeSignConfig,
+                             self).packaging_basename
 
         return DistributionCodeSignConfig(
             base_config.identity, base_config.keychain, base_config.notary_user,
diff --git a/chrome/installer/mac/signing/model_test.py b/chrome/installer/mac/signing/model_test.py
index 6aa9470c..d816ba6 100644
--- a/chrome/installer/mac/signing/model_test.py
+++ b/chrome/installer/mac/signing/model_test.py
@@ -76,7 +76,8 @@
         self.assertEqual(base_config.base_bundle_id, config.base_bundle_id)
         self.assertEqual(base_config.provisioning_profile_basename,
                          config.provisioning_profile_basename)
-        self.assertEqual(base_config.dmg_basename, config.dmg_basename)
+        self.assertEqual(base_config.packaging_basename,
+                         config.packaging_basename)
 
     def test_channel_no_customize(self):
         base_config = TestConfig()
@@ -87,7 +88,8 @@
         self.assertEqual(base_config.base_bundle_id, config.base_bundle_id)
         self.assertEqual(base_config.provisioning_profile_basename,
                          config.provisioning_profile_basename)
-        self.assertEqual(base_config.dmg_basename, config.dmg_basename)
+        self.assertEqual(base_config.packaging_basename,
+                         config.packaging_basename)
 
     def test_channel_customize(self):
         base_config = TestConfig()
@@ -99,24 +101,26 @@
         self.assertEqual('test.signing.bundle_id.beta', config.base_bundle_id)
         self.assertEqual('provisiontest_Beta',
                          config.provisioning_profile_basename)
-        self.assertEqual('AppProductBeta-99.0.9999.99', config.dmg_basename)
+        self.assertEqual('AppProductBeta-99.0.9999.99',
+                         config.packaging_basename)
 
-    def test_dmg_basename_no_channel(self):
+    def test_packaging_basename_no_channel(self):
         base_config = TestConfig()
         config = model.Distribution(
-            dmg_name_fragment='Canary').to_config(base_config)
+            packaging_name_fragment='Canary').to_config(base_config)
         self.assertEqual(base_config, config.base_config)
         self.assertEqual(base_config.app_product, config.app_product)
         self.assertEqual(base_config.base_bundle_id, config.base_bundle_id)
         self.assertEqual(base_config.provisioning_profile_basename,
                          config.provisioning_profile_basename)
-        self.assertEqual('AppProduct-99.0.9999.99-Canary', config.dmg_basename)
+        self.assertEqual('AppProduct-99.0.9999.99-Canary',
+                         config.packaging_basename)
 
-    def test_dmg_basename_channel(self):
+    def test_packaging_basename_channel(self):
         dist = model.Distribution(
             channel='dev',
             app_name_fragment='Dev',
-            dmg_name_fragment='Dev',
+            packaging_name_fragment='Dev',
             channel_customize=True)
         config = dist.to_config(TestConfig())
         self.assertEqual('App Product Dev', config.app_product)
@@ -124,7 +128,8 @@
         self.assertEqual('test.signing.bundle_id.dev', config.base_bundle_id)
         self.assertEqual('provisiontest_Dev',
                          config.provisioning_profile_basename)
-        self.assertEqual('AppProductDev-99.0.9999.99-Dev', config.dmg_basename)
+        self.assertEqual('AppProductDev-99.0.9999.99-Dev',
+                         config.packaging_basename)
 
 
 class TestPaths(unittest.TestCase):
diff --git a/chrome/installer/mac/signing/pipeline.py b/chrome/installer/mac/signing/pipeline.py
index 2a05fce..898c0d4 100644
--- a/chrome/installer/mac/signing/pipeline.py
+++ b/chrome/installer/mac/signing/pipeline.py
@@ -69,7 +69,8 @@
         if actual_framework_change_count != signed_framework_change_count:
             raise ValueError(
                 'While customizing and signing {} ({}), actual_framework_change_count {} != signed_framework_change_count {}'
-                .format(dist_config.base_bundle_id, dist_config.dmg_basename,
+                .format(dist_config.base_bundle_id,
+                        dist_config.packaging_basename,
                         actual_framework_change_count,
                         signed_framework_change_count))
 
@@ -135,10 +136,10 @@
     # brand code is in use, use the actual brand code instead of the
     # name fragment, to avoid leaking the association between brand
     # codes and their meanings.
-    dmg_identifier = dist_config.dmg_basename
+    dmg_identifier = dist_config.packaging_basename
     if dist.branding_code:
-        dmg_identifier = dist_config.dmg_basename.replace(
-            dist.dmg_name_fragment, dist.branding_code)
+        dmg_identifier = dist_config.packaging_basename.replace(
+            dist.packaging_name_fragment, dist.branding_code)
 
     product = model.CodeSignedProduct(
         dmg_path, dmg_identifier, sign_with_identifier=True)
@@ -168,7 +169,8 @@
         dsstore_file = 'chrome_dmg_dsstore'
         icon_file = 'chrome_dmg_icon.icns'
 
-    dmg_path = os.path.join(paths.output, '{}.dmg'.format(config.dmg_basename))
+    dmg_path = os.path.join(paths.output,
+                            '{}.dmg'.format(config.packaging_basename))
     app_path = os.path.join(paths.work, config.app_dir)
 
     # A locally-created empty directory is more trustworthy than /var/empty.
@@ -245,7 +247,7 @@
                              cwd=paths.work)
 
 
-def sign_all(orig_paths, config, package_dmg=True, do_notarization=True):
+def sign_all(orig_paths, config, disable_packaging=False, do_notarization=True):
     """For each distribution in |config|, performs customization, signing, and
     DMG packaging and places the resulting signed DMG in |orig_paths.output|.
     The |paths.input| must contain the products to customize and sign.
@@ -270,23 +272,27 @@
         for dist in config.distributions:
             with commands.WorkDirectory(orig_paths) as paths:
                 dist_config = dist.to_config(config)
+                do_packaging = (dist.package_as_dmg or
+                                dist.package_as_pkg) and not disable_packaging
 
-                # If not packaging into a DMG, simply move the signed bundle to
-                # the output directory.
-                if not package_dmg and not do_notarization:
+                # If not packaging and not notarizing, then simply drop the
+                # signed bundle in the output directory when done signing.
+                if not do_packaging and not do_notarization:
                     dest_dir = paths.output
                 else:
                     dest_dir = notary_paths.work
 
-                dest_dir = os.path.join(dest_dir, dist_config.dmg_basename)
+                dest_dir = os.path.join(dest_dir,
+                                        dist_config.packaging_basename)
                 _customize_and_sign_chrome(paths, dist_config, dest_dir,
                                            signed_frameworks)
 
                 # If the build products are to be notarized, ZIP the app bundle
                 # and submit it for notarization.
                 if do_notarization:
-                    zip_file = os.path.join(notary_paths.work,
-                                            dist_config.dmg_basename + '.zip')
+                    zip_file = os.path.join(
+                        notary_paths.work,
+                        dist_config.packaging_basename + '.zip')
                     commands.run_command([
                         'zip', '--recurse-paths', '--symlinks', '--quiet',
                         zip_file, dist_config.app_dir
@@ -301,29 +307,36 @@
                                                     config):
                 dist_config = uuids_to_config[result]
                 dest_dir = os.path.join(notary_paths.work,
-                                        dist_config.dmg_basename)
+                                        dist_config.packaging_basename)
                 _staple_chrome(notary_paths.replace_work(dest_dir), dist_config)
 
-        # After all apps are optionally notarized, package the DMGs.
-        uuids_to_dmg_path = {}
-        if package_dmg:
+        # After all apps are optionally notarized, package as required.
+        if not disable_packaging:
+            uuids_to_package_path = {}
             for dist in config.distributions:
                 dist_config = dist.to_config(config)
-                paths = orig_paths.replace_work(
-                    os.path.join(notary_paths.work, dist_config.dmg_basename))
 
-                dmg_path = _package_and_sign_dmg(paths, dist_config)
+                if dist.package_as_dmg:
+                    paths = orig_paths.replace_work(
+                        os.path.join(notary_paths.work,
+                                     dist_config.packaging_basename))
 
-                if do_notarization:
-                    uuid = notarize.submit(dmg_path, dist_config)
-                    uuids_to_dmg_path[uuid] = dmg_path
+                    dmg_path = _package_and_sign_dmg(paths, dist_config)
 
-            # Wait for DMG notarization results to come back, stapling as they
-            # do.
+                    if do_notarization:
+                        uuid = notarize.submit(dmg_path, dist_config)
+                        uuids_to_package_path[uuid] = dmg_path
+
+                if dist.package_as_pkg:
+                    # TODO(avi): Do packaging as a pkg here.
+                    pass
+
+            # Wait for packaging notarization results to come back, stapling as
+            # they do.
             if do_notarization:
                 for result in notarize.wait_for_results(
-                        uuids_to_dmg_path.keys(), config):
-                    dmg_path = uuids_to_dmg_path[result]
-                    notarize.staple(dmg_path)
+                        uuids_to_package_path.keys(), config):
+                    package_path = uuids_to_package_path[result]
+                    notarize.staple(package_path)
 
     _package_installer_tools(orig_paths, config)
diff --git a/chrome/installer/mac/signing/pipeline_test.py b/chrome/installer/mac/signing/pipeline_test.py
index 34b322d..958b0d14 100644
--- a/chrome/installer/mac/signing/pipeline_test.py
+++ b/chrome/installer/mac/signing/pipeline_test.py
@@ -128,7 +128,7 @@
                                             signed_frameworks)
 
         branded_dist = model.Distribution(
-            branding_code='c0de', dmg_name_fragment='Branded')
+            branding_code='c0de', packaging_name_fragment='Branded')
         branded_dist_config = branded_dist.to_config(config)
         paths = self.paths.replace_work('$W')
 
@@ -279,7 +279,7 @@
 
         config = test_config.TestConfig()
         dist = model.Distribution(
-            branding_code='MOO', dmg_name_fragment='ForCows')
+            branding_code='MOO', packaging_name_fragment='ForCows')
         dist_config = dist.to_config(config)
 
         paths = self.paths.replace_work('$W')
@@ -514,7 +514,7 @@
             mock.call._package_installer_tools(mock.ANY, mock.ANY),
         ])
 
-    def test_sign_no_dmg(self, **kwargs):
+    def test_sign_no_packaging(self, **kwargs):
         manager = mock.Mock()
         for attr in kwargs:
             manager.attach_mock(kwargs[attr], attr)
@@ -524,7 +524,7 @@
         kwargs['wait_for_results'].return_value = iter([app_uuid])
 
         config = test_config.TestConfig()
-        pipeline.sign_all(self.paths, config, package_dmg=False)
+        pipeline.sign_all(self.paths, config, disable_packaging=True)
 
         manager.assert_has_calls([
             # First customize the distribution and sign it.
@@ -578,14 +578,14 @@
             mock.call._package_installer_tools(mock.ANY, mock.ANY),
         ])
 
-    def test_sign_no_dmg_no_notarization(self, **kwargs):
+    def test_sign_no_packaging_no_notarization(self, **kwargs):
         manager = mock.Mock()
         for attr in kwargs:
             manager.attach_mock(kwargs[attr], attr)
 
         config = test_config.TestConfig()
         pipeline.sign_all(
-            self.paths, config, package_dmg=False, do_notarization=False)
+            self.paths, config, disable_packaging=True, do_notarization=False)
 
         manager.assert_has_calls([
             # First customize the distribution and sign it.
@@ -617,7 +617,7 @@
                 return [
                     model.Distribution(),
                     model.Distribution(
-                        branding_code='MOO', dmg_name_fragment='ForCows'),
+                        branding_code='MOO', packaging_name_fragment='ForCows'),
                 ]
 
         config = Config()
diff --git a/chrome/services/cups_proxy/public/mojom/BUILD.gn b/chrome/services/cups_proxy/public/mojom/BUILD.gn
index 87e00bc..5df08c8 100644
--- a/chrome/services/cups_proxy/public/mojom/BUILD.gn
+++ b/chrome/services/cups_proxy/public/mojom/BUILD.gn
@@ -7,7 +7,6 @@
 
 mojom("mojom") {
   sources = [
-    "constants.mojom",
     "proxy.mojom",
   ]
 
diff --git a/chrome/services/cups_proxy/public/mojom/constants.mojom b/chrome/services/cups_proxy/public/mojom/constants.mojom
deleted file mode 100644
index 3833df8..0000000
--- a/chrome/services/cups_proxy/public/mojom/constants.mojom
+++ /dev/null
@@ -1,11 +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.
-
-module chromeos.printing.mojom;
-
-// Service id.
-const string kCupsProxyServiceName = "cups_proxy";
-
-// StartCupsProxyService capability id.
-const string kStartCupsProxyServiceCapability = "start_cups_proxy_service";
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 64bfdca..d02280c 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1060,6 +1060,8 @@
       "../browser/renderer_context_menu/spelling_menu_observer_browsertest.cc",
       "../browser/renderer_host/render_process_host_chrome_browsertest.cc",
       "../browser/repost_form_warning_browsertest.cc",
+      "../browser/resource_coordinator/discard_before_unload_helper.cc",
+      "../browser/resource_coordinator/discard_before_unload_helper.h",
       "../browser/resource_coordinator/discard_before_unload_helper_browsertest.cc",
       "../browser/resource_coordinator/local_site_characteristics_data_store_factory_browsertest.cc",
       "../browser/resource_coordinator/local_site_characteristics_data_unittest_utils.cc",
diff --git a/chrome/test/base/perf/performance_test.cc b/chrome/test/base/perf/performance_test.cc
index ef9d127..fa4635c 100644
--- a/chrome/test/base/perf/performance_test.cc
+++ b/chrome/test/base/perf/performance_test.cc
@@ -129,6 +129,7 @@
         base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(kTraceDir);
     base::FilePath trace_file;
     CHECK(base::CreateTemporaryFileInDir(dir, &trace_file));
+    LOG(INFO) << "Created the trace file: " << trace_file;
 
     auto trace_data_endpoint = content::TracingController::CreateFileEndpoint(
         trace_file, runloop.QuitClosure());
diff --git a/chrome/test/chromedriver/chrome/browser_info.cc b/chrome/test/chromedriver/chrome/browser_info.cc
index d36e690..ff4eed5 100644
--- a/chrome/test/chromedriver/chrome/browser_info.cc
+++ b/chrome/test/chromedriver/chrome/browser_info.cc
@@ -79,9 +79,6 @@
     return Status(kOk);
   }
 
-  const Status error =
-      Status(kUnknownError, "unrecognized Chrome version: " + browser_string);
-
   int build_no = 0;
   if (base::StartsWith(browser_string, kVersionPrefix,
                        base::CompareCase::SENSITIVE) ||
@@ -125,7 +122,8 @@
     return Status(kOk);
   }
 
-  return error;
+  return Status(kUnknownError,
+                "unrecognized Chrome version: " + browser_string);
 }
 
 Status ParseBrowserVersionString(const std::string& browser_version,
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index ceb0191..b36efa6 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -154,10 +154,6 @@
     'HeadlessInvalidCertificateTest.*',
     # Similar issues with HeadlessChromeDriverTest.
     'HeadlessChromeDriverTest.*',
-    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2277
-    # RemoteBrowserTest requires extra setup. TODO(johnchen@chromium.org):
-    # Modify the test so it runs correctly as isolated test.
-    'RemoteBrowserTest.*',
     # Flaky: https://crbug.com/899919
     'SessionHandlingTest.testGetSessions',
     # Flaky due to occasional timeout in starting Chrome
@@ -190,6 +186,7 @@
         'ChromeDriverTest.testWindowMaximize',
         'ChromeDriverTest.testWindowMinimize',
         'ChromeLogPathCapabilityTest.testChromeLogPath',
+        # Connecting to running browser is not supported on Android.
         'RemoteBrowserTest.*',
         # Don't enable perf testing on Android yet.
         'PerfTest.*',
@@ -3847,7 +3844,7 @@
     parser.error('Need path specified when replayable log set to true.')
 
   global _CHROMEDRIVER_BINARY
-  _CHROMEDRIVER_BINARY = options.chromedriver
+  _CHROMEDRIVER_BINARY = util.GetAbsolutePathOfUserPath(options.chromedriver)
 
   if (options.android_package and
       options.android_package not in _ANDROID_NEGATIVE_FILTER):
@@ -3864,7 +3861,28 @@
   if options.chrome:
     _CHROME_BINARY = util.GetAbsolutePathOfUserPath(options.chrome)
   else:
-    _CHROME_BINARY = None
+    # In some test environments (such as commit queue), it's not convenient to
+    # specify Chrome binary location on the command line. Try to use heuristics
+    # to locate the Chrome binary next to the ChromeDriver binary.
+    driver_path = os.path.dirname(_CHROMEDRIVER_BINARY)
+    chrome_path = None
+    platform = util.GetPlatformName()
+    if platform == 'linux':
+      chrome_path = os.path.join(driver_path, 'chrome')
+    elif platform == 'mac':
+      if os.path.exists(os.path.join(driver_path, 'Google Chrome.app')):
+        chrome_path = os.path.join(driver_path, 'Google Chrome.app',
+                                   'Contents', 'MacOS', 'Google Chrome')
+      else:
+        chrome_path = os.path.join(driver_path, 'Chromium.app',
+                                   'Contents', 'MacOS', 'Chromium')
+    elif platform == 'win':
+      chrome_path = os.path.join(driver_path, 'chrome.exe')
+
+    if chrome_path is not None and os.path.exists(chrome_path):
+      _CHROME_BINARY = chrome_path
+    else:
+      _CHROME_BINARY = None
 
   global _ANDROID_PACKAGE_KEY
   _ANDROID_PACKAGE_KEY = options.android_package
diff --git a/chrome/test/data/extensions/api_test/login_screen_apis/extension.crx b/chrome/test/data/extensions/api_test/login_screen_apis/extension.crx
index 221b31ed..0742a2eb 100644
--- a/chrome/test/data/extensions/api_test/login_screen_apis/extension.crx
+++ b/chrome/test/data/extensions/api_test/login_screen_apis/extension.crx
Binary files differ
diff --git a/chrome/test/data/extensions/api_test/login_screen_apis/extension/background.js b/chrome/test/data/extensions/api_test/login_screen_apis/extension/background.js
index cd8f1ff..4d3d48fe 100644
--- a/chrome/test/data/extensions/api_test/login_screen_apis/extension/background.js
+++ b/chrome/test/data/extensions/api_test/login_screen_apis/extension/background.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 const cannotCreateMultipleWindowsErrorMessage =
-    'Can\'t create more than one window per extension.';
+    'Login screen extension UI already in use.';
 const cannotCloseNoWindowErrorMessage = 'No open window to close.';
 const cannotAccessLocalStorageErrorMessage =
     '"local" is not available for login screen extensions';
diff --git a/chrome/test/data/extensions/api_test/login_screen_apis/extension/manifest.json b/chrome/test/data/extensions/api_test/login_screen_apis/extension/manifest.json
index 47ac707..8d0dd00 100644
--- a/chrome/test/data/extensions/api_test/login_screen_apis/extension/manifest.json
+++ b/chrome/test/data/extensions/api_test/login_screen_apis/extension/manifest.json
@@ -1,6 +1,6 @@
 {
   "name": "Login screen APIs test extension",
-  "version": "1.18",
+  "version": "1.19",
   "manifest_version": 2,
   "description": "Login screen APIs test extension",
   "background": {
diff --git a/chrome/test/data/extensions/api_test/login_screen_apis/update_manifest.xml b/chrome/test/data/extensions/api_test/login_screen_apis/update_manifest.xml
index e005911..ce618c6 100644
--- a/chrome/test/data/extensions/api_test/login_screen_apis/update_manifest.xml
+++ b/chrome/test/data/extensions/api_test/login_screen_apis/update_manifest.xml
@@ -7,6 +7,6 @@
   <app appid='oclffehlkdgibkainkilopaalpdobkan'>
     <updatecheck
         codebase='http://mock.http/extensions/api_test/login_screen_apis/extension.crx'
-        version='1.18' />
+        version='1.19' />
    </app>
 </gupdate>
diff --git a/chrome/test/data/pdf/accessibility/readme.md b/chrome/test/data/pdf/accessibility/readme.md
new file mode 100644
index 0000000..307c899
--- /dev/null
+++ b/chrome/test/data/pdf/accessibility/readme.md
@@ -0,0 +1,23 @@
+# PdfExtensionAccessibilityTreeDumpTest
+
+A `PdfExtensionAccessibilityTreeDumpTest` loads a PDF file, waits for it
+to load, then dumps the accessibility tree. More about accessibility tree
+dump tests at `content/test/data/accessibility/readme.md`.
+
+## Filters and directives
+
+Please refer to `content/test/data/accessibility/readme.md` to see
+how to use filters. Supported filters and directives are:
+* [x] `@<platform>-ALLOW:`
+* [ ] `@<platform>-ALLOW-EMPTY:`
+* [ ] `@<platform>-DENY:`
+* [ ] `@WAIT-FOR:`
+
+The PdfExtensionAccessibilityTreeDumpTest implementation of extra
+directives/filters is similar to the DumpAccessibilityTreeTest. If you
+need to support additional directives/filters, see
+/src/content/test/data/accessibility/readme.md to respect the same
+interface as the one used for HTML accessibility tree dump tests.
+
+Implementation details for parsing extra directives in HTML can be
+found in `DumpAccessibilityTestBase::ParseHtmlForExtraDirectives()`.
diff --git a/chrome/test/data/pdf/accessibility/text-direction-expected-blink.txt b/chrome/test/data/pdf/accessibility/text-direction-expected-blink.txt
new file mode 100644
index 0000000..769efb5
--- /dev/null
+++ b/chrome/test/data/pdf/accessibility/text-direction-expected-blink.txt
@@ -0,0 +1,9 @@
+embeddedObject textDirection=ltr
+++document restriction=readOnly
+++++region name='Page 1' restriction=readOnly isPageBreakingObject=true
+++++++paragraph restriction=readOnly
+++++++++staticText name='abcdef<newline>' restriction=readOnly
+++++++++++inlineTextBox name='abcdef<newline>' restriction=readOnly textDirection=ltr
+++++++paragraph restriction=readOnly
+++++++++staticText name='شممشا' restriction=readOnly
+++++++++++inlineTextBox name='شممشا' restriction=readOnly textDirection=rtl
diff --git a/chrome/test/data/pdf/accessibility/text-direction.in b/chrome/test/data/pdf/accessibility/text-direction.in
new file mode 100644
index 0000000..73d80d2
--- /dev/null
+++ b/chrome/test/data/pdf/accessibility/text-direction.in
@@ -0,0 +1,91 @@
+{{header}}
+%@BLINK-ALLOW:textDirection=*
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 8 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type0
+  /BaseFont /Arial
+  /Encoding /Identity-H
+  /DescendantFonts [6 0 R]
+  /ToUnicode 7 0 R
+>>
+endobj
+{{object 5 0}} <<
+  /Type /FontDescriptor
+  /FontName /Arial
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Font
+  /FontDescriptor 5 0 R
+  /BaseFont /Arial
+  /Subtype /CIDFontType2
+  /W [0 [778] 68 [444 500 444 500 444 333] 910 [229] 951 952 531 996 [394]]
+>>
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+1 begincodespacerange
+<0000> <FFFF>
+endcodespacerange
+2 beginbfchar
+<038E> <FE8E>
+<03E4> <FEE4>
+endbfchar
+2 beginbfrange
+<0044> <0049> <0061>
+<03B7> <03B8> <FEB7>
+endbfrange
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+endstream
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+/F1 16 Tf
+30 100 Td
+<004400450046004700480049> Tj
+ET
+BT
+/F1 16 Tf
+30 50 Td
+<038E03B803E403E403B7> Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/chrome/test/data/pdf/accessibility/text-direction.pdf b/chrome/test/data/pdf/accessibility/text-direction.pdf
new file mode 100644
index 0000000..d03f657
--- /dev/null
+++ b/chrome/test/data/pdf/accessibility/text-direction.pdf
@@ -0,0 +1,106 @@
+%PDF-1.7
+% ò¤ô
+%@BLINK-ALLOW:textDirection=*
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 8 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type0
+  /BaseFont /Arial
+  /Encoding /Identity-H
+  /DescendantFonts [6 0 R]
+  /ToUnicode 7 0 R
+>>
+endobj
+5 0 obj <<
+  /Type /FontDescriptor
+  /FontName /Arial
+>>
+endobj
+6 0 obj <<
+  /Type /Font
+  /FontDescriptor 5 0 R
+  /BaseFont /Arial
+  /Subtype /CIDFontType2
+  /W [0 [778] 68 [444 500 444 500 444 333] 910 [229] 951 952 531 996 [394]]
+>>
+endobj
+7 0 obj <<
+  /Length 297
+>>
+stream
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+1 begincodespacerange
+<0000> <FFFF>
+endcodespacerange
+2 beginbfchar
+<038E> <FE8E>
+<03E4> <FEE4>
+endbfchar
+2 beginbfrange
+<0044> <0049> <0061>
+<03B7> <03B8> <FEB7>
+endbfrange
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+endstream
+endobj
+8 0 obj <<
+  /Length 107
+>>
+stream
+BT
+/F1 16 Tf
+30 100 Td
+<004400450046004700480049> Tj
+ET
+BT
+/F1 16 Tf
+30 50 Td
+<038E03B803E403E403B7> Tj
+ET
+endstream
+endobj
+xref
+0 9
+0000000000 65535 f 
+0000000045 00000 n 
+0000000098 00000 n 
+0000000187 00000 n 
+0000000313 00000 n 
+0000000455 00000 n 
+0000000519 00000 n 
+0000000698 00000 n 
+0000001047 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 9
+>>
+startxref
+1206
+%%EOF
\ No newline at end of file
diff --git a/chrome/test/data/webui/cr_components/cr_components_browsertest.js b/chrome/test/data/webui/cr_components/cr_components_browsertest.js
index 5cd0bfa..2ca72b9 100644
--- a/chrome/test/data/webui/cr_components/cr_components_browsertest.js
+++ b/chrome/test/data/webui/cr_components/cr_components_browsertest.js
@@ -49,14 +49,33 @@
   extraLibraries: CrComponentsBrowserTest.prototype.extraLibraries.concat([
     'managed_footnote_test.js',
   ]),
+
+  /** @override */
+  get suiteName() {
+    return managed_footnote_test.suiteName;
+  }
 };
 
-TEST_F('CrComponentsManagedFootnoteTest', 'All', function() {
-  mocha.run();
+TEST_F('CrComponentsManagedFootnoteTest', 'Hidden', function() {
+  runMochaTest(this.suiteName, managed_footnote_test.TestNames.Hidden);
+});
+
+TEST_F('CrComponentsManagedFootnoteTest', 'LoadTimeDataBrowser', function() {
+  runMochaTest(
+      this.suiteName, managed_footnote_test.TestNames.LoadTimeDataBrowser);
+});
+
+TEST_F('CrComponentsManagedFootnoteTest', 'Events', function() {
+  runMochaTest(this.suiteName, managed_footnote_test.TestNames.Events);
 });
 
 GEN('#if defined(OS_CHROMEOS)');
 
+TEST_F('CrComponentsManagedFootnoteTest', 'LoadTimeDataDevice', function() {
+  runMochaTest(
+      this.suiteName, managed_footnote_test.TestNames.LoadTimeDataDevice);
+});
+
 /**
  * @constructor
  * @extends {CrComponentsBrowserTest}
diff --git a/chrome/test/data/webui/cr_components/managed_footnote_test.js b/chrome/test/data/webui/cr_components/managed_footnote_test.js
index 256a6cd6..79f5ff3 100644
--- a/chrome/test/data/webui/cr_components/managed_footnote_test.js
+++ b/chrome/test/data/webui/cr_components/managed_footnote_test.js
@@ -2,52 +2,89 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-suite('managed-footnote', function() {
-  setup(function() {
-    PolymerTest.clearBody();
-  });
+/** @fileoverview Suite of tests for managed-footnote. */
+cr.define('managed_footnote_test', function() {
+  /** @enum {string} */
+  const TestNames = {
+    Hidden: 'Hidden When isManaged Is False',
+    LoadTimeDataBrowser: 'Reads Attributes From loadTimeData browser message',
+    Events: 'Responds to is-managed-changed events',
+    LoadTimeDataDevice: 'Reads Attributes From loadTimeData device message',
+  };
 
-  teardown(function() {
-    PolymerTest.clearBody();
-  });
+  const suiteName = 'ManagedFootnoteTest';
 
-  /**
-   * Resets loadTimeData to the parameters, inserts a <managed-footnote> element
-   * in the DOM, and returns it.
-   * @param {boolean} isManaged Whether the footnote should be visible.
-   * @param {string} message String to display inside the element, before href
-   *     substitution.
-   * @return {HTMLElement}
-   */
-  function setupTestElement(isManaged, message, managementPageUrl) {
-    loadTimeData.overrideValues({
-      chromeManagementUrl: managementPageUrl,
-      isManaged: isManaged,
-      managedByOrg: message,
+  suite(suiteName, function() {
+    setup(function() {
+      PolymerTest.clearBody();
     });
-    const footnote = document.createElement('managed-footnote');
-    document.body.appendChild(footnote);
-    Polymer.dom.flush();
-    return footnote;
-  }
 
-  test('Hidden When isManaged Is False', function() {
-    const footnote = setupTestElement(false, '', '');
-    assertEquals('none', getComputedStyle(footnote).display);
+    teardown(function() {
+      PolymerTest.clearBody();
+    });
+
+    /**
+     * Resets loadTimeData to the parameters, inserts a <managed-footnote>
+     * element in the DOM, and returns it.
+     * @param {boolean} isManaged Whether the footnote should be visible.
+     * @param {string} browserMessage String to display inside the element,
+     *     before href substitution.
+     * @param {string} deviceMessage String to display inside the element,
+     *     before href substitution.
+     * @return {HTMLElement}
+     */
+    function setupTestElement(
+        isManaged, browserMessage, deviceMessage, managementPageUrl) {
+      loadTimeData.overrideValues({
+        chromeManagementUrl: managementPageUrl,
+        isManaged: isManaged,
+        browserManagedByOrg: browserMessage,
+        deviceManagedByOrg: deviceMessage,
+      });
+      const footnote = document.createElement('managed-footnote');
+      document.body.appendChild(footnote);
+      Polymer.dom.flush();
+      return footnote;
+    }
+
+    test('Hidden When isManaged Is False', function() {
+      const footnote = setupTestElement(false, '', '', '');
+      assertEquals('none', getComputedStyle(footnote).display);
+    });
+
+    test('Reads Attributes From loadTimeData browser message', function() {
+      const browserMessage = 'the quick brown fox jumps over the lazy dog';
+      const deviceMessage = 'the lazy dog jumps over the quick brown fox';
+      const footnote = setupTestElement(true, browserMessage, '', '');
+
+      assertNotEquals('none', getComputedStyle(footnote).display);
+      assertTrue(footnote.shadowRoot.textContent.includes(browserMessage));
+    });
+
+    test('Responds to is-managed-changed events', function() {
+      const footnote = setupTestElement(false, '', '', '');
+      assertEquals('none', getComputedStyle(footnote).display);
+
+      cr.webUIListenerCallback('is-managed-changed', [true]);
+      assertNotEquals('none', getComputedStyle(footnote).display);
+    });
+
+    test('Reads Attributes From loadTimeData device message', function() {
+      const browserMessage = 'the quick brown fox jumps over the lazy dog';
+      const deviceMessage = 'the lazy dog jumps over the quick brown fox';
+      const footnote =
+          setupTestElement(true, browserMessage, deviceMessage, '');
+
+      assertNotEquals('none', getComputedStyle(footnote).display);
+      assertTrue(footnote.shadowRoot.textContent.includes(browserMessage));
+
+      footnote.showDeviceInfo = true;
+      assertTrue(footnote.shadowRoot.textContent.includes(deviceMessage));
+    });
   });
 
-  test('Reads Attributes From loadTimeData', function() {
-    const message = 'the quick brown fox jumps over the lazy dog';
-    const footnote = setupTestElement(true, message, '');
-    assertNotEquals('none', getComputedStyle(footnote).display);
-    assertTrue(footnote.shadowRoot.textContent.includes(message));
-  });
-
-  test('Responds to is-managed-changed events', function() {
-    const footnote = setupTestElement(false, '', '');
-    assertEquals('none', getComputedStyle(footnote).display);
-
-    cr.webUIListenerCallback('is-managed-changed', [true]);
-    assertNotEquals('none', getComputedStyle(footnote).display);
-  });
+  return {
+    suiteName: suiteName,
+    TestNames: TestNames,
+  };
 });
diff --git a/chrome/test/data/webui/cr_elements/cr_drawer_tests.js b/chrome/test/data/webui/cr_elements/cr_drawer_tests.js
index cfe628d..3de787b 100644
--- a/chrome/test/data/webui/cr_elements/cr_drawer_tests.js
+++ b/chrome/test/data/webui/cr_elements/cr_drawer_tests.js
@@ -52,6 +52,24 @@
         });
   });
 
+  test('tapping icon closes drawer', async () => {
+    // Create a drawer with an icon and open it.
+    document.body.innerHTML = `
+      <cr-drawer id="drawer" align="ltr" icon-name="menu" icon-title="close">
+      </cr-drawer>
+    `;
+    Polymer.dom.flush();
+    const drawer = document.getElementById('drawer');
+    drawer.openDrawer();
+    await test_util.eventToPromise('cr-drawer-opened', drawer);
+
+    // Tapping the icon closes the drawer.
+    MockInteractions.tap(drawer.$.iconButton);
+    await test_util.eventToPromise('close', drawer);
+    assertFalse(drawer.open);
+    assertTrue(drawer.wasCanceled());
+  });
+
   test('align=ltr', function() {
     const drawer = createDrawer('ltr');
     drawer.openDrawer();
diff --git a/chrome/test/data/webui/downloads/manager_tests.js b/chrome/test/data/webui/downloads/manager_tests.js
index 34cfe45..7dde38b 100644
--- a/chrome/test/data/webui/downloads/manager_tests.js
+++ b/chrome/test/data/webui/downloads/manager_tests.js
@@ -130,10 +130,10 @@
     assertFalse(manager.$.toolbar.hasClearableDownloads);
   });
 
-  test('loadTimeData contains isManaged and managedByOrg', function() {
+  test('loadTimeData contains isManaged and browserManagedByOrg', function() {
     // Check that loadTimeData contains these values.
     loadTimeData.getBoolean('isManaged');
-    loadTimeData.getString('managedByOrg');
+    loadTimeData.getString('browserManagedByOrg');
   });
 
   test('toast is shown when clear-all-command is fired', () => {
diff --git a/chrome/test/data/webui/extensions/item_list_test.js b/chrome/test/data/webui/extensions/item_list_test.js
index e71ec027..dd61f3c 100644
--- a/chrome/test/data/webui/extensions/item_list_test.js
+++ b/chrome/test/data/webui/extensions/item_list_test.js
@@ -106,7 +106,7 @@
     test(assert(TestNames.LoadTimeData), function() {
       // Check that loadTimeData contains these values.
       loadTimeData.getBoolean('isManaged');
-      loadTimeData.getString('managedByOrg');
+      loadTimeData.getString('browserManagedByOrg');
     });
   });
 
diff --git a/chrome/test/media_router/BUILD.gn b/chrome/test/media_router/BUILD.gn
index cddd27a0..7f789e6 100644
--- a/chrome/test/media_router/BUILD.gn
+++ b/chrome/test/media_router/BUILD.gn
@@ -46,9 +46,6 @@
     ":browser_test_resources",
     "//chrome/browser/resources/media_router/extension:media_router",
   ]
-  data = [
-    "$root_gen_dir/chrome/browser/resources/media_router/extension/",
-  ]
 }
 
 copy("browser_test_resources") {
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index 2162aab..2895cbc 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -58,6 +58,7 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/child_process_security_policy.h"
+#include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/system_connector.h"
@@ -506,12 +507,16 @@
 
   url_request_context_factory_->InitializeOnUIThread(nullptr);
 
-  cast_browser_process_->SetConnectivityChecker(ConnectivityChecker::Create(
-      base::CreateSingleThreadTaskRunner({content::BrowserThread::IO}),
-      url_request_context_factory_->GetSystemGetter()));
-
   cast_browser_process_->SetBrowserContext(
       std::make_unique<CastBrowserContext>());
+
+  cast_browser_process_->SetConnectivityChecker(ConnectivityChecker::Create(
+      base::CreateSingleThreadTaskRunner({content::BrowserThread::IO}),
+      content::BrowserContext::GetDefaultStoragePartition(
+          cast_browser_process_->browser_context())
+          ->GetURLLoaderFactoryForBrowserProcessIOThread(),
+      content::GetNetworkConnectionTracker()));
+
   cast_browser_process_->SetMetricsServiceClient(
       std::make_unique<metrics::CastMetricsServiceClient>(
           cast_browser_process_->browser_client(),
diff --git a/chromecast/graphics/gestures/multiple_tap_detector_test.cc b/chromecast/graphics/gestures/multiple_tap_detector_test.cc
index 096f2b1b..f53ffcfb 100644
--- a/chromecast/graphics/gestures/multiple_tap_detector_test.cc
+++ b/chromecast/graphics/gestures/multiple_tap_detector_test.cc
@@ -79,13 +79,13 @@
     ui::TouchEvent press(
         ui::ET_TOUCH_PRESSED, tap_point, simulated_clock_.NowTicks(),
         ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH,
-                           ui::PointerDetails::kUnknownPointerId));
+                           ui::kPointerIdUnknown));
     generator_->Dispatch(&press);
     simulated_clock_.Advance(base::TimeDelta::FromMilliseconds(kTapLengthMs));
     ui::TouchEvent release(
         ui::ET_TOUCH_RELEASED, tap_point, simulated_clock_.NowTicks(),
         ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH,
-                           ui::PointerDetails::kUnknownPointerId));
+                           ui::kPointerIdUnknown));
     generator_->Dispatch(&release);
   }
 
diff --git a/chromecast/graphics/gestures/side_swipe_detector.cc b/chromecast/graphics/gestures/side_swipe_detector.cc
index 7bfde99..2c3877af 100644
--- a/chromecast/graphics/gestures/side_swipe_detector.cc
+++ b/chromecast/graphics/gestures/side_swipe_detector.cc
@@ -58,7 +58,7 @@
       gesture_handler_(gesture_handler),
       root_window_(root_window),
       current_swipe_(CastSideSwipeOrigin::NONE),
-      current_pointer_id_(ui::PointerDetails::kUnknownPointerId) {
+      current_pointer_id_(ui::kPointerIdUnknown) {
   DCHECK(gesture_handler);
   DCHECK(root_window);
   root_window_->GetHost()->GetEventSource()->AddEventRewriter(this);
@@ -180,7 +180,7 @@
     gesture_handler_->HandleSideSwipe(CastSideSwipeEvent::END, current_swipe_,
                                       touch_location);
     current_swipe_ = CastSideSwipeOrigin::NONE;
-    current_pointer_id_ = ui::PointerDetails::kUnknownPointerId;
+    current_pointer_id_ = ui::kPointerIdUnknown;
 
     // If the finger is still inside the touch margin at release, this is not
     // really a side swipe. Stream out events we stashed for later retrieval.
diff --git a/chromecast/net/BUILD.gn b/chromecast/net/BUILD.gn
index c6f2443..156051c 100644
--- a/chromecast/net/BUILD.gn
+++ b/chromecast/net/BUILD.gn
@@ -34,6 +34,8 @@
     "//chromecast/public",
     "//components/proxy_config",
     "//net",
+    "//services/network/public/cpp",
+    "//services/network/public/mojom",
   ]
 
   if (is_fuchsia) {
diff --git a/chromecast/net/DEPS b/chromecast/net/DEPS
index 90e91a5..4ab9563 100644
--- a/chromecast/net/DEPS
+++ b/chromecast/net/DEPS
@@ -1,4 +1,6 @@
 include_rules = [
   "+components/proxy_config",
   "+net",
+  "+services/network/public/cpp",
+  "+services/network/public/mojom",
 ]
diff --git a/chromecast/net/connectivity_checker.cc b/chromecast/net/connectivity_checker.cc
index 7c7a50aa..b82855a 100644
--- a/chromecast/net/connectivity_checker.cc
+++ b/chromecast/net/connectivity_checker.cc
@@ -6,13 +6,16 @@
 
 #include "base/single_thread_task_runner.h"
 #include "chromecast/net/connectivity_checker_impl.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace chromecast {
 
-ConnectivityChecker::ConnectivityChecker()
-    : connectivity_observer_list_(
-          new base::ObserverListThreadSafe<ConnectivityObserver>()) {
-}
+ConnectivityChecker::ConnectivityChecker(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : RefCountedDeleteOnSequence(std::move(task_runner)),
+      connectivity_observer_list_(
+          base::MakeRefCounted<
+              base::ObserverListThreadSafe<ConnectivityObserver>>()) {}
 
 ConnectivityChecker::~ConnectivityChecker() {
 }
@@ -36,9 +39,12 @@
 // static
 scoped_refptr<ConnectivityChecker> ConnectivityChecker::Create(
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
-    net::URLRequestContextGetter* url_request_context_getter) {
+    std::unique_ptr<network::SharedURLLoaderFactoryInfo>
+        url_loader_factory_info,
+    network::NetworkConnectionTracker* network_connection_tracker) {
   return ConnectivityCheckerImpl::Create(task_runner,
-                                         url_request_context_getter);
+                                         std::move(url_loader_factory_info),
+                                         network_connection_tracker);
 }
 
 }  // namespace chromecast
diff --git a/chromecast/net/connectivity_checker.h b/chromecast/net/connectivity_checker.h
index 8c47710..85ad605 100644
--- a/chromecast/net/connectivity_checker.h
+++ b/chromecast/net/connectivity_checker.h
@@ -8,22 +8,24 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_delete_on_sequence.h"
 #include "base/observer_list_threadsafe.h"
+#include "base/sequenced_task_runner_helpers.h"
 
 namespace base {
 class SingleThreadTaskRunner;
 }
 
-namespace net {
-class URLRequestContextGetter;
-}
+namespace network {
+class SharedURLLoaderFactoryInfo;
+class NetworkConnectionTracker;
+}  // namespace network
 
 namespace chromecast {
 
 // Checks if internet connectivity is available.
 class ConnectivityChecker
-    : public base::RefCountedThreadSafe<ConnectivityChecker> {
+    : public base::RefCountedDeleteOnSequence<ConnectivityChecker> {
  public:
   class ConnectivityObserver {
    public:
@@ -40,9 +42,9 @@
 
   static scoped_refptr<ConnectivityChecker> Create(
       const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
-      net::URLRequestContextGetter* url_request_context_getter);
-
-  ConnectivityChecker();
+      std::unique_ptr<network::SharedURLLoaderFactoryInfo>
+          url_loader_factory_info,
+      network::NetworkConnectionTracker* network_connection_tracker);
 
   void AddConnectivityObserver(ConnectivityObserver* observer);
   void RemoveConnectivityObserver(ConnectivityObserver* observer);
@@ -54,13 +56,16 @@
   virtual void Check() = 0;
 
  protected:
+  explicit ConnectivityChecker(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
   virtual ~ConnectivityChecker();
 
   // Notifies observes that connectivity has changed.
   void Notify(bool connected);
 
  private:
-  friend class base::RefCountedThreadSafe<ConnectivityChecker>;
+  friend class base::RefCountedDeleteOnSequence<ConnectivityChecker>;
+  friend class base::DeleteHelper<ConnectivityChecker>;
 
   const scoped_refptr<base::ObserverListThreadSafe<ConnectivityObserver>>
       connectivity_observer_list_;
diff --git a/chromecast/net/connectivity_checker_impl.cc b/chromecast/net/connectivity_checker_impl.cc
index 7869fae..95d2c0af 100644
--- a/chromecast/net/connectivity_checker_impl.cc
+++ b/chromecast/net/connectivity_checker_impl.cc
@@ -22,9 +22,10 @@
 #include "net/http/http_status_code.h"
 #include "net/http/http_transaction_factory.h"
 #include "net/socket/ssl_client_socket.h"
-#include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_context_builder.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/mojom/network_change_manager.mojom.h"
 
 namespace chromecast {
 
@@ -64,50 +65,57 @@
 // static
 scoped_refptr<ConnectivityCheckerImpl> ConnectivityCheckerImpl::Create(
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-    net::URLRequestContextGetter* url_request_context_getter) {
+    std::unique_ptr<network::SharedURLLoaderFactoryInfo>
+        url_loader_factory_info,
+    network::NetworkConnectionTracker* network_connection_tracker) {
   DCHECK(task_runner);
 
-  auto connectivity_checker =
-      base::WrapRefCounted(new ConnectivityCheckerImpl(task_runner));
+  auto connectivity_checker = base::WrapRefCounted(
+      new ConnectivityCheckerImpl(task_runner, network_connection_tracker));
   task_runner->PostTask(
       FROM_HERE,
       base::BindOnce(&ConnectivityCheckerImpl::Initialize, connectivity_checker,
-                     base::RetainedRef(url_request_context_getter)));
+                     std::move(url_loader_factory_info)));
   return connectivity_checker;
 }
 
 ConnectivityCheckerImpl::ConnectivityCheckerImpl(
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : ConnectivityChecker(),
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    network::NetworkConnectionTracker* network_connection_tracker)
+    : ConnectivityChecker(task_runner),
       task_runner_(std::move(task_runner)),
+      network_connection_tracker_(network_connection_tracker),
       connected_(false),
-      connection_type_(net::NetworkChangeNotifier::CONNECTION_NONE),
+      connection_type_(network::mojom::ConnectionType::CONNECTION_NONE),
       check_errors_(0),
       network_changed_pending_(false) {
   DCHECK(task_runner_.get());
+  DCHECK(network_connection_tracker_);
 }
 
 void ConnectivityCheckerImpl::Initialize(
-    net::URLRequestContextGetter* url_request_context_getter) {
-  url_request_context_getter_ = url_request_context_getter;
+    std::unique_ptr<network::SharedURLLoaderFactoryInfo>
+        url_loader_factory_info) {
   DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(url_loader_factory_info);
+  url_loader_factory_ = network::SharedURLLoaderFactory::Create(
+      std::move(url_loader_factory_info));
+
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   base::CommandLine::StringType check_url_str =
       command_line->GetSwitchValueNative(switches::kConnectivityCheckUrl);
   connectivity_check_url_.reset(new GURL(
       check_url_str.empty() ? kDefaultConnectivityCheckUrl : check_url_str));
 
-  url_request_context_ = url_request_context_getter->GetURLRequestContext();
-
-  net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
+  network_connection_tracker_->AddNetworkConnectionObserver(this);
   task_runner_->PostTask(FROM_HERE,
                          base::BindOnce(&ConnectivityCheckerImpl::Check, this));
 }
 
 ConnectivityCheckerImpl::~ConnectivityCheckerImpl() {
   DCHECK(task_runner_.get());
-  net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
-  task_runner_->DeleteSoon(FROM_HERE, url_request_.release());
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  network_connection_tracker_->RemoveNetworkConnectionObserver(this);
 }
 
 bool ConnectivityCheckerImpl::Connected() const {
@@ -117,18 +125,17 @@
 
 void ConnectivityCheckerImpl::SetConnected(bool connected) {
   DCHECK(task_runner_->BelongsToCurrentThread());
-  if (connected_ == connected)
-    return;
   {
     base::AutoLock auto_lock(connected_lock_);
+    if (connected_ == connected)
+      return;
     connected_ = connected;
   }
 
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   base::CommandLine::StringType check_url_str =
       command_line->GetSwitchValueNative(switches::kConnectivityCheckUrl);
-  if (check_url_str.empty())
-  {
+  if (check_url_str.empty()) {
     connectivity_check_url_.reset(new GURL(
       connected ? kHttpConnectivityCheckUrl : kDefaultConnectivityCheckUrl));
     LOG(INFO) << "Change check url=" << *connectivity_check_url_;
@@ -145,24 +152,33 @@
 
 void ConnectivityCheckerImpl::CheckInternal() {
   DCHECK(task_runner_->BelongsToCurrentThread());
-  DCHECK(url_request_context_);
+  DCHECK(url_loader_factory_);
 
   // Don't check connectivity if network is offline, because Internet could be
   // accessible via netifs ignored.
-  if (net::NetworkChangeNotifier::IsOffline())
+  if (network_connection_tracker_->IsOffline())
     return;
 
-  // If url_request_ is non-null, there is already a check going on. Don't
+  // If url_loader_ is non-null, there is already a check going on. Don't
   // start another.
-  if (url_request_.get())
+  if (url_loader_)
     return;
 
-  DVLOG(1) << "Connectivity check: url=" << *connectivity_check_url_;
-  url_request_ = url_request_context_->CreateRequest(
-      *connectivity_check_url_, net::MAXIMUM_PRIORITY, this,
-      MISSING_TRAFFIC_ANNOTATION);
-  url_request_->set_method("HEAD");
-  url_request_->Start();
+  VLOG(1) << "Connectivity check: url=" << *connectivity_check_url_;
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = GURL(*connectivity_check_url_);
+  resource_request->method = "HEAD";
+  resource_request->priority = net::MAXIMUM_PRIORITY;
+  // To enable ssl_info in the response.
+  resource_request->report_raw_headers = true;
+
+  url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
+                                                 MISSING_TRAFFIC_ANNOTATION);
+  network::SimpleURLLoader::HeadersOnlyCallback callback =
+      base::BindOnce(&ConnectivityCheckerImpl::OnConnectivityCheckComplete,
+                     base::Unretained(this));
+  url_loader_->DownloadHeadersOnly(url_loader_factory_.get(),
+                                   std::move(callback));
 
   timeout_.Reset(base::Bind(&ConnectivityCheckerImpl::OnUrlRequestTimeout,
                             this));
@@ -173,8 +189,8 @@
       FROM_HERE, timeout_.callback(), base::TimeDelta::FromSeconds(timeout));
 }
 
-void ConnectivityCheckerImpl::OnNetworkChanged(
-    net::NetworkChangeNotifier::ConnectionType type) {
+void ConnectivityCheckerImpl::OnConnectionChanged(
+    network::mojom::ConnectionType type) {
   DVLOG(2) << "OnNetworkChanged " << type;
   connection_type_ = type;
 
@@ -183,15 +199,16 @@
   network_changed_pending_ = true;
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
-      base::BindOnce(&ConnectivityCheckerImpl::OnNetworkChangedInternal, this),
+      base::BindOnce(&ConnectivityCheckerImpl::OnConnectionChangedInternal,
+                     this),
       base::TimeDelta::FromSeconds(kNetworkChangedDelayInSeconds));
 }
 
-void ConnectivityCheckerImpl::OnNetworkChangedInternal() {
+void ConnectivityCheckerImpl::OnConnectionChangedInternal() {
   network_changed_pending_ = false;
   Cancel();
 
-  if (connection_type_ == net::NetworkChangeNotifier::CONNECTION_NONE) {
+  if (connection_type_ == network::mojom::ConnectionType::CONNECTION_NONE) {
     SetConnected(false);
     return;
   }
@@ -199,18 +216,25 @@
   Check();
 }
 
-void ConnectivityCheckerImpl::OnResponseStarted(net::URLRequest* request,
-                                                int net_error) {
+void ConnectivityCheckerImpl::OnConnectivityCheckComplete(
+    scoped_refptr<net::HttpResponseHeaders> headers) {
   DCHECK(task_runner_->BelongsToCurrentThread());
-  int http_response_code =
-      (net_error == net::OK &&
-       request->response_info().headers.get() != nullptr)
-          ? request->response_info().headers->response_code()
-          : net::HTTP_BAD_REQUEST;
+  DCHECK(url_loader_);
+  timeout_.Cancel();
+  int error = url_loader_->NetError();
+  if (error == net::ERR_INSECURE_RESPONSE && url_loader_->ResponseInfo() &&
+      url_loader_->ResponseInfo()->ssl_info) {
+    LOG(ERROR) << "OnSSLCertificateError: cert_status="
+               << url_loader_->ResponseInfo()->ssl_info->cert_status;
+    OnUrlRequestError(ErrorType::SSL_CERTIFICATE_ERROR);
+    return;
+  }
+  int http_response_code = (error == net::OK && headers)
+                               ? headers->response_code()
+                               : net::HTTP_BAD_REQUEST;
 
   // Clears resources.
-  // URLRequest::Cancel() is called in destructor.
-  url_request_.reset(nullptr);
+  url_loader_.reset(nullptr);
 
   if (http_response_code < 400) {
     DVLOG(1) << "Connectivity check succeeded";
@@ -222,31 +246,10 @@
         FROM_HERE,
         base::BindOnce(&ConnectivityCheckerImpl::CheckInternal, this),
         base::TimeDelta::FromSeconds(kConnectivitySuccessPeriodSeconds));
-    timeout_.Cancel();
     return;
   }
   LOG(ERROR) << "Connectivity check failed: " << http_response_code;
   OnUrlRequestError(ErrorType::BAD_HTTP_STATUS);
-  timeout_.Cancel();
-}
-
-void ConnectivityCheckerImpl::OnReadCompleted(net::URLRequest* request,
-                                              int bytes_read) {
-  NOTREACHED();
-}
-
-void ConnectivityCheckerImpl::OnSSLCertificateError(
-    net::URLRequest* request,
-    int net_error,
-    const net::SSLInfo& ssl_info,
-    bool fatal) {
-  DCHECK(task_runner_->BelongsToCurrentThread());
-  LOG(ERROR) << "OnSSLCertificateError: cert_status=" << ssl_info.cert_status;
-  url_request_context_->http_transaction_factory()
-      ->GetSession()
-      ->ClearSSLSessionCache();
-  OnUrlRequestError(ErrorType::SSL_CERTIFICATE_ERROR);
-  timeout_.Cancel();
 }
 
 void ConnectivityCheckerImpl::OnUrlRequestError(ErrorType type) {
@@ -262,7 +265,7 @@
     check_errors_ = kNumErrorsToNotifyOffline;
     SetConnected(false);
   }
-  url_request_.reset(nullptr);
+  url_loader_.reset(nullptr);
   // Check again.
   task_runner_->PostDelayedTask(
       FROM_HERE, base::BindOnce(&ConnectivityCheckerImpl::Check, this),
@@ -277,10 +280,10 @@
 
 void ConnectivityCheckerImpl::Cancel() {
   DCHECK(task_runner_->BelongsToCurrentThread());
-  if (!url_request_.get())
+  if (!url_loader_)
     return;
-  DVLOG(2) << "Cancel connectivity check in progress";
-  url_request_.reset(nullptr);  // URLRequest::Cancel() is called in destructor.
+  VLOG(2) << "Cancel connectivity check in progress";
+  url_loader_.reset(nullptr);
   timeout_.Cancel();
 }
 
diff --git a/chromecast/net/connectivity_checker_impl.h b/chromecast/net/connectivity_checker_impl.h
index 8d7185e..2b0cbbbf 100644
--- a/chromecast/net/connectivity_checker_impl.h
+++ b/chromecast/net/connectivity_checker_impl.h
@@ -5,24 +5,28 @@
 #ifndef CHROMECAST_NET_CONNECTIVITY_CHECKER_IMPL_H_
 #define CHROMECAST_NET_CONNECTIVITY_CHECKER_IMPL_H_
 
+#include <memory>
+
 #include "base/cancelable_callback.h"
 #include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
 #include "chromecast/net/connectivity_checker.h"
-#include "net/base/network_change_notifier.h"
-#include "net/url_request/url_request.h"
+#include "services/network/public/cpp/network_connection_tracker.h"
 
 class GURL;
 
 namespace base {
 class SingleThreadTaskRunner;
-}
+}  // namespace base
 
 namespace net {
-class SSLInfo;
-class URLRequest;
-class URLRequestContext;
-class URLRequestContextGetter;
-}
+class HttpResponseHeaders;
+}  // namespace net
+
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+}  // namespace network
 
 namespace chromecast {
 
@@ -30,13 +34,14 @@
 // to given url.
 class ConnectivityCheckerImpl
     : public ConnectivityChecker,
-      public net::URLRequest::Delegate,
-      public net::NetworkChangeNotifier::NetworkChangeObserver {
+      public network::NetworkConnectionTracker::NetworkConnectionObserver {
  public:
   // Connectivity checking and initialization will run on task_runner.
   static scoped_refptr<ConnectivityCheckerImpl> Create(
       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-      net::URLRequestContextGetter* url_request_context_getter);
+      std::unique_ptr<network::SharedURLLoaderFactoryInfo>
+          url_loader_factory_info,
+      network::NetworkConnectionTracker* network_connection_tracker);
 
   // ConnectivityChecker implementation:
   bool Connected() const override;
@@ -44,26 +49,23 @@
 
  protected:
   explicit ConnectivityCheckerImpl(
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      network::NetworkConnectionTracker* network_connection_tracker);
   ~ConnectivityCheckerImpl() override;
 
  private:
-  // UrlRequest::Delegate implementation:
-  void OnResponseStarted(net::URLRequest* request, int net_error) override;
-  void OnReadCompleted(net::URLRequest* request, int bytes_read) override;
-  void OnSSLCertificateError(net::URLRequest* request,
-                             int net_error,
-                             const net::SSLInfo& ssl_info,
-                             bool fatal) override;
-
   // Initializes ConnectivityChecker
-  void Initialize(net::URLRequestContextGetter* url_request_context_getter);
+  void Initialize(std::unique_ptr<network::SharedURLLoaderFactoryInfo>
+                      url_loader_factory_info);
 
-  // net::NetworkChangeNotifier::NetworkChangeObserver implementation:
-  void OnNetworkChanged(
-      net::NetworkChangeNotifier::ConnectionType type) override;
+  // network::NetworkConnectionTracker::NetworkConnectionObserver
+  // implementation:
+  void OnConnectionChanged(network::mojom::ConnectionType type) override;
 
-  void OnNetworkChangedInternal();
+  void OnConnectionChangedInternal();
+
+  void OnConnectivityCheckComplete(
+      scoped_refptr<net::HttpResponseHeaders> headers);
 
   // Cancels current connectivity checking in progress.
   void Cancel();
@@ -86,17 +88,17 @@
   void CheckInternal();
 
   std::unique_ptr<GURL> connectivity_check_url_;
-  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
-  net::URLRequestContext* url_request_context_;
-  std::unique_ptr<net::URLRequest> url_request_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  std::unique_ptr<network::SimpleURLLoader> url_loader_;
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  network::NetworkConnectionTracker* const network_connection_tracker_;
 
   // connected_lock_ protects access to connected_ which is shared across
   // threads.
   mutable base::Lock connected_lock_;
   bool connected_;
 
-  net::NetworkChangeNotifier::ConnectionType connection_type_;
+  network::mojom::ConnectionType connection_type_;
   // Number of connectivity check errors.
   unsigned int check_errors_;
   bool network_changed_pending_;
diff --git a/chromecast/net/fake_connectivity_checker.cc b/chromecast/net/fake_connectivity_checker.cc
index fc3caed..cbe0514 100644
--- a/chromecast/net/fake_connectivity_checker.cc
+++ b/chromecast/net/fake_connectivity_checker.cc
@@ -3,13 +3,13 @@
 // found in the LICENSE file.
 
 #include "chromecast/net/fake_connectivity_checker.h"
+#include "base/threading/thread_task_runner_handle.h"
 
 namespace chromecast {
 
 FakeConnectivityChecker::FakeConnectivityChecker()
-    : ConnectivityChecker(),
-      connected_(true) {
-}
+    : ConnectivityChecker(base::ThreadTaskRunnerHandle::Get()),
+      connected_(true) {}
 
 FakeConnectivityChecker::~FakeConnectivityChecker() {}
 
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc
index 2d18917..c4c49d1 100644
--- a/chromeos/constants/chromeos_switches.cc
+++ b/chromeos/constants/chromeos_switches.cc
@@ -426,8 +426,13 @@
 // Interval at which we check for total time on OOBE.
 const char kOobeTimerInterval[] = "oobe-timer-interval";
 
-// Url addrress of SAML provider for a SAML public session.
-// TODO: Remove when https://crbug.com/984021 is fixed.
+// SAML assertion consumer URL, used to detect when Gaia-less SAML flows end
+// (e.g. for SAML managed guest sessions)
+// TODO(984021): Remove when URL is sent by DMServer.
+const char kPublicAccountsSamlAclUrl[] = "public-accounts-saml-acl-url";
+
+// Url address of SAML provider for a SAML public session.
+// TODO(984021): Remove when URL is sent by DMServer.
 const char kPublicAccountsSamlUrl[] = "public-accounts-saml-url";
 
 // If set to "true", the profile requires policy during restart (policy load
@@ -440,8 +445,8 @@
 // The rlz ping delay (in seconds) that overwrites the default value.
 const char kRlzPingDelay[] = "rlz-ping-delay";
 
-// Password change url for SAML users. Remove when https://crbug.com/941489 is
-// fixed.
+// Password change url for SAML users.
+// TODO(941489): Remove when the bug is fixed.
 const char kSamlPasswordChangeUrl[] = "saml-password-change-url";
 
 // Smaller, denser shelf in clamshell mode.
@@ -489,6 +494,9 @@
 const char kTetherHostScansIgnoreWiredConnections[] =
     "tether-host-scans-ignore-wired-connections";
 
+// Shows all Bluetooth devices in UI (System Tray/Settings Page.)
+const char kUnfilteredBluetoothDevices[] = "unfiltered-bluetooth-devices";
+
 // Used to tell the policy infrastructure to not let profile initialization
 // complete until policy is manually set by a test. This is used to provide
 // backward compatibility with a few tests that incorrectly use the
@@ -577,5 +585,10 @@
       kDisableArcCpuRestriction);
 }
 
+bool IsUnfilteredBluetoothDevicesEnabled() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      kUnfilteredBluetoothDevices);
+}
+
 }  // namespace switches
 }  // namespace chromeos
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h
index 3edbf1b..e843cb9 100644
--- a/chromeos/constants/chromeos_switches.h
+++ b/chromeos/constants/chromeos_switches.h
@@ -170,6 +170,8 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kOobeSkipPostLogin[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kOobeSkipToLogin[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kOobeTimerInterval[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kPublicAccountsSamlAclUrl[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kPublicAccountsSamlUrl[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kDisableArcCpuRestriction[];
@@ -195,6 +197,8 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kWaitForInitialPolicyFetchForTest[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kWakeOnWifiPacket[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kUnfilteredBluetoothDevices[];
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -248,6 +252,9 @@
 // Returns true if |kDisableArcCpuRestriction| is true.
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsArcCpuRestrictionDisabled();
 
+// Returns true if all Bluetooth devices in UI (System Tray/Settings Page.)
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsUnfilteredBluetoothDevicesEnabled();
+
 }  // namespace switches
 }  // namespace chromeos
 
diff --git a/chromeos/services/device_sync/cryptauth_scheduler_impl.cc b/chromeos/services/device_sync/cryptauth_scheduler_impl.cc
index bdfae71..3ec6d19 100644
--- a/chromeos/services/device_sync/cryptauth_scheduler_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_scheduler_impl.cc
@@ -428,7 +428,7 @@
 
   // Recover from failure using immediate retry.
   DCHECK(retry_count > 0);
-  if (retry_count <= client_directive_.retry_attempts()) {
+  if (retry_count < client_directive_.retry_attempts()) {
     return std::max(kZeroTimeDelta,
                     kImmediateRetryDelay - time_since_last_attempt);
   }
diff --git a/chromeos/services/device_sync/cryptauth_scheduler_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_scheduler_impl_unittest.cc
index e3979123..16dbb1bd 100644
--- a/chromeos/services/device_sync/cryptauth_scheduler_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_scheduler_impl_unittest.cc
@@ -502,9 +502,12 @@
                                   false /* device_registry_changed */,
                                   base::nullopt /* client_directive */));
 
+    // Verify the next scheduled Enrollment/DeviceSync. At this point, note that
+    // the number of failed attempts == |attempt| == retry count of the next
+    // request.
     expected_request.set_retry_count(attempt);
     base::TimeDelta expected_delay =
-        attempt <= cryptauthv2::GetClientDirectiveForTest().retry_attempts()
+        attempt < cryptauthv2::GetClientDirectiveForTest().retry_attempts()
             ? kImmediateRetryDelay
             : base::TimeDelta::FromMilliseconds(
                   cryptauthv2::GetClientDirectiveForTest()
diff --git a/chromeos/services/network_config/cros_network_config.cc b/chromeos/services/network_config/cros_network_config.cc
index 6e5f24e..48d230f 100644
--- a/chromeos/services/network_config/cros_network_config.cc
+++ b/chromeos/services/network_config/cros_network_config.cc
@@ -1182,9 +1182,17 @@
       result->ethernet = std::move(ethernet);
       break;
     }
-    case mojom::NetworkType::kTether:
-      // No Tether specific managed properties.
+    case mojom::NetworkType::kTether: {
+      // Tether has no managed properties, provide the state properties.
+      auto tether = mojom::TetherStateProperties::New();
+      tether->battery_percentage = network_state->battery_percentage();
+      tether->carrier = network_state->tether_carrier();
+      tether->has_connected_to_host =
+          network_state->tether_has_connected_to_host();
+      tether->signal_strength = network_state->signal_strength();
+      result->tether = std::move(tether);
       break;
+    }
     case mojom::NetworkType::kVPN: {
       auto vpn = mojom::ManagedVPNProperties::New();
       const base::Value* vpn_dict =
diff --git a/chromeos/services/network_config/public/mojom/cros_network_config.mojom b/chromeos/services/network_config/public/mojom/cros_network_config.mojom
index 14e4bc2..ab29e5b2 100644
--- a/chromeos/services/network_config/public/mojom/cros_network_config.mojom
+++ b/chromeos/services/network_config/public/mojom/cros_network_config.mojom
@@ -581,6 +581,9 @@
   bool restricted_connectivity = false;
   ManagedIPConfigProperties? static_ip_config;
   IPConfigProperties? saved_ip_config;
+  // Tether has no managed properties. Provide TetherStateProperties for display
+  // purposes.
+  TetherStateProperties? tether;
   ManagedVPNProperties? vpn;
   ManagedWiFiProperties? wifi;
 };
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index e2341f8d..abd4cf3d 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -151,6 +151,10 @@
     "viewport_mode.h",
     "web_controller.cc",
     "web_controller.h",
+    "website_login_fetcher.cc",
+    "website_login_fetcher.h",
+    "website_login_fetcher_impl.cc",
+    "website_login_fetcher_impl.h",
   ]
 
   deps = [
@@ -160,6 +164,7 @@
     "//components/autofill/core/browser",
     "//components/autofill/core/common",
     "//components/autofill_assistant/browser/devtools",
+    "//components/password_manager/core/browser:browser",
     "//components/signin/public/identity_manager",
     "//components/strings:components_strings_grit",
     "//components/version_info",
@@ -180,6 +185,7 @@
     "actions/mock_action_delegate.h",
     "actions/popup_message_action_unittest.cc",
     "actions/prompt_action_unittest.cc",
+    "actions/set_form_field_value_action_unittest.cc",
     "actions/wait_for_document_action_unittest.cc",
     "actions/wait_for_dom_action_unittest.cc",
     "batch_element_checker_unittest.cc",
@@ -196,6 +202,8 @@
     "mock_service.h",
     "mock_web_controller.cc",
     "mock_web_controller.h",
+    "mock_website_login_fetcher.cc",
+    "mock_website_login_fetcher.h",
     "protocol_utils_unittest.cc",
     "retry_timer_unittest.cc",
     "script_executor_unittest.cc",
diff --git a/components/autofill_assistant/browser/DEPS b/components/autofill_assistant/browser/DEPS
index 514714e..bc88f17 100644
--- a/components/autofill_assistant/browser/DEPS
+++ b/components/autofill_assistant/browser/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+chrome/android/features/autofill_assistant/test_support_jni_headers",
   "+components/autofill",
+  "+components/password_manager/core/browser",
   "+components/version_info/version_info.h",
   "+content/public/browser",
   "+content/public/test",
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index e532ac11..3193730 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -36,8 +36,10 @@
 class ClientMemory;
 class ClientStatus;
 struct ClientSettings;
+struct PaymentInformation;
 struct PaymentRequestOptions;
 class UserAction;
+class WebsiteLoginFetcher;
 
 // Action delegate called when processing actions.
 class ActionDelegate {
@@ -109,7 +111,8 @@
   // Asks the user to provide the data used by UseAddressAction and
   // UseCreditCardAction.
   virtual void GetPaymentInformation(
-      std::unique_ptr<PaymentRequestOptions> options) = 0;
+      std::unique_ptr<PaymentRequestOptions> options,
+      std::unique_ptr<PaymentInformation> information) = 0;
 
   using GetFullCardCallback =
       base::OnceCallback<void(std::unique_ptr<autofill::CreditCard> card,
@@ -254,6 +257,9 @@
   // Get current personal data manager.
   virtual autofill::PersonalDataManager* GetPersonalDataManager() = 0;
 
+  // Get current login fetcher.
+  virtual WebsiteLoginFetcher* GetWebsiteLoginFetcher() = 0;
+
   // Get associated web contents.
   virtual content::WebContents* GetWebContents() = 0;
 
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 1d176b6..ae3eab0 100644
--- a/components/autofill_assistant/browser/actions/get_payment_information_action.cc
+++ b/components/autofill_assistant/browser/actions/get_payment_information_action.cc
@@ -10,6 +10,7 @@
 #include "base/android/locale_utils.h"
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/browser/autofill_data_util.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
@@ -19,7 +20,9 @@
 #include "components/autofill_assistant/browser/client_memory.h"
 #include "components/autofill_assistant/browser/metrics.h"
 #include "components/autofill_assistant/browser/service.pb.h"
+#include "components/autofill_assistant/browser/website_login_fetcher_impl.h"
 #include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_contents.h"
 #include "third_party/blink/public/mojom/payments/payment_request.mojom.h"
 #include "third_party/libaddressinput/chromium/addressinput_util.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
@@ -27,6 +30,21 @@
 
 namespace autofill_assistant {
 
+GetPaymentInformationAction::LoginDetails::LoginDetails(
+    bool _hide_if_no_other_choice,
+    const std::string& _payload,
+    const WebsiteLoginFetcher::Login& _login)
+    : hide_if_no_other_choice(_hide_if_no_other_choice),
+      payload(_payload),
+      login(_login) {}
+
+GetPaymentInformationAction::LoginDetails::LoginDetails(
+    bool _hide_if_no_other_choice,
+    const std::string& _payload)
+    : hide_if_no_other_choice(_hide_if_no_other_choice), payload(_payload) {}
+
+GetPaymentInformationAction::LoginDetails::~LoginDetails() = default;
+
 GetPaymentInformationAction::GetPaymentInformationAction(
     ActionDelegate* delegate,
     const ActionProto& proto)
@@ -38,7 +56,7 @@
   delegate_->GetPersonalDataManager()->RemoveObserver(this);
 
   // Report UMA histograms.
-  if (presented_to_user_) {
+  if (shown_to_user_) {
     Metrics::RecordPaymentRequestPrefilledSuccess(initially_prefilled,
                                                   action_successful_);
     Metrics::RecordPaymentRequestAutofillChanged(personal_data_changed_,
@@ -56,10 +74,21 @@
     std::move(callback).Run(std::move(processed_action_proto_));
     return;
   }
+
+  // If Chrome password manager logins are requested, we need to asynchronously
+  // obtain them before showing the payment request.
   auto get_payment_information = proto_.get_payment_information();
-  if (get_payment_information.has_prompt()) {
-    delegate_->SetStatusMessage(get_payment_information.prompt());
-  }
+  auto password_manager_option = std::find_if(
+      get_payment_information.login_details().login_options().begin(),
+      get_payment_information.login_details().login_options().end(),
+      [&](const LoginDetailsProto::LoginOptionProto& option) {
+        return option.type_case() ==
+               LoginDetailsProto::LoginOptionProto::kPasswordManager;
+      });
+  bool requests_pwm_logins =
+      password_manager_option !=
+      get_payment_information.login_details().login_options().end();
+
   payment_options->confirm_callback = base::BindOnce(
       &GetPaymentInformationAction::OnGetPaymentInformation,
       weak_ptr_factory_.GetWeakPtr(), std::move(get_payment_information));
@@ -69,16 +98,93 @@
   payment_options->terms_link_callback = base::BindOnce(
       &GetPaymentInformationAction::OnTermsAndConditionsLinkClicked,
       weak_ptr_factory_.GetWeakPtr());
+  if (requests_pwm_logins) {
+    delegate_->GetWebsiteLoginFetcher()->GetLoginsForUrl(
+        delegate_->GetWebContents()->GetLastCommittedURL(),
+        base::BindOnce(&GetPaymentInformationAction::OnGetLogins,
+                       weak_ptr_factory_.GetWeakPtr(), *password_manager_option,
+                       std::move(payment_options)));
+  } else {
+    ShowToUser(std::move(payment_options));
+  }
+}
+
+void GetPaymentInformationAction::OnGetLogins(
+    const LoginDetailsProto::LoginOptionProto& login_option,
+    std::unique_ptr<PaymentRequestOptions> payment_options,
+    std::vector<WebsiteLoginFetcher::Login> logins) {
+  for (const auto& login : logins) {
+    LoginChoice choice = {
+        base::NumberToString(payment_options->login_choices.size()),
+        login.username, login_option.preselection_priority()};
+    payment_options->login_choices.emplace_back(std::move(choice));
+    login_details_map_.emplace(
+        choice.identifier,
+        std::make_unique<LoginDetails>(login_option.hide_if_no_other_options(),
+                                       login_option.payload(), login));
+  }
+  ShowToUser(std::move(payment_options));
+}
+
+void GetPaymentInformationAction::ShowToUser(
+    std::unique_ptr<PaymentRequestOptions> payment_options) {
+  // Create and set initial state.
+  auto payment_information = std::make_unique<PaymentInformation>();
+  auto get_payment_information = proto_.get_payment_information();
+  switch (get_payment_information.terms_and_conditions_state()) {
+    case GetPaymentInformationProto::NOT_SELECTED:
+      payment_information->terms_and_conditions = NOT_SELECTED;
+      break;
+    case GetPaymentInformationProto::ACCEPTED:
+      payment_information->terms_and_conditions = ACCEPTED;
+      break;
+    case GetPaymentInformationProto::REVIEW_REQUIRED:
+      payment_information->terms_and_conditions = REQUIRES_REVIEW;
+      break;
+  }
+
+  // Special case: if the only available login option has
+  // |hide_if_no_other_options=true|, the section will not be shown.
+  bool only_login_requested =
+      payment_options->request_login_choice &&
+      !payment_options->request_payer_name &&
+      !payment_options->request_payer_email &&
+      !payment_options->request_payer_phone &&
+      !payment_options->request_shipping &&
+      !payment_options->request_payment_method &&
+      !get_payment_information.request_terms_and_conditions();
+
+  if (payment_options->login_choices.size() == 1 &&
+      login_details_map_.at(payment_options->login_choices.at(0).identifier)
+          ->hide_if_no_other_choice) {
+    payment_options->request_login_choice = false;
+    payment_information->login_choice_identifier.assign(
+        payment_options->login_choices[0].identifier);
+
+    // If only the login section is requested and the choice has already been
+    // made implicitly, the entire UI will not be shown and the action will
+    // complete immediately.
+    if (only_login_requested) {
+      payment_information->succeed = true;
+      std::move(payment_options->confirm_callback)
+          .Run(std::move(payment_information));
+      return;
+    }
+  }
 
   // Gather info for UMA histograms.
-  if (!presented_to_user_) {
-    presented_to_user_ = true;
+  if (!shown_to_user_) {
+    shown_to_user_ = true;
     initially_prefilled = IsInitialAutofillDataComplete(
         delegate_->GetPersonalDataManager(), *payment_options);
     delegate_->GetPersonalDataManager()->AddObserver(this);
   }
 
-  delegate_->GetPaymentInformation(std::move(payment_options));
+  if (get_payment_information.has_prompt()) {
+    delegate_->SetStatusMessage(get_payment_information.prompt());
+  }
+  delegate_->GetPaymentInformation(std::move(payment_options),
+                                   std::move(payment_information));
 }
 
 void GetPaymentInformationAction::OnGetPaymentInformation(
@@ -141,6 +247,20 @@
             std::make_unique<autofill::AutofillProfile>(contact_profile));
       }
     }
+
+    if (get_payment_information.has_login_details()) {
+      auto login_details =
+          login_details_map_.find(payment_information->login_choice_identifier);
+      DCHECK(login_details != login_details_map_.end());
+      if (login_details->second->login.has_value()) {
+        delegate_->GetClientMemory()->set_selected_login(
+            *login_details->second->login);
+      }
+
+      processed_action_proto_->mutable_payment_details()->set_login_payload(
+          login_details->second->payload);
+    }
+
     processed_action_proto_->mutable_payment_details()
         ->set_is_terms_and_conditions_accepted(
             payment_information->terms_and_conditions ==
@@ -176,7 +296,7 @@
 }
 
 std::unique_ptr<PaymentRequestOptions>
-GetPaymentInformationAction::CreateOptionsFromProto() const {
+GetPaymentInformationAction::CreateOptionsFromProto() {
   auto payment_options = std::make_unique<PaymentRequestOptions>();
   auto get_payment_information = proto_.get_payment_information();
 
@@ -205,6 +325,39 @@
       payment_options->billing_postal_code_missing_text.empty()) {
     return nullptr;
   }
+  payment_options->request_login_choice =
+      get_payment_information.has_login_details();
+  payment_options->login_section_title.assign(
+      get_payment_information.login_details().section_title());
+
+  // Transform login options to concrete login choices.
+  for (const auto& login_option :
+       get_payment_information.login_details().login_options()) {
+    switch (login_option.type_case()) {
+      case LoginDetailsProto::LoginOptionProto::kCustom: {
+        LoginChoice choice = {
+            base::NumberToString(payment_options->login_choices.size()),
+            login_option.custom().label(),
+            login_option.has_preselection_priority()
+                ? login_option.preselection_priority()
+                : -1};
+        payment_options->login_choices.emplace_back(std::move(choice));
+        login_details_map_.emplace(choice.identifier,
+                                   std::make_unique<LoginDetails>(
+                                       login_option.hide_if_no_other_options(),
+                                       login_option.payload()));
+        break;
+      }
+      case LoginDetailsProto::LoginOptionProto::kPasswordManager: {
+        // Will be retrieved later.
+        break;
+      }
+      case LoginDetailsProto::LoginOptionProto::TYPE_NOT_SET: {
+        // Login option specified, but type not set (should never happen).
+        return nullptr;
+      }
+    }
+  }
 
   // TODO(crbug.com/806868): Maybe we could refactor this to make the confirm
   // chip and direct_action part of the additional_actions.
@@ -222,18 +375,6 @@
     payment_options->additional_actions.push_back(action);
   }
 
-  switch (get_payment_information.terms_and_conditions_state()) {
-    case GetPaymentInformationProto::NOT_SELECTED:
-      payment_options->initial_terms_and_conditions = NOT_SELECTED;
-      break;
-    case GetPaymentInformationProto::ACCEPTED:
-      payment_options->initial_terms_and_conditions = ACCEPTED;
-      break;
-    case GetPaymentInformationProto::REVIEW_REQUIRED:
-      payment_options->initial_terms_and_conditions = REQUIRES_REVIEW;
-      break;
-  }
-
   if (get_payment_information.request_terms_and_conditions()) {
     payment_options->show_terms_as_checkbox =
         get_payment_information.show_terms_as_checkbox();
diff --git a/components/autofill_assistant/browser/actions/get_payment_information_action.h b/components/autofill_assistant/browser/actions/get_payment_information_action.h
index a78c231f..cc7a2a0 100644
--- a/components/autofill_assistant/browser/actions/get_payment_information_action.h
+++ b/components/autofill_assistant/browser/actions/get_payment_information_action.h
@@ -11,10 +11,13 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/personal_data_manager_observer.h"
+#include "components/autofill/core/common/password_form.h"
 #include "components/autofill_assistant/browser/actions/action.h"
 #include "components/autofill_assistant/browser/payment_request.h"
+#include "components/autofill_assistant/browser/website_login_fetcher.h"
 
 namespace autofill_assistant {
 
@@ -31,6 +34,18 @@
   void OnPersonalDataChanged() override;
 
  private:
+  struct LoginDetails {
+    LoginDetails(bool hide_if_no_other_choice, const std::string& payload);
+    LoginDetails(bool hide_if_no_other_choice,
+                 const std::string& payload,
+                 const WebsiteLoginFetcher::Login& login);
+    ~LoginDetails();
+    bool hide_if_no_other_choice;
+    std::string payload;
+    // Only for Chrome PWM login details.
+    base::Optional<WebsiteLoginFetcher::Login> login;
+  };
+
   void InternalProcessAction(ProcessActionCallback callback) override;
 
   void OnGetPaymentInformation(
@@ -39,8 +54,13 @@
   void OnAdditionalActionTriggered(int index);
   void OnTermsAndConditionsLinkClicked(int link);
 
+  void OnGetLogins(const LoginDetailsProto::LoginOptionProto& login_option,
+                   std::unique_ptr<PaymentRequestOptions> payment_options,
+                   std::vector<WebsiteLoginFetcher::Login> logins);
+  void ShowToUser(std::unique_ptr<PaymentRequestOptions> payment_options);
+
   // Creates a new instance of |PaymentRequestOptions| from |proto_|.
-  std::unique_ptr<PaymentRequestOptions> CreateOptionsFromProto() const;
+  std::unique_ptr<PaymentRequestOptions> CreateOptionsFromProto();
 
   bool IsInitialAutofillDataComplete(
       autofill::PersonalDataManager* personal_data_manager,
@@ -53,11 +73,15 @@
       const autofill::CreditCard& credit_card,
       const PaymentRequestOptions& payment_options);
 
-  bool presented_to_user_ = false;
+  bool shown_to_user_ = false;
   bool initially_prefilled = false;
   bool personal_data_changed_ = false;
   bool action_successful_ = false;
   ProcessActionCallback callback_;
+
+  // Maps login choice identifiers to the corresponding login details.
+  std::map<std::string, std::unique_ptr<LoginDetails>> login_details_map_;
+
   base::WeakPtrFactory<GetPaymentInformationAction> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(GetPaymentInformationAction);
diff --git a/components/autofill_assistant/browser/actions/get_payment_information_action_unittest.cc b/components/autofill_assistant/browser/actions/get_payment_information_action_unittest.cc
index 596f6fcc..18cd5c2 100644
--- a/components/autofill_assistant/browser/actions/get_payment_information_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/get_payment_information_action_unittest.cc
@@ -22,12 +22,11 @@
   void SetUp() override {
     ON_CALL(mock_action_delegate_, GetPersonalDataManager)
         .WillByDefault(Return(&mock_personal_data_manager_));
-    ON_CALL(mock_action_delegate_, GetPaymentInformation(_))
+    ON_CALL(mock_action_delegate_, GetPaymentInformation(_, _))
         .WillByDefault(
-            Invoke([](std::unique_ptr<PaymentRequestOptions> options) {
-              auto payment_information = std::make_unique<PaymentInformation>();
-              std::move(options->confirm_callback)
-                  .Run(std::move(payment_information));
+            Invoke([](std::unique_ptr<PaymentRequestOptions> options,
+                      std::unique_ptr<PaymentInformation> information) {
+              std::move(options->confirm_callback).Run(std::move(information));
             }));
   }
 
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.h b/components/autofill_assistant/browser/actions/mock_action_delegate.h
index 713c1a8c..e67faec 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -108,8 +108,9 @@
                void(const Selector& selector,
                     base::OnceCallback<void(const ClientStatus&)> callback));
 
-  MOCK_METHOD1(GetPaymentInformation,
-               void(std::unique_ptr<PaymentRequestOptions> options));
+  MOCK_METHOD2(GetPaymentInformation,
+               void(std::unique_ptr<PaymentRequestOptions> options,
+                    std::unique_ptr<PaymentInformation> information));
 
   MOCK_METHOD1(OnGetFullCard,
                void(base::OnceCallback<void(const autofill::CreditCard& card,
@@ -146,7 +147,7 @@
                      int delay_in_millisecond,
                      base::OnceCallback<void(const ClientStatus&)> callback) {
     OnSetFieldValue(selector, value, callback);
-    OnSetFieldValue(selector, value, delay_in_millisecond, delay_in_millisecond,
+    OnSetFieldValue(selector, value, simulate_key_presses, delay_in_millisecond,
                     callback);
   }
 
@@ -168,11 +169,20 @@
                     const std::string& value,
                     base::OnceCallback<void(const ClientStatus&)> callback));
 
-  MOCK_METHOD4(SendKeyboardInput,
+  void SendKeyboardInput(
+      const Selector& selector,
+      const std::vector<UChar32>& codepoints,
+      int delay_in_millisecond,
+      base::OnceCallback<void(const ClientStatus&)> callback) {
+    OnSendKeyboardInput(selector, codepoints, delay_in_millisecond, callback);
+  }
+
+  MOCK_METHOD4(OnSendKeyboardInput,
                void(const Selector& selector,
                     const std::vector<UChar32>& codepoints,
                     int delay_in_millisecond,
-                    base::OnceCallback<void(const ClientStatus&)> callback));
+                    base::OnceCallback<void(const ClientStatus&)>& callback));
+
   MOCK_METHOD2(GetOuterHtml,
                void(const Selector& selector,
                     base::OnceCallback<void(const ClientStatus&,
@@ -187,6 +197,7 @@
   MOCK_METHOD0(Restart, void());
   MOCK_METHOD0(GetClientMemory, ClientMemory*());
   MOCK_METHOD0(GetPersonalDataManager, autofill::PersonalDataManager*());
+  MOCK_METHOD0(GetWebsiteLoginFetcher, WebsiteLoginFetcher*());
   MOCK_METHOD0(GetWebContents, content::WebContents*());
   MOCK_METHOD1(SetDetails, void(std::unique_ptr<Details> details));
   MOCK_METHOD1(SetInfoBox, void(const InfoBox& info_box));
diff --git a/components/autofill_assistant/browser/actions/set_form_field_value_action.cc b/components/autofill_assistant/browser/actions/set_form_field_value_action.cc
index 0abe0c6..80552b7 100644
--- a/components/autofill_assistant/browser/actions/set_form_field_value_action.cc
+++ b/components/autofill_assistant/browser/actions/set_form_field_value_action.cc
@@ -10,157 +10,188 @@
 #include "base/callback.h"
 #include "components/autofill_assistant/browser/actions/action_delegate.h"
 #include "components/autofill_assistant/browser/client_status.h"
-#include "components/autofill_assistant/browser/string_conversions_util.h"
 
 namespace autofill_assistant {
 
+SetFormFieldValueAction::FieldInput::FieldInput(
+    std::unique_ptr<std::vector<UChar32>> _keyboard_input)
+    : keyboard_input(std::move(_keyboard_input)) {}
+
+SetFormFieldValueAction::FieldInput::FieldInput(std::string _value)
+    : value(_value) {}
+
+SetFormFieldValueAction::FieldInput::FieldInput(bool _use_password)
+    : use_password(_use_password) {}
+
+SetFormFieldValueAction::FieldInput::FieldInput(FieldInput&& other) = default;
+
+SetFormFieldValueAction::FieldInput::~FieldInput() {}
+
 SetFormFieldValueAction::SetFormFieldValueAction(ActionDelegate* delegate,
                                                  const ActionProto& proto)
     : Action(delegate, proto) {
   DCHECK(proto_.has_set_form_value());
   DCHECK_GT(proto_.set_form_value().element().selectors_size(), 0);
   DCHECK_GT(proto_.set_form_value().value_size(), 0);
-  DCHECK(proto_.set_form_value().value(0).has_text());
 }
 
 SetFormFieldValueAction::~SetFormFieldValueAction() {}
 
 void SetFormFieldValueAction::InternalProcessAction(
     ProcessActionCallback callback) {
-  Selector selector =
-      Selector(proto_.set_form_value().element()).MustBeVisible();
-  if (selector.empty()) {
+  process_action_callback_ = std::move(callback);
+  selector_ = Selector(proto_.set_form_value().element()).MustBeVisible();
+  if (selector_.empty()) {
     DVLOG(1) << __func__ << ": empty selector";
-    UpdateProcessedAction(INVALID_SELECTOR);
-    std::move(callback).Run(std::move(processed_action_proto_));
+    EndAction(ClientStatus(INVALID_SELECTOR));
     return;
   }
+
+  // Check proto fields.
+  for (const auto& keypress : proto_.set_form_value().value()) {
+    switch (keypress.keypress_case()) {
+      case SetFormFieldValueProto_KeyPress::kKeycode:
+        // DEPRECATED: the field `keycode' used to contain a single character to
+        // input as text. Since there is no easy way to convert keycodes to
+        // text, this field is now deprecated and only works for US-ASCII
+        // characters. You should use the `keyboard_input' field instead.
+        if (keypress.keycode() >= 128) {
+          DVLOG(1) << "SetFormFieldValueAction: field `keycode' is deprecated "
+                   << "and only supports US-ASCII values (encountered "
+                   << keypress.keycode() << "). Use field `key' instead.";
+          EndAction(ClientStatus(INVALID_ACTION));
+          return;
+        }
+        field_inputs_.emplace_back(
+            /* keyboard_input = */ std::make_unique<std::vector<UChar32>>(
+                1, keypress.keycode()));
+        break;
+      case SetFormFieldValueProto_KeyPress::kKeyboardInput:
+        if (keypress.keyboard_input().empty()) {
+          DVLOG(1) << "SetFormFieldValueAction: field 'keyboard_input' must be "
+                      "non-empty if set.";
+          EndAction(ClientStatus(INVALID_ACTION));
+          return;
+        }
+        field_inputs_.emplace_back(
+            /* keyboard_input = */ std::make_unique<std::vector<UChar32>>(
+                UTF8ToUnicode(keypress.keyboard_input())));
+        break;
+      case SetFormFieldValueProto_KeyPress::kUseUsername:
+        FALLTHROUGH;
+      case SetFormFieldValueProto_KeyPress::kUsePassword:
+        // Login information must have been stored by a previous action.
+        if (!delegate_->GetClientMemory()->has_selected_login()) {
+          DVLOG(1) << "SetFormFieldValueAction: requested login details not "
+                      "available in client memory.";
+          EndAction(ClientStatus(PRECONDITION_FAILED));
+          return;
+        }
+        if (keypress.keypress_case() ==
+            SetFormFieldValueProto_KeyPress::kUseUsername) {
+          field_inputs_.emplace_back(/* value = */ delegate_->GetClientMemory()
+                                         ->selected_login()
+                                         ->username);
+        } else {
+          field_inputs_.emplace_back(/* use_password = */ true);
+        }
+        break;
+      case SetFormFieldValueProto_KeyPress::kText:
+        // Currently no check required.
+        field_inputs_.emplace_back(/* value = */ keypress.text());
+        break;
+      default:
+        DVLOG(1) << "Unrecognized field for SetFormFieldValueProto_KeyPress";
+        EndAction(ClientStatus(INVALID_ACTION));
+        return;
+    }
+  }
+
   delegate_->ShortWaitForElement(
-      selector, base::BindOnce(&SetFormFieldValueAction::OnWaitForElement,
-                               weak_ptr_factory_.GetWeakPtr(),
-                               std::move(callback), selector));
+      selector_, base::BindOnce(&SetFormFieldValueAction::OnWaitForElement,
+                                weak_ptr_factory_.GetWeakPtr()));
 }
 
-void SetFormFieldValueAction::OnWaitForElement(ProcessActionCallback callback,
-                                               const Selector& selector,
-                                               bool element_found) {
+void SetFormFieldValueAction::OnWaitForElement(bool element_found) {
   if (!element_found) {
-    UpdateProcessedAction(ELEMENT_RESOLUTION_FAILED);
-    std::move(callback).Run(std::move(processed_action_proto_));
+    EndAction(ClientStatus(ELEMENT_RESOLUTION_FAILED));
     return;
   }
   // Start with first value, then call OnSetFieldValue() recursively until done.
-  OnSetFieldValue(std::move(callback), selector, /* next = */ 0,
-                  OkClientStatus());
+  OnSetFieldValue(/* next = */ 0, OkClientStatus());
 }
 
-void SetFormFieldValueAction::OnSetFieldValue(ProcessActionCallback callback,
-                                              const Selector& selector,
-                                              int next,
+void SetFormFieldValueAction::OnSetFieldValue(int next,
                                               const ClientStatus& status) {
   // If something went wrong or we are out of values: finish
   if (!status.ok() || next >= proto_.set_form_value().value_size()) {
-    UpdateProcessedAction(status);
-    std::move(callback).Run(std::move(processed_action_proto_));
+    EndAction(status);
     return;
   }
 
-  const auto& key_field = proto_.set_form_value().value(next);
   bool simulate_key_presses = proto_.set_form_value().simulate_key_presses();
   int delay_in_millisecond = proto_.set_form_value().delay_in_millisecond();
-  switch (key_field.keypress_case()) {
-    case SetFormFieldValueProto_KeyPress::kText:
-      if (simulate_key_presses || key_field.text().empty()) {
-        // If we are already using keyboard simulation or we are trying to set
-        // an empty value, no need to trigger keyboard fallback. Simply move on
-        // to next value after |SetFieldValue| is done.
-        delegate_->SetFieldValue(
-            selector, key_field.text(), simulate_key_presses,
-            delay_in_millisecond,
-            base::BindOnce(&SetFormFieldValueAction::OnSetFieldValue,
-                           weak_ptr_factory_.GetWeakPtr(), std::move(callback),
-                           selector,
-                           /* next = */ next + 1));
+  auto next_field_callback = base::BindOnce(
+      &SetFormFieldValueAction::OnSetFieldValue, weak_ptr_factory_.GetWeakPtr(),
+      /* next = */ next + 1);
+  for (const auto& field_input : field_inputs_) {
+    if (field_input.keyboard_input) {
+      delegate_->SendKeyboardInput(selector_, *field_input.keyboard_input,
+                                   delay_in_millisecond,
+                                   std::move(next_field_callback));
+    } else if (field_input.use_password) {
+      delegate_->GetWebsiteLoginFetcher()->GetPasswordForLogin(
+          *delegate_->GetClientMemory()->selected_login(),
+          base::BindOnce(&SetFormFieldValueAction::OnGetPassword,
+                         weak_ptr_factory_.GetWeakPtr(),
+                         /* field_index = */ next));
+    } else {
+      if (simulate_key_presses || field_input.value.empty()) {
+        delegate_->SetFieldValue(selector_, field_input.value,
+                                 simulate_key_presses, delay_in_millisecond,
+                                 std::move(next_field_callback));
       } else {
-        // Trigger a check for keyboard fallback when |SetFieldValue| is done.
         delegate_->SetFieldValue(
-            selector, key_field.text(), simulate_key_presses,
+            selector_, field_input.value, simulate_key_presses,
             delay_in_millisecond,
             base::BindOnce(
                 &SetFormFieldValueAction::OnSetFieldValueAndCheckFallback,
-                weak_ptr_factory_.GetWeakPtr(), std::move(callback), selector,
-                /* next = */ next));
+                weak_ptr_factory_.GetWeakPtr(),
+                /* field_index = */ next,
+                /* requested_value = */ field_input.value));
       }
-      break;
-    case SetFormFieldValueProto_KeyPress::kKeycode:
-      // DEPRECATED: the field `keycode' used to contain a single character to
-      // input as text. Since there is no easy way to convert keycodes to text,
-      // this field is now deprecated and only works for US-ASCII characters.
-      // You should use the `keyboard_input' field instead.
-      if (key_field.keycode() < 128) {  // US-ASCII
-        delegate_->SendKeyboardInput(
-            selector, {key_field.keycode()}, delay_in_millisecond,
-            base::BindOnce(&SetFormFieldValueAction::OnSetFieldValue,
-                           weak_ptr_factory_.GetWeakPtr(), std::move(callback),
-                           selector,
-                           /* next = */ next + 1));
-      } else {
-        DVLOG(3)
-            << "SetFormFieldValueProto_KeyPress: field `keycode' is deprecated "
-            << "and only supports US-ASCII values (encountered "
-            << key_field.keycode() << "). Use field `key' instead.";
-        OnSetFieldValue(std::move(callback), selector, next,
-                        ClientStatus(INVALID_ACTION));
-      }
-      break;
-    case SetFormFieldValueProto_KeyPress::kKeyboardInput:
-      delegate_->SendKeyboardInput(
-          selector, UTF8ToUnicode(key_field.keyboard_input()),
-          delay_in_millisecond,
-          base::BindOnce(&SetFormFieldValueAction::OnSetFieldValue,
-                         weak_ptr_factory_.GetWeakPtr(), std::move(callback),
-                         selector,
-                         /* next = */ next + 1));
-      break;
-    default:
-      DVLOG(1) << "Unrecognized field for SetFormFieldValueProto_KeyPress";
-      OnSetFieldValue(std::move(callback), selector, next,
-                      ClientStatus(INVALID_ACTION));
-      break;
+    }
   }
 }
 
 void SetFormFieldValueAction::OnSetFieldValueAndCheckFallback(
-    ProcessActionCallback callback,
-    const Selector& selector,
-    int next,
+    int field_index,
+    const std::string& requested_value,
     const ClientStatus& status) {
   if (!status.ok()) {
-    OnSetFieldValue(std::move(callback), selector, next + 1, status);
+    EndAction(status);
     return;
   }
   delegate_->GetFieldValue(
-      selector, base::BindOnce(&SetFormFieldValueAction::OnGetFieldValue,
-                               weak_ptr_factory_.GetWeakPtr(),
-                               std::move(callback), selector, next));
+      selector_, base::BindOnce(&SetFormFieldValueAction::OnGetFieldValue,
+                                weak_ptr_factory_.GetWeakPtr(), field_index,
+                                requested_value));
 }
 
-void SetFormFieldValueAction::OnGetFieldValue(ProcessActionCallback callback,
-                                              const Selector& selector,
-                                              int next,
-                                              bool get_value_status,
-                                              const std::string& value) {
-  const auto& key_field = proto_.set_form_value().value(next);
-
+void SetFormFieldValueAction::OnGetFieldValue(
+    int field_index,
+    const std::string& requested_value,
+    bool get_value_status,
+    const std::string& actual_value) {
   // Move to next value if |GetFieldValue| failed.
   if (!get_value_status) {
-    OnSetFieldValue(std::move(callback), selector, next + 1, OkClientStatus());
+    OnSetFieldValue(field_index + 1, OkClientStatus());
     return;
   }
 
   // If value is still empty while it is not supposed to be, trigger keyboard
   // simulation fallback.
-  if (key_field.text().size() > 0 && value.empty()) {
+  if (!requested_value.empty() && actual_value.empty()) {
     // Report a key press simulation fallback has happened.
     auto result = SetFormFieldValueProto::Result();
     result.set_fallback_to_simulate_key_presses(true);
@@ -169,17 +200,46 @@
     // Run |SetFieldValue| with keyboard simulation on and move on to next value
     // afterwards.
     delegate_->SetFieldValue(
-        selector, key_field.text(), /*simulate_key_presses = */ true,
+        selector_, requested_value, /*simulate_key_presses = */ true,
         proto_.set_form_value().delay_in_millisecond(),
         base::BindOnce(&SetFormFieldValueAction::OnSetFieldValue,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback),
-                       selector,
-                       /* next = */ next + 1));
+                       weak_ptr_factory_.GetWeakPtr(),
+                       /* next = */ field_index + 1));
     return;
   }
 
   // Move to next value in all other cases.
-  OnSetFieldValue(std::move(callback), selector, next + 1, OkClientStatus());
+  OnSetFieldValue(field_index + 1, OkClientStatus());
+}
+
+void SetFormFieldValueAction::OnGetPassword(int field_index,
+                                            bool success,
+                                            std::string password) {
+  if (!success) {
+    EndAction(ClientStatus(AUTOFILL_INFO_NOT_AVAILABLE));
+    return;
+  }
+  bool simulate_key_presses = proto_.set_form_value().simulate_key_presses();
+  int delay_in_millisecond = proto_.set_form_value().delay_in_millisecond();
+  if (simulate_key_presses) {
+    delegate_->SetFieldValue(
+        selector_, password, simulate_key_presses, delay_in_millisecond,
+        base::BindOnce(&SetFormFieldValueAction::OnSetFieldValue,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       /* next = */ field_index + 1));
+  } else {
+    delegate_->SetFieldValue(
+        selector_, password, simulate_key_presses, delay_in_millisecond,
+        base::BindOnce(
+            &SetFormFieldValueAction::OnSetFieldValueAndCheckFallback,
+            weak_ptr_factory_.GetWeakPtr(),
+            /* next = */ field_index, /* requested_value = */ password));
+  }
+}
+
+void SetFormFieldValueAction::EndAction(const ClientStatus& status) {
+  UpdateProcessedAction(status);
+  std::move(process_action_callback_).Run(std::move(processed_action_proto_));
 }
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/set_form_field_value_action.h b/components/autofill_assistant/browser/actions/set_form_field_value_action.h
index 8890982c..b00c045 100644
--- a/components/autofill_assistant/browser/actions/set_form_field_value_action.h
+++ b/components/autofill_assistant/browser/actions/set_form_field_value_action.h
@@ -8,9 +8,11 @@
 #include <string>
 #include <vector>
 
+#include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/autofill_assistant/browser/actions/action.h"
+#include "components/autofill_assistant/browser/string_conversions_util.h"
 
 namespace autofill_assistant {
 
@@ -22,29 +24,48 @@
   ~SetFormFieldValueAction() override;
 
  private:
+  // A field input as extracted from the proto, but already checked for
+  // validity.
+  struct FieldInput {
+    explicit FieldInput(std::unique_ptr<std::vector<UChar32>> keyboard_input);
+    explicit FieldInput(std::string value);
+    explicit FieldInput(bool use_password);
+    FieldInput(FieldInput&& other);
+    ~FieldInput();
+
+    // The keys to press if either |keycode| or |keyboard_input| is set, else
+    // nullptr.
+    std::unique_ptr<std::vector<UChar32>> keyboard_input = nullptr;
+    // True if the value should be retrieved from the login details in client
+    // memory.
+    bool use_password = false;
+    // The string to input (for all other cases).
+    std::string value;
+  };
+
   // Overrides Action:
   void InternalProcessAction(ProcessActionCallback callback) override;
 
-  void OnWaitForElement(ProcessActionCallback callback,
-                        const Selector& selector,
-                        bool element_found);
+  void OnWaitForElement(bool element_found);
 
-  void OnGetFieldValue(ProcessActionCallback callback,
-                       const Selector& selector,
-                       int next,
+  void OnGetFieldValue(int field_index,
+                       const std::string& requested_value,
                        bool status,
-                       const std::string& value);
+                       const std::string& actual_value);
 
-  void OnSetFieldValue(ProcessActionCallback callback,
-                       const Selector& selector,
-                       int next,
-                       const ClientStatus& status);
+  void OnSetFieldValue(int next, const ClientStatus& status);
 
-  void OnSetFieldValueAndCheckFallback(ProcessActionCallback callback,
-                                       const Selector& selector,
-                                       int next,
+  void OnSetFieldValueAndCheckFallback(int field_index,
+                                       const std::string& requested_value,
                                        const ClientStatus& status);
 
+  void OnGetPassword(int field_index, bool success, std::string password);
+
+  void EndAction(const ClientStatus& status);
+
+  Selector selector_;
+  std::vector<FieldInput> field_inputs_;
+  ProcessActionCallback process_action_callback_;
   base::WeakPtrFactory<SetFormFieldValueAction> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(SetFormFieldValueAction);
diff --git a/components/autofill_assistant/browser/actions/set_form_field_value_action_unittest.cc b/components/autofill_assistant/browser/actions/set_form_field_value_action_unittest.cc
new file mode 100644
index 0000000..4ca8944
--- /dev/null
+++ b/components/autofill_assistant/browser/actions/set_form_field_value_action_unittest.cc
@@ -0,0 +1,226 @@
+// 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/autofill_assistant/browser/actions/set_form_field_value_action.h"
+
+#include <string>
+#include <utility>
+
+#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
+#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
+#include "components/autofill_assistant/browser/client_memory.h"
+#include "components/autofill_assistant/browser/client_status.h"
+#include "components/autofill_assistant/browser/mock_website_login_fetcher.h"
+#include "components/autofill_assistant/browser/string_conversions_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace {
+const char kFakeUrl[] = "https://www.example.com";
+const char kFakeSelector[] = "#some_selector";
+const char kFakeUsername[] = "user@example.com";
+const char kFakePassword[] = "example_password";
+}  // namespace
+
+namespace autofill_assistant {
+namespace {
+
+using ::base::test::RunOnceCallback;
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Pointee;
+using ::testing::Property;
+using ::testing::Return;
+
+class SetFormFieldValueActionTest : public testing::Test {
+ public:
+  void SetUp() override {
+    set_form_field_proto_ = proto_.mutable_set_form_value();
+    set_form_field_proto_->mutable_element()->add_selectors(kFakeSelector);
+    set_form_field_proto_->mutable_element()->set_visibility_requirement(
+        MUST_BE_VISIBLE);
+    ON_CALL(mock_action_delegate_, GetClientMemory)
+        .WillByDefault(Return(&client_memory_));
+    ON_CALL(mock_action_delegate_, GetWebsiteLoginFetcher)
+        .WillByDefault(Return(&mock_website_login_fetcher_));
+    ON_CALL(mock_action_delegate_, OnShortWaitForElement(_, _))
+        .WillByDefault(RunOnceCallback<1>(true));
+    ON_CALL(mock_action_delegate_, OnSetFieldValue(_, _, _, _, _))
+        .WillByDefault(RunOnceCallback<4>(OkClientStatus()));
+
+    ON_CALL(mock_website_login_fetcher_, OnGetLoginsForUrl(_, _))
+        .WillByDefault(
+            RunOnceCallback<1>(std::vector<WebsiteLoginFetcher::Login>{
+                WebsiteLoginFetcher::Login(GURL(kFakeUrl), kFakeUsername)}));
+    ON_CALL(mock_website_login_fetcher_, OnGetPasswordForLogin(_, _))
+        .WillByDefault(RunOnceCallback<1>(true, kFakePassword));
+    client_memory_.set_selected_login({GURL(kFakeUrl), kFakeUsername});
+    fake_selector_ = Selector({kFakeSelector}).MustBeVisible();
+  }
+
+ protected:
+  Selector fake_selector_;
+  MockActionDelegate mock_action_delegate_;
+  MockWebsiteLoginFetcher mock_website_login_fetcher_;
+  base::MockCallback<Action::ProcessActionCallback> callback_;
+  ActionProto proto_;
+  SetFormFieldValueProto* set_form_field_proto_;
+  ClientMemory client_memory_;
+};
+
+TEST_F(SetFormFieldValueActionTest, RequestedUsernameButNoLoginInClientMemory) {
+  ClientMemory empty_client_memory;
+  ON_CALL(mock_action_delegate_, GetClientMemory)
+      .WillByDefault(Return(&empty_client_memory));
+  auto* value = set_form_field_proto_->add_value();
+  value->set_use_username(true);
+  SetFormFieldValueAction action(&mock_action_delegate_, proto_);
+  EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
+                                              PRECONDITION_FAILED))));
+  action.ProcessAction(callback_.Get());
+}
+
+TEST_F(SetFormFieldValueActionTest, RequestedPasswordButNoLoginInClientMemory) {
+  ClientMemory empty_client_memory;
+  ON_CALL(mock_action_delegate_, GetClientMemory)
+      .WillByDefault(Return(&empty_client_memory));
+  auto* value = set_form_field_proto_->add_value();
+  value->set_use_password(true);
+  SetFormFieldValueAction action(&mock_action_delegate_, proto_);
+  EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
+                                              PRECONDITION_FAILED))));
+  action.ProcessAction(callback_.Get());
+}
+
+TEST_F(SetFormFieldValueActionTest, RequestedPasswordButPasswordNotAvailable) {
+  ON_CALL(mock_website_login_fetcher_, OnGetPasswordForLogin(_, _))
+      .WillByDefault(RunOnceCallback<1>(false, std::string()));
+  auto* value = set_form_field_proto_->add_value();
+  value->set_use_password(true);
+  SetFormFieldValueAction action(&mock_action_delegate_, proto_);
+  EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
+                                              AUTOFILL_INFO_NOT_AVAILABLE))));
+  action.ProcessAction(callback_.Get());
+}
+
+TEST_F(SetFormFieldValueActionTest, NonAsciiKeycode) {
+  auto* value = set_form_field_proto_->add_value();
+  value->set_keycode(UTF8ToUnicode("𠜎")[0]);
+  SetFormFieldValueAction action(&mock_action_delegate_, proto_);
+  EXPECT_CALL(
+      callback_,
+      Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
+  action.ProcessAction(callback_.Get());
+}
+
+TEST_F(SetFormFieldValueActionTest, Username) {
+  auto* value = set_form_field_proto_->add_value();
+  value->set_use_username(true);
+  SetFormFieldValueAction action(&mock_action_delegate_, proto_);
+  ON_CALL(mock_action_delegate_, OnGetFieldValue(_, _))
+      .WillByDefault(RunOnceCallback<1>(true, kFakeUsername));
+  EXPECT_CALL(mock_action_delegate_,
+              OnSetFieldValue(fake_selector_, kFakeUsername, _, _, _))
+      .WillOnce(RunOnceCallback<4>(OkClientStatus()));
+
+  EXPECT_CALL(
+      callback_,
+      Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+  action.ProcessAction(callback_.Get());
+}
+
+TEST_F(SetFormFieldValueActionTest, Password) {
+  auto* value = set_form_field_proto_->add_value();
+  value->set_use_password(true);
+  SetFormFieldValueAction action(&mock_action_delegate_, proto_);
+  ON_CALL(mock_action_delegate_, OnGetFieldValue(_, _))
+      .WillByDefault(RunOnceCallback<1>(true, kFakePassword));
+  EXPECT_CALL(mock_action_delegate_,
+              OnSetFieldValue(fake_selector_, kFakePassword, _, _, _))
+      .WillOnce(RunOnceCallback<4>(OkClientStatus()));
+
+  EXPECT_CALL(
+      callback_,
+      Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+  action.ProcessAction(callback_.Get());
+}
+
+TEST_F(SetFormFieldValueActionTest, Keycode) {
+  auto* value = set_form_field_proto_->add_value();
+  value->set_keycode(13);  // carriage return
+  SetFormFieldValueAction action(&mock_action_delegate_, proto_);
+  EXPECT_CALL(mock_action_delegate_,
+              OnSendKeyboardInput(fake_selector_, std::vector<int>{13}, _, _))
+      .WillOnce(RunOnceCallback<3>(OkClientStatus()));
+
+  EXPECT_CALL(
+      callback_,
+      Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+  action.ProcessAction(callback_.Get());
+}
+
+TEST_F(SetFormFieldValueActionTest, KeyboardInput) {
+  auto* value = set_form_field_proto_->add_value();
+  std::string keyboard_input = "SomeQuery𠜎\r";
+  value->set_keyboard_input(keyboard_input);
+  SetFormFieldValueAction action(&mock_action_delegate_, proto_);
+  EXPECT_CALL(
+      mock_action_delegate_,
+      OnSendKeyboardInput(fake_selector_, UTF8ToUnicode(keyboard_input), _, _))
+      .WillOnce(RunOnceCallback<3>(OkClientStatus()));
+
+  EXPECT_CALL(
+      callback_,
+      Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+  action.ProcessAction(callback_.Get());
+}
+
+TEST_F(SetFormFieldValueActionTest, Text) {
+  auto* value = set_form_field_proto_->add_value();
+  value->set_text("SomeText𠜎");
+  SetFormFieldValueAction action(&mock_action_delegate_, proto_);
+  ON_CALL(mock_action_delegate_, OnGetFieldValue(_, _))
+      .WillByDefault(RunOnceCallback<1>(true, "SomeText𠜎"));
+  EXPECT_CALL(mock_action_delegate_,
+              OnSetFieldValue(fake_selector_, "SomeText𠜎", _, _, _))
+      .WillOnce(RunOnceCallback<4>(OkClientStatus()));
+
+  EXPECT_CALL(
+      callback_,
+      Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+  action.ProcessAction(callback_.Get());
+}
+
+// Test that automatic fallback to simulate keystrokes works.
+TEST_F(SetFormFieldValueActionTest, Fallback) {
+  auto* value = set_form_field_proto_->add_value();
+  value->set_text("123");
+  SetFormFieldValueAction action(&mock_action_delegate_, proto_);
+
+  ON_CALL(mock_action_delegate_, OnGetFieldValue(_, _))
+      .WillByDefault(RunOnceCallback<1>(true, ""));
+
+  {
+    InSequence seq;
+    EXPECT_CALL(mock_action_delegate_,
+                OnSetFieldValue(fake_selector_, "123",
+                                /* simulate_key_presses = */ false, _, _));
+    EXPECT_CALL(mock_action_delegate_,
+                OnSetFieldValue(fake_selector_, "123",
+                                /* simulate_key_presses = */ true, _, _));
+  }
+
+  EXPECT_CALL(callback_,
+              Run(Pointee(AllOf(
+                  Property(&ProcessedActionProto::status, ACTION_APPLIED),
+                  Property(&ProcessedActionProto::set_form_field_value_result,
+                           Property(&SetFormFieldValueProto::Result::
+                                        fallback_to_simulate_key_presses,
+                                    true))))));
+
+  action.ProcessAction(callback_.Get());
+}
+
+}  // namespace
+}  // namespace autofill_assistant
\ No newline at end of file
diff --git a/components/autofill_assistant/browser/client.h b/components/autofill_assistant/browser/client.h
index f71247f0..8e372fd 100644
--- a/components/autofill_assistant/browser/client.h
+++ b/components/autofill_assistant/browser/client.h
@@ -15,6 +15,7 @@
 
 namespace autofill_assistant {
 class AccessTokenFetcher;
+class WebsiteLoginFetcher;
 
 // A client interface that needs to be supplied to the controller by the
 // embedder.
@@ -44,6 +45,9 @@
   // Returns the current active personal data manager.
   virtual autofill::PersonalDataManager* GetPersonalDataManager() = 0;
 
+  // Returns the currently active login fetcher.
+  virtual WebsiteLoginFetcher* GetWebsiteLoginFetcher() = 0;
+
   // Returns the server URL to be used for requests to the backend.
   virtual std::string GetServerUrl() = 0;
 
diff --git a/components/autofill_assistant/browser/client_memory.cc b/components/autofill_assistant/browser/client_memory.cc
index ddfa6fd..8103c12 100644
--- a/components/autofill_assistant/browser/client_memory.cc
+++ b/components/autofill_assistant/browser/client_memory.cc
@@ -13,20 +13,21 @@
 ClientMemory::ClientMemory() = default;
 ClientMemory::~ClientMemory() = default;
 
-const autofill::CreditCard* ClientMemory::selected_card() {
+const autofill::CreditCard* ClientMemory::selected_card() const {
   if (selected_card_.has_value())
     return selected_card_->get();
   return nullptr;
 }
 
-bool ClientMemory::has_selected_card() {
+bool ClientMemory::has_selected_card() const {
   return selected_card_.has_value();
 }
 
 const autofill::AutofillProfile* ClientMemory::selected_address(
-    const std::string& name) {
-  if (selected_addresses_.find(name) != selected_addresses_.end())
-    return selected_addresses_[name].get();
+    const std::string& name) const {
+  auto it = selected_addresses_.find(name);
+  if (it != selected_addresses_.end())
+    return it->second.get();
 
   return nullptr;
 }
@@ -42,10 +43,25 @@
   selected_addresses_[name] = std::move(address);
 }
 
-bool ClientMemory::has_selected_address(const std::string& name) {
+bool ClientMemory::has_selected_address(const std::string& name) const {
   return selected_addresses_.find(name) != selected_addresses_.end();
 }
 
+void ClientMemory::set_selected_login(const WebsiteLoginFetcher::Login& login) {
+  selected_login_ = login;
+}
+
+bool ClientMemory::has_selected_login() const {
+  return selected_login_.has_value();
+}
+
+const WebsiteLoginFetcher::Login* ClientMemory::selected_login() const {
+  if (selected_login_.has_value()) {
+    return &(*selected_login_);
+  }
+  return nullptr;
+}
+
 std::string ClientMemory::GetAllAddressKeyNames() const {
   std::vector<std::string> entries;
   for (const auto& entry : selected_addresses_) {
diff --git a/components/autofill_assistant/browser/client_memory.h b/components/autofill_assistant/browser/client_memory.h
index 1175e9bf..ee24fb35 100644
--- a/components/autofill_assistant/browser/client_memory.h
+++ b/components/autofill_assistant/browser/client_memory.h
@@ -11,45 +11,55 @@
 #include "base/optional.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill_assistant/browser/website_login_fetcher.h"
 
 namespace autofill_assistant {
 // Data shared between scripts and actions.
 class ClientMemory {
  public:
   ClientMemory();
-  virtual ~ClientMemory();
+  ~ClientMemory();
 
   // Selected credit card, if any. It will be a nullptr if didn't select
   // anything or if selected 'Fill manually'.
-  virtual const autofill::CreditCard* selected_card();
+  const autofill::CreditCard* selected_card() const;
 
   // Return true if card has been selected, otherwise return false.
   // Note that selected_card() might return nullptr when has_selected_card() is
   // true because fill manually was chosen.
-  virtual bool has_selected_card();
+  bool has_selected_card() const;
 
   // Selected address for |name|. It will be a nullptr if didn't select anything
   // or if selected 'Fill manually'.
-  virtual const autofill::AutofillProfile* selected_address(
-      const std::string& name);
+  const autofill::AutofillProfile* selected_address(
+      const std::string& name) const;
 
   // Return true if address has been selected, otherwise return false.
   // Note that selected_address() might return nullptr when
   // has_selected_address() is true because fill manually was chosen.
-  virtual bool has_selected_address(const std::string& name);
+  bool has_selected_address(const std::string& name) const;
 
   // Set the selected card.
-  virtual void set_selected_card(std::unique_ptr<autofill::CreditCard> card);
+  void set_selected_card(std::unique_ptr<autofill::CreditCard> card);
 
   // Set the selected address for |name|.
-  virtual void set_selected_address(
-      const std::string& name,
-      std::unique_ptr<autofill::AutofillProfile> address);
+  void set_selected_address(const std::string& name,
+                            std::unique_ptr<autofill::AutofillProfile> address);
 
-  virtual std::string GetAllAddressKeyNames() const;
+  // Set the selected login.
+  void set_selected_login(const WebsiteLoginFetcher::Login& login);
+
+  // Return true if a login has been selected, otherwise false.
+  bool has_selected_login() const;
+
+  // The selected login or nullptr if no login was selected.
+  const WebsiteLoginFetcher::Login* selected_login() const;
+
+  std::string GetAllAddressKeyNames() const;
 
  private:
   base::Optional<std::unique_ptr<autofill::CreditCard>> selected_card_;
+  base::Optional<WebsiteLoginFetcher::Login> selected_login_;
 
   // The address key requested by the autofill action.
   std::map<std::string, std::unique_ptr<autofill::AutofillProfile>>
diff --git a/components/autofill_assistant/browser/client_status.cc b/components/autofill_assistant/browser/client_status.cc
index da69a37..9b16d5cb 100644
--- a/components/autofill_assistant/browser/client_status.cc
+++ b/components/autofill_assistant/browser/client_status.cc
@@ -122,6 +122,10 @@
       out << "NAVIGATION_ERROR";
       break;
 
+    case ProcessedActionStatusProto::AUTOFILL_INFO_NOT_AVAILABLE:
+      out << "AUTOFILL_INFO_NOT_AVAILABLE";
+      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 81ca5885..da16d9d 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -144,6 +144,10 @@
   return client_->GetPersonalDataManager();
 }
 
+WebsiteLoginFetcher* Controller::GetWebsiteLoginFetcher() {
+  return client_->GetWebsiteLoginFetcher();
+}
+
 content::WebContents* Controller::GetWebContents() {
   return web_contents();
 }
@@ -481,7 +485,7 @@
   ClearInfoBox();
   SetDetails(nullptr);
   SetUserActions(nullptr);
-  SetPaymentRequestOptions(nullptr);
+  SetPaymentRequestOptions(nullptr, nullptr);
   EnterState(AutofillAssistantState::STOPPED);
 }
 
@@ -928,7 +932,7 @@
   // when the user clicks "Cancel" during that action.
   payment_request_info->succeed = true;
 
-  SetPaymentRequestOptions(nullptr);
+  SetPaymentRequestOptions(nullptr, nullptr);
   std::move(callback).Run(std::move(payment_request_info));
 }
 
@@ -938,7 +942,7 @@
 
   auto callback =
       std::move(payment_request_options_->additional_actions_callback);
-  SetPaymentRequestOptions(nullptr);
+  SetPaymentRequestOptions(nullptr, nullptr);
   std::move(callback).Run(index);
 }
 
@@ -947,7 +951,7 @@
     return;
 
   auto callback = std::move(payment_request_options_->terms_link_callback);
-  SetPaymentRequestOptions(nullptr);
+  SetPaymentRequestOptions(nullptr, nullptr);
   std::move(callback).Run(link);
 }
 
@@ -1013,6 +1017,17 @@
   }
 }
 
+void Controller::SetLoginOption(std::string identifier) {
+  if (!payment_request_info_ || !payment_request_options_)
+    return;
+
+  payment_request_info_->login_choice_identifier.assign(identifier);
+  UpdatePaymentRequestActions();
+  for (ControllerObserver& observer : observers_) {
+    observer.OnPaymentRequestInformationChanged(payment_request_info_.get());
+  }
+}
+
 void Controller::UpdatePaymentRequestActions() {
   // TODO(crbug.com/806868): This method uses #SetUserActions(), which means
   // that updating the PR action buttons will also clear the suggestions. We
@@ -1047,9 +1062,12 @@
       payment_request_info_->terms_and_conditions != NOT_SELECTED ||
       payment_request_options_->accept_terms_and_conditions_text.empty();
 
+  bool login_ok = !payment_request_options_->request_login_choice ||
+                  !payment_request_info_->login_choice_identifier.empty();
+
   bool confirm_button_enabled = contact_info_ok && shipping_address_ok &&
                                 payment_method_ok && billing_address_ok &&
-                                terms_ok;
+                                terms_ok && login_ok;
 
   UserAction confirm(payment_request_options_->confirm_action);
   confirm.SetEnabled(confirm_button_enabled);
@@ -1343,7 +1361,8 @@
 }
 
 void Controller::SetPaymentRequestOptions(
-    std::unique_ptr<PaymentRequestOptions> options) {
+    std::unique_ptr<PaymentRequestOptions> options,
+    std::unique_ptr<PaymentInformation> information) {
   DCHECK(!options ||
          (options->confirm_callback && options->additional_actions_callback &&
           options->terms_link_callback));
@@ -1351,15 +1370,8 @@
   if (payment_request_options_ == nullptr && options == nullptr)
     return;
 
-  if (options) {
-    payment_request_info_ = std::make_unique<PaymentInformation>();
-
-    // TODO(crbug.com/806868): set initial state according to proto.
-    payment_request_info_->terms_and_conditions =
-        options->initial_terms_and_conditions;
-  }
-
   payment_request_options_ = std::move(options);
+  payment_request_info_ = std::move(information);
   UpdatePaymentRequestActions();
   for (ControllerObserver& observer : observers_) {
     observer.OnPaymentRequestOptionsChanged(payment_request_options_.get());
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index 9f9b38a..f57434b 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -96,6 +96,7 @@
   ClientMemory* GetClientMemory() override;
   const TriggerContext* GetTriggerContext() override;
   autofill::PersonalDataManager* GetPersonalDataManager() override;
+  WebsiteLoginFetcher* GetWebsiteLoginFetcher() override;
   content::WebContents* GetWebContents() override;
   void SetTouchableElementArea(const ElementAreaProto& area) override;
   void SetStatusMessage(const std::string& message) override;
@@ -126,7 +127,8 @@
 
   void EnterState(AutofillAssistantState state) override;
   void SetPaymentRequestOptions(
-      std::unique_ptr<PaymentRequestOptions> options) override;
+      std::unique_ptr<PaymentRequestOptions> options,
+      std::unique_ptr<PaymentInformation> information) override;
   void OnScriptError(const std::string& error_message,
                      Metrics::DropOutReason reason);
 
@@ -155,6 +157,7 @@
   void SetCreditCard(std::unique_ptr<autofill::CreditCard> card) override;
   void SetTermsAndConditions(
       TermsAndConditionsState terms_and_conditions) override;
+  void SetLoginOption(std::string identifier) override;
   void OnTermsAndConditionsLinkClicked(int link) override;
   void GetTouchableArea(std::vector<RectF>* area) const override;
   void GetRestrictedArea(std::vector<RectF>* area) const override;
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index 8bd476f..f971edd 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -61,6 +61,7 @@
   autofill::PersonalDataManager* GetPersonalDataManager() override {
     return nullptr;
   }
+  WebsiteLoginFetcher* GetWebsiteLoginFetcher() override { return nullptr; }
   std::string GetServerUrl() override { return ""; }
   std::string GetAccountEmailAddress() override { return ""; }
   std::string GetLocale() override { return ""; }
@@ -503,42 +504,42 @@
 }
 
 TEST_F(ControllerTest, Reset) {
-    // 1. Fetch scripts for URL, which in contains a single "reset" script.
-    SupportsScriptResponseProto script_response;
-    auto* reset_script = AddRunnableScript(&script_response, "reset");
-    RunOnce(reset_script);
-    std::string script_response_str;
-    script_response.SerializeToString(&script_response_str);
-    EXPECT_CALL(*mock_service_, OnGetScriptsForUrl(_, _, _))
-        .WillRepeatedly(RunOnceCallback<2>(true, script_response_str));
+  // 1. Fetch scripts for URL, which in contains a single "reset" script.
+  SupportsScriptResponseProto script_response;
+  auto* reset_script = AddRunnableScript(&script_response, "reset");
+  RunOnce(reset_script);
+  std::string script_response_str;
+  script_response.SerializeToString(&script_response_str);
+  EXPECT_CALL(*mock_service_, OnGetScriptsForUrl(_, _, _))
+      .WillRepeatedly(RunOnceCallback<2>(true, script_response_str));
 
-    Start("http://a.example.com/path");
-    EXPECT_THAT(controller_->GetUserActions(),
-                ElementsAre(Property(&UserAction::chip,
-                                     Field(&Chip::text, StrEq("reset")))));
+  Start("http://a.example.com/path");
+  EXPECT_THAT(controller_->GetUserActions(),
+              ElementsAre(Property(&UserAction::chip,
+                                   Field(&Chip::text, StrEq("reset")))));
 
-    // 2. Execute the "reset" script, which contains a reset action.
-    ActionsResponseProto actions_response;
-    actions_response.add_actions()->mutable_reset();
-    std::string actions_response_str;
-    actions_response.SerializeToString(&actions_response_str);
-    EXPECT_CALL(*mock_service_, OnGetActions(StrEq("reset"), _, _, _, _, _))
-        .WillOnce(RunOnceCallback<5>(true, actions_response_str));
+  // 2. Execute the "reset" script, which contains a reset action.
+  ActionsResponseProto actions_response;
+  actions_response.add_actions()->mutable_reset();
+  std::string actions_response_str;
+  actions_response.SerializeToString(&actions_response_str);
+  EXPECT_CALL(*mock_service_, OnGetActions(StrEq("reset"), _, _, _, _, _))
+      .WillOnce(RunOnceCallback<5>(true, actions_response_str));
 
-    controller_->GetClientMemory()->set_selected_card(
-        std::make_unique<autofill::CreditCard>());
-    EXPECT_TRUE(controller_->GetClientMemory()->has_selected_card());
+  controller_->GetClientMemory()->set_selected_card(
+      std::make_unique<autofill::CreditCard>());
+  EXPECT_TRUE(controller_->GetClientMemory()->has_selected_card());
 
-    EXPECT_TRUE(controller_->PerformUserAction(0));
+  EXPECT_TRUE(controller_->PerformUserAction(0));
 
-    // Resetting should have cleared the client memory
-    EXPECT_FALSE(controller_->GetClientMemory()->has_selected_card());
+  // Resetting should have cleared the client memory
+  EXPECT_FALSE(controller_->GetClientMemory()->has_selected_card());
 
-    // The reset script should be available again, even though it's marked
-    // RunOnce, as the script state should have been cleared as well.
-    EXPECT_THAT(controller_->GetUserActions(),
-                ElementsAre(Property(&UserAction::chip,
-                                     Field(&Chip::text, StrEq("reset")))));
+  // The reset script should be available again, even though it's marked
+  // RunOnce, as the script state should have been cleared as well.
+  EXPECT_THAT(controller_->GetUserActions(),
+              ElementsAre(Property(&UserAction::chip,
+                                   Field(&Chip::text, StrEq("reset")))));
 }
 
 TEST_F(ControllerTest, RefreshScriptWhenDomainChanges) {
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.cc b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
index 6d85266..4917c1c 100644
--- a/components/autofill_assistant/browser/fake_script_executor_delegate.cc
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
@@ -46,6 +46,10 @@
   return nullptr;
 }
 
+WebsiteLoginFetcher* FakeScriptExecutorDelegate::GetWebsiteLoginFetcher() {
+  return nullptr;
+}
+
 content::WebContents* FakeScriptExecutorDelegate::GetWebContents() {
   return nullptr;
 }
@@ -95,8 +99,10 @@
 }
 
 void FakeScriptExecutorDelegate::SetPaymentRequestOptions(
-    std::unique_ptr<PaymentRequestOptions> options) {
+    std::unique_ptr<PaymentRequestOptions> options,
+    std::unique_ptr<PaymentInformation> information) {
   payment_request_options_ = std::move(options);
+  payment_request_info_ = std::move(information);
 }
 
 void FakeScriptExecutorDelegate::SetViewportMode(ViewportMode mode) {
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.h b/components/autofill_assistant/browser/fake_script_executor_delegate.h
index 9930b56..7fbb0fd594 100644
--- a/components/autofill_assistant/browser/fake_script_executor_delegate.h
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.h
@@ -33,6 +33,7 @@
   ClientMemory* GetClientMemory() override;
   TriggerContext* GetTriggerContext() override;
   autofill::PersonalDataManager* GetPersonalDataManager() override;
+  WebsiteLoginFetcher* GetWebsiteLoginFetcher() override;
   content::WebContents* GetWebContents() override;
   void EnterState(AutofillAssistantState state) override;
   void SetTouchableElementArea(const ElementAreaProto& element) override;
@@ -48,7 +49,8 @@
   void SetUserActions(
       std::unique_ptr<std::vector<UserAction>> user_actions) override;
   void SetPaymentRequestOptions(
-      std::unique_ptr<PaymentRequestOptions> options) override;
+      std::unique_ptr<PaymentRequestOptions> options,
+      std::unique_ptr<PaymentInformation> information) override;
   void SetViewportMode(ViewportMode mode) override;
   ViewportMode GetViewportMode() override;
   void SetPeekMode(ConfigureBottomSheetProto::PeekMode peek_mode) override;
@@ -112,6 +114,7 @@
   std::unique_ptr<InfoBox> info_box_;
   std::unique_ptr<std::vector<UserAction>> user_actions_;
   std::unique_ptr<PaymentRequestOptions> payment_request_options_;
+  std::unique_ptr<PaymentInformation> payment_request_info_;
   bool navigating_to_new_document_ = false;
   bool navigation_error_ = false;
   std::set<ScriptExecutorDelegate::Listener*> listeners_;
diff --git a/components/autofill_assistant/browser/mock_website_login_fetcher.cc b/components/autofill_assistant/browser/mock_website_login_fetcher.cc
new file mode 100644
index 0000000..d538a20b
--- /dev/null
+++ b/components/autofill_assistant/browser/mock_website_login_fetcher.cc
@@ -0,0 +1,12 @@
+// 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/autofill_assistant/browser/mock_website_login_fetcher.h"
+
+namespace autofill_assistant {
+
+MockWebsiteLoginFetcher::MockWebsiteLoginFetcher() {}
+MockWebsiteLoginFetcher::~MockWebsiteLoginFetcher() {}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/mock_website_login_fetcher.h b/components/autofill_assistant/browser/mock_website_login_fetcher.h
new file mode 100644
index 0000000..57dc326
--- /dev/null
+++ b/components/autofill_assistant/browser/mock_website_login_fetcher.h
@@ -0,0 +1,46 @@
+// 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_AUTOFILL_ASSISTANT_BROWSER_MOCK_WEBSITE_LOGIN_FETCHER_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_WEBSITE_LOGIN_FETCHER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "components/autofill_assistant/browser/website_login_fetcher.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+
+// Mock login fetcher for unit tests.
+class MockWebsiteLoginFetcher : public WebsiteLoginFetcher {
+ public:
+  MockWebsiteLoginFetcher();
+  ~MockWebsiteLoginFetcher() override;
+
+  void GetLoginsForUrl(
+      const GURL& url,
+      base::OnceCallback<void(std::vector<Login>)> callback) override {
+    OnGetLoginsForUrl(url, callback);
+  }
+
+  MOCK_METHOD2(OnGetLoginsForUrl,
+               void(const GURL& domain,
+                    base::OnceCallback<void(std::vector<Login>)>&));
+
+  void GetPasswordForLogin(
+      const Login& login,
+      base::OnceCallback<void(bool, std::string)> callback) override {
+    OnGetPasswordForLogin(login, callback);
+  }
+
+  MOCK_METHOD2(OnGetPasswordForLogin,
+               void(const Login& login,
+                    base::OnceCallback<void(bool, std::string)>&));
+
+  DISALLOW_COPY_AND_ASSIGN(MockWebsiteLoginFetcher);
+};
+
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_WEBSITE_LOGIN_FETCHER_H_
diff --git a/components/autofill_assistant/browser/payment_request.cc b/components/autofill_assistant/browser/payment_request.cc
index 88ed062..351b625 100644
--- a/components/autofill_assistant/browser/payment_request.cc
+++ b/components/autofill_assistant/browser/payment_request.cc
@@ -6,9 +6,16 @@
 
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/common/password_form.h"
 
 namespace autofill_assistant {
 
+LoginChoice::LoginChoice(const std::string& id,
+                         const std::string& text,
+                         int priority)
+    : identifier(id), label(text), preselect_priority(priority) {}
+LoginChoice::~LoginChoice() = default;
+
 PaymentInformation::PaymentInformation() = default;
 PaymentInformation::~PaymentInformation() = default;
 
diff --git a/components/autofill_assistant/browser/payment_request.h b/components/autofill_assistant/browser/payment_request.h
index 47b771b28..9a4957f 100644
--- a/components/autofill_assistant/browser/payment_request.h
+++ b/components/autofill_assistant/browser/payment_request.h
@@ -29,6 +29,20 @@
   REQUIRES_REVIEW = 2,
 };
 
+// Represents a concrete login choice in the UI, e.g., 'Guest checkout' or
+// a particular Chrome PWM login account.
+struct LoginChoice {
+  LoginChoice(const std::string& id, const std::string& text, int priority);
+  ~LoginChoice();
+
+  // Uniquely identifies this login choice.
+  std::string identifier;
+  // The label to display to the user.
+  std::string label;
+  // The priority to pre-select this choice (-1 == not set/automatic).
+  int preselect_priority = -1;
+};
+
 // Struct for holding the payment information data.
 struct PaymentInformation {
   PaymentInformation();
@@ -38,6 +52,7 @@
   std::unique_ptr<autofill::CreditCard> card;
   std::unique_ptr<autofill::AutofillProfile> shipping_address;
   std::unique_ptr<autofill::AutofillProfile> billing_address;
+  std::string login_choice_identifier;
   std::string payer_name;
   std::string payer_phone;
   std::string payer_email;
@@ -54,6 +69,7 @@
   bool request_payer_phone = false;
   bool request_shipping = false;
   bool request_payment_method = false;
+  bool request_login_choice = false;
 
   bool require_billing_postal_code = false;
   std::string billing_postal_code_missing_text;
@@ -63,7 +79,9 @@
   bool show_terms_as_checkbox = false;
 
   std::vector<std::string> supported_basic_card_networks;
+  std::vector<LoginChoice> login_choices;
   std::string default_email;
+  std::string login_section_title;
   UserActionProto confirm_action;
   std::vector<UserActionProto> additional_actions;
   TermsAndConditionsState initial_terms_and_conditions = NOT_SELECTED;
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index 92e8b635..42ac7a47 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -204,7 +204,8 @@
 }
 
 void ScriptExecutor::GetPaymentInformation(
-    std::unique_ptr<PaymentRequestOptions> options) {
+    std::unique_ptr<PaymentRequestOptions> options,
+    std::unique_ptr<PaymentInformation> information) {
   options->confirm_callback = base::BindOnce(
       &ScriptExecutor::OnGetPaymentInformation, weak_ptr_factory_.GetWeakPtr(),
       std::move(options->confirm_callback));
@@ -215,7 +216,8 @@
   options->terms_link_callback = base::BindOnce(
       &ScriptExecutor::OnTermsAndConditionsLinkClicked,
       weak_ptr_factory_.GetWeakPtr(), std::move(options->terms_link_callback));
-  delegate_->SetPaymentRequestOptions(std::move(options));
+  delegate_->SetPaymentRequestOptions(std::move(options),
+                                      std::move(information));
   delegate_->EnterState(AutofillAssistantState::PROMPT);
 }
 
@@ -488,6 +490,10 @@
   return delegate_->GetPersonalDataManager();
 }
 
+WebsiteLoginFetcher* ScriptExecutor::GetWebsiteLoginFetcher() {
+  return delegate_->GetWebsiteLoginFetcher();
+}
+
 content::WebContents* ScriptExecutor::GetWebContents() {
   return delegate_->GetWebContents();
 }
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index d30814f9..2cd4199 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -119,7 +119,8 @@
       ClickAction::ClickType click_type,
       base::OnceCallback<void(const ClientStatus&)> callback) override;
   void GetPaymentInformation(
-      std::unique_ptr<PaymentRequestOptions> options) override;
+      std::unique_ptr<PaymentRequestOptions> options,
+      std::unique_ptr<PaymentInformation> information) override;
   void GetFullCard(GetFullCardCallback callback) override;
   void Prompt(std::unique_ptr<std::vector<UserAction>> user_actions) override;
   void CancelPrompt() override;
@@ -187,6 +188,7 @@
   void Restart() override;
   ClientMemory* GetClientMemory() override;
   autofill::PersonalDataManager* GetPersonalDataManager() override;
+  WebsiteLoginFetcher* GetWebsiteLoginFetcher() override;
   content::WebContents* GetWebContents() override;
   void SetDetails(std::unique_ptr<Details> details) override;
   void ClearInfoBox() override;
diff --git a/components/autofill_assistant/browser/script_executor_delegate.h b/components/autofill_assistant/browser/script_executor_delegate.h
index dd254916..c1343510 100644
--- a/components/autofill_assistant/browser/script_executor_delegate.h
+++ b/components/autofill_assistant/browser/script_executor_delegate.h
@@ -33,6 +33,7 @@
 class ClientMemory;
 struct ClientSettings;
 class TriggerContext;
+class WebsiteLoginFetcher;
 
 class ScriptExecutorDelegate {
  public:
@@ -51,6 +52,7 @@
   virtual ClientMemory* GetClientMemory() = 0;
   virtual const TriggerContext* GetTriggerContext() = 0;
   virtual autofill::PersonalDataManager* GetPersonalDataManager() = 0;
+  virtual WebsiteLoginFetcher* GetWebsiteLoginFetcher() = 0;
   virtual content::WebContents* GetWebContents() = 0;
   virtual void EnterState(AutofillAssistantState state) = 0;
 
@@ -65,7 +67,8 @@
   virtual void SetInfoBox(const InfoBox& info_box) = 0;
   virtual void ClearInfoBox() = 0;
   virtual void SetPaymentRequestOptions(
-      std::unique_ptr<PaymentRequestOptions> options) = 0;
+      std::unique_ptr<PaymentRequestOptions> options,
+      std::unique_ptr<PaymentInformation> information) = 0;
   virtual void SetProgress(int progress) = 0;
   virtual void SetProgressVisible(bool visible) = 0;
   virtual void SetUserActions(
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index f865ea6..952451b 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -433,6 +433,8 @@
   // If set, this means that the user clicked on one of the terms and conditions
   // links.
   optional int32 terms_link = 5;
+  // The payload of the chosen login option.
+  optional bytes login_payload = 6;
 }
 
 message ProcessedActionProto {
@@ -650,6 +652,10 @@
 
   // A selector included into the current action is invalid.
   INVALID_SELECTOR = 20;
+
+  // The requested autofill info (e.g., Chrome password manager login) was not
+  // available. It might have been recently deleted.
+  AUTOFILL_INFO_NOT_AVAILABLE = 21;
 }
 
 // The pseudo type values come from
@@ -855,7 +861,8 @@
   repeated RequiredField required_fields = 6;
 }
 
-// Fill a form with a credit card if there is, otherwise fail this action.
+// Fill a form with a credit card if there is one stored in client memory,
+// otherwise fail this action.
 message UseCreditCardProto {
   // Message used to indicate what form fields should be filled with what
   // information.
@@ -1124,6 +1131,39 @@
   optional bool request_payer_phone = 4;
 }
 
+message LoginDetailsProto {
+  // A custom login option which will be handled by the backend, e.g.,
+  // 'Guest checkout' or 'Log in with Google'.
+  message LoginOptionCustomProto {
+    // The label to display to the user.
+    optional string label = 1;
+  }
+
+  // Offers all matching Chrome password manager logins for the current website.
+  message LoginOptionPasswordManagerProto {}
+
+  message LoginOptionProto {
+    // If the option was chosen, this payload will be returned to the server.
+    optional bytes payload = 1;
+
+    // Whether the UI should be hidden if this login option is the only
+    // available choice (i.e., this option will be implicitly selected).
+    optional bool hide_if_no_other_options = 2;
+
+    // Determines the priority with which to pre-select this login choice.
+    // The lower the value, the higher the priority.
+    optional int32 preselection_priority = 3;
+    oneof type {
+      LoginOptionCustomProto custom = 4;
+      LoginOptionPasswordManagerProto password_manager = 5;
+    }
+  }
+  // The title for the login selection (e.g., 'Login details for <domain>').
+  optional string section_title = 1;
+  // The list of available login options.
+  repeated LoginOptionProto login_options = 2;
+}
+
 // Asks to provide the data used by UseAddressAction and
 // UseCreditCardAction.
 message GetPaymentInformationProto {
@@ -1180,6 +1220,8 @@
   // The error message to display when the billing address is missing the
   // postal code. This field is mandatory if |require_postal_code| is true.
   optional string billing_postal_code_missing_text = 15;
+  // The login details that should be gathered.
+  optional LoginDetailsProto login_details = 16;
 }
 
 // Resets Autofill Assistant: clears any state and server payload.
@@ -1323,6 +1365,12 @@
       // layout. This can also be used for keyboard control sequences such
       // as "\r" or "\t".
       string keyboard_input = 3;
+      // Use the username from the Chrome password manager login previously
+      // selected in a GetPaymentInformationAction.
+      bool use_username = 4;
+      // Use the password from the Chrome password manager login previously
+      // selected in a GetPaymentInformationAction.
+      bool use_password = 5;
     }
   }
 
diff --git a/components/autofill_assistant/browser/ui_delegate.h b/components/autofill_assistant/browser/ui_delegate.h
index 680f94b9..5bef8a54 100644
--- a/components/autofill_assistant/browser/ui_delegate.h
+++ b/components/autofill_assistant/browser/ui_delegate.h
@@ -119,6 +119,10 @@
   virtual void SetTermsAndConditions(
       TermsAndConditionsState terms_and_conditions) = 0;
 
+  // Sets the chosen login option, pertaining to the current payment request
+  // options.
+  virtual void SetLoginOption(std::string identifier) = 0;
+
   // Called when the user clicks a link on the terms & conditions message.
   virtual void OnTermsAndConditionsLinkClicked(int link) = 0;
 
diff --git a/components/autofill_assistant/browser/website_login_fetcher.cc b/components/autofill_assistant/browser/website_login_fetcher.cc
new file mode 100644
index 0000000..4ae6a9e3
--- /dev/null
+++ b/components/autofill_assistant/browser/website_login_fetcher.cc
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/website_login_fetcher.h"
+
+namespace autofill_assistant {
+
+WebsiteLoginFetcher::Login::Login(const GURL& _origin,
+                                  const std::string& _username)
+    : origin(_origin), username(_username) {}
+
+WebsiteLoginFetcher::Login::Login(const Login& other) = default;
+WebsiteLoginFetcher::Login::~Login() = default;
+
+}  // namespace autofill_assistant
\ No newline at end of file
diff --git a/components/autofill_assistant/browser/website_login_fetcher.h b/components/autofill_assistant/browser/website_login_fetcher.h
new file mode 100644
index 0000000..fe8c36df
--- /dev/null
+++ b/components/autofill_assistant/browser/website_login_fetcher.h
@@ -0,0 +1,51 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEBSITE_LOGIN_FETCHER_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEBSITE_LOGIN_FETCHER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "url/gurl.h"
+
+namespace autofill_assistant {
+
+// Common interface for implementations that fetch login details for websites.
+class WebsiteLoginFetcher {
+ public:
+  // Uniquely represents a particular login.
+  struct Login {
+    Login(const GURL& origin, const std::string& username);
+    Login(const Login& other);
+    ~Login();
+
+    // The scheme, host, port and path of the login website.
+    GURL origin;
+    std::string username;
+  };
+
+  WebsiteLoginFetcher() = default;
+  virtual ~WebsiteLoginFetcher() = default;
+
+  // Asynchronously returns all matching login details for |url| in the
+  // specified callback.
+  virtual void GetLoginsForUrl(
+      const GURL& url,
+      base::OnceCallback<void(std::vector<Login>)> callback) = 0;
+
+  // Retrieves the password for |login| in the specified |callback|, or |false|
+  // if the password could not be retrieved.
+  virtual void GetPasswordForLogin(
+      const Login& login,
+      base::OnceCallback<void(bool, std::string)> callback) = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(WebsiteLoginFetcher);
+};
+
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEBSITE_LOGIN_FETCHER_H_
diff --git a/components/autofill_assistant/browser/website_login_fetcher_impl.cc b/components/autofill_assistant/browser/website_login_fetcher_impl.cc
new file mode 100644
index 0000000..64ba33fe
--- /dev/null
+++ b/components/autofill_assistant/browser/website_login_fetcher_impl.cc
@@ -0,0 +1,178 @@
+// 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/autofill_assistant/browser/website_login_fetcher_impl.h"
+
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task/post_task.h"
+#include "components/password_manager/core/browser/form_fetcher_impl.h"
+#include "components/password_manager/core/browser/password_manager_util.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace autofill_assistant {
+
+// Represents a pending form fetcher request which will notify the
+// |WebsiteLoginFetcherImpl| when finished.
+class WebsiteLoginFetcherImpl::PendingRequest
+    : public password_manager::FormFetcher::Consumer {
+ public:
+  PendingRequest(
+      const password_manager::PasswordStore::FormDigest& form_digest,
+      const password_manager::PasswordManagerClient* client,
+      base::OnceCallback<void(const PendingRequest*)> notify_finished_callback)
+      : form_fetcher_(
+            std::make_unique<password_manager::FormFetcherImpl>(form_digest,
+                                                                client,
+                                                                true)),
+        notify_finished_callback_(std::move(notify_finished_callback)),
+        weak_ptr_factory_(this) {}
+
+  ~PendingRequest() override = default;
+  void Start() {
+    // Note: Currently, |FormFetcherImpl| has the default state NOT_WAITING.
+    // This has the unfortunate side effect that new consumers are immediately
+    // notified with an empty result. As a workaround to avoid this first
+    // notification, we register as consumer *after* we call |Fetch|.
+    form_fetcher_->Fetch();
+    form_fetcher_->AddConsumer(this);
+  }
+
+ protected:
+  // From password_manager::FormFetcher::Consumer:
+  // This implementation should be called by subclasses when they are finished.
+  void OnFetchCompleted() override {
+    // This needs to be done asynchronously, because it will lead to the
+    // destruction of |this|, which needs to happen *after* this call has
+    // returned.
+    base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
+                             base::BindOnce(&PendingRequest::NotifyFinished,
+                                            weak_ptr_factory_.GetWeakPtr()));
+  }
+  std::unique_ptr<password_manager::FormFetcher> form_fetcher_;
+
+ private:
+  void NotifyFinished() { std::move(notify_finished_callback_).Run(this); }
+
+  base::OnceCallback<void(const PendingRequest*)> notify_finished_callback_;
+  base::WeakPtrFactory<PendingRequest> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(PendingRequest);
+};
+
+// A pending request to fetch all logins that match the specified |form_digest|.
+class WebsiteLoginFetcherImpl::PendingFetchLoginsRequest
+    : public WebsiteLoginFetcherImpl::PendingRequest {
+ public:
+  PendingFetchLoginsRequest(
+      const password_manager::PasswordStore::FormDigest& form_digest,
+      const password_manager::PasswordManagerClient* client,
+      base::OnceCallback<void(std::vector<Login>)> callback,
+      base::OnceCallback<void(const PendingRequest*)> notify_finished_callback)
+      : PendingRequest(form_digest,
+                       client,
+                       std::move(notify_finished_callback)),
+        callback_(std::move(callback)) {}
+
+ protected:
+  // From PendingRequest:
+  void OnFetchCompleted() override {
+    std::vector<const autofill::PasswordForm*> matches =
+        form_fetcher_->GetNonFederatedMatches();
+    std::map<base::string16, const autofill::PasswordForm*> best_matches;
+    std::vector<const autofill::PasswordForm*> not_best_matches;
+    const autofill::PasswordForm* preferred_match = nullptr;
+    password_manager_util::FindBestMatches(matches, &best_matches,
+                                           &not_best_matches, &preferred_match);
+
+    std::vector<Login> logins;
+    for (const auto& match : best_matches) {
+      logins.emplace_back(match.second->origin,
+                          base::UTF16ToUTF8(match.second->username_value));
+    }
+    std::move(callback_).Run(logins);
+    PendingRequest::OnFetchCompleted();
+  }
+
+ private:
+  base::OnceCallback<void(std::vector<Login>)> callback_;
+};
+
+// A pending request to fetch the password for the specified |login|.
+class WebsiteLoginFetcherImpl::PendingFetchPasswordRequest
+    : public WebsiteLoginFetcherImpl::PendingRequest {
+ public:
+  PendingFetchPasswordRequest(
+      const password_manager::PasswordStore::FormDigest& form_digest,
+      const password_manager::PasswordManagerClient* client,
+      const Login& login,
+      base::OnceCallback<void(bool, std::string)> callback,
+      base::OnceCallback<void(const PendingRequest*)> notify_finished_callback)
+      : PendingRequest(form_digest,
+                       client,
+                       std::move(notify_finished_callback)),
+        login_(login),
+        callback_(std::move(callback)) {}
+
+ protected:
+  // From PendingRequest:
+  void OnFetchCompleted() override {
+    std::vector<const autofill::PasswordForm*> matches =
+        form_fetcher_->GetNonFederatedMatches();
+    for (const auto* match : matches) {
+      if (base::UTF16ToUTF8(match->username_value) == login_.username) {
+        std::move(callback_).Run(true,
+                                 base::UTF16ToUTF8(match->password_value));
+        PendingRequest::OnFetchCompleted();
+        return;
+      }
+    }
+    std::move(callback_).Run(false, std::string());
+    PendingRequest::OnFetchCompleted();
+  }
+
+ private:
+  Login login_;
+  base::OnceCallback<void(bool, std::string)> callback_;
+};
+
+WebsiteLoginFetcherImpl::WebsiteLoginFetcherImpl(
+    const password_manager::PasswordManagerClient* client)
+    : client_(client), weak_ptr_factory_(this) {}
+
+WebsiteLoginFetcherImpl::~WebsiteLoginFetcherImpl() = default;
+
+void WebsiteLoginFetcherImpl::GetLoginsForUrl(
+    const GURL& url,
+    base::OnceCallback<void(std::vector<Login>)> callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  password_manager::PasswordStore::FormDigest digest(
+      autofill::PasswordForm::Scheme::kHtml, url.GetOrigin().spec(), GURL());
+  pending_requests_.emplace_back(std::make_unique<PendingFetchLoginsRequest>(
+      digest, client_, std::move(callback),
+      base::BindOnce(&WebsiteLoginFetcherImpl::OnRequestFinished,
+                     weak_ptr_factory_.GetWeakPtr())));
+  pending_requests_.back()->Start();
+}
+
+void WebsiteLoginFetcherImpl::GetPasswordForLogin(
+    const Login& login,
+    base::OnceCallback<void(bool, std::string)> callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  password_manager::PasswordStore::FormDigest digest(
+      autofill::PasswordForm::Scheme::kHtml, login.origin.spec(), GURL());
+  pending_requests_.emplace_back(std::make_unique<PendingFetchPasswordRequest>(
+      digest, client_, login, std::move(callback),
+      base::BindOnce(&WebsiteLoginFetcherImpl::OnRequestFinished,
+                     weak_ptr_factory_.GetWeakPtr())));
+  pending_requests_.back()->Start();
+}
+
+void WebsiteLoginFetcherImpl::OnRequestFinished(const PendingRequest* request) {
+  base::EraseIf(pending_requests_, [request](const auto& candidate_request) {
+    return candidate_request.get() == request;
+  });
+}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/website_login_fetcher_impl.h b/components/autofill_assistant/browser/website_login_fetcher_impl.h
new file mode 100644
index 0000000..58ea3c2
--- /dev/null
+++ b/components/autofill_assistant/browser/website_login_fetcher_impl.h
@@ -0,0 +1,54 @@
+// 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_AUTOFILL_ASSISTANT_BROWSER_WEBSITE_LOGIN_FETCHER_IMPL_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEBSITE_LOGIN_FETCHER_IMPL_H_
+
+#include "base/memory/weak_ptr.h"
+#include "components/autofill_assistant/browser/website_login_fetcher.h"
+
+namespace password_manager {
+class PasswordManagerClient;
+}  // namespace password_manager
+
+namespace autofill_assistant {
+
+// Native implementation of the autofill assistant website login fetcher, which
+// wraps access to the Chrome password manager.
+class WebsiteLoginFetcherImpl : public WebsiteLoginFetcher {
+ public:
+  WebsiteLoginFetcherImpl(
+      const password_manager::PasswordManagerClient* client);
+  ~WebsiteLoginFetcherImpl() override;
+
+  // From WebsiteLoginFetcher:
+  void GetLoginsForUrl(
+      const GURL& url,
+      base::OnceCallback<void(std::vector<Login>)> callback) override;
+  void GetPasswordForLogin(
+      const Login& login,
+      base::OnceCallback<void(bool, std::string)> callback) override;
+
+ private:
+  class PendingRequest;
+  class PendingFetchLoginsRequest;
+  class PendingFetchPasswordRequest;
+
+  void OnRequestFinished(const PendingRequest* request);
+
+  const password_manager::PasswordManagerClient* client_;
+
+  // Fetch requests owned by the password manager, released when they are
+  // finished.
+  std::vector<std::unique_ptr<PendingRequest>> pending_requests_;
+
+  // Needs to be the last member.
+  base::WeakPtrFactory<WebsiteLoginFetcherImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebsiteLoginFetcherImpl);
+};
+
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEBSITE_LOGIN_FETCHER_IMPL_H_
diff --git a/components/module_installer/android/java/src/org/chromium/components/module_installer/ModuleInstallerImpl.java b/components/module_installer/android/java/src/org/chromium/components/module_installer/ModuleInstallerImpl.java
index f9487153..538eb05e 100644
--- a/components/module_installer/android/java/src/org/chromium/components/module_installer/ModuleInstallerImpl.java
+++ b/components/module_installer/android/java/src/org/chromium/components/module_installer/ModuleInstallerImpl.java
@@ -68,7 +68,7 @@
         // SplitCompat#install should always be run for the application first before it is run for
         // any activities.
         init();
-        SplitCompat.install(activity);
+        SplitCompat.installActivity(activity);
     }
 
     @Override
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index 502f77b..bc22e9f 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -258,6 +258,8 @@
       "http_credentials_cleaner.h",
       "leak_detection_delegate.cc",
       "leak_detection_delegate.h",
+      "leak_detection_delegate_helper.cc",
+      "leak_detection_delegate_helper.h",
       "leak_detection_dialog_utils.cc",
       "leak_detection_dialog_utils.h",
     ]
@@ -535,6 +537,7 @@
     sources += [
       "hsts_query_unittest.cc",
       "http_credentials_cleaner_unittest.cc",
+      "leak_detection_delegate_helper_unittest.cc",
       "leak_detection_delegate_unittest.cc",
     ]
   }
diff --git a/components/password_manager/core/browser/leak_detection/BUILD.gn b/components/password_manager/core/browser/leak_detection/BUILD.gn
index 8650d53..7c3dc53 100644
--- a/components/password_manager/core/browser/leak_detection/BUILD.gn
+++ b/components/password_manager/core/browser/leak_detection/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/config/jumbo.gni")
-import("//testing/libfuzzer/fuzzer_test.gni")
 import("//third_party/libprotobuf-mutator/fuzzable_proto_library.gni")
 
 fuzzable_proto_library("proto") {
@@ -97,11 +96,8 @@
   ]
 }
 
-fuzzer_test("encryption_utils_fuzzer") {
-  sources = [
-    "cipher_encrypt_fuzzer.cc",
-  ]
+group("fuzzers") {
   deps = [
-    ":leak_detection",
+    "//components/password_manager/core/browser/leak_detection/fuzzer",
   ]
 }
diff --git a/components/password_manager/core/browser/leak_detection/fuzzer/BUILD.gn b/components/password_manager/core/browser/leak_detection/fuzzer/BUILD.gn
new file mode 100644
index 0000000..323b1db
--- /dev/null
+++ b/components/password_manager/core/browser/leak_detection/fuzzer/BUILD.gn
@@ -0,0 +1,43 @@
+import("//testing/libfuzzer/fuzzer_test.gni")
+
+# Empty group for package discovery.
+group("fuzzer") {
+}
+
+fuzzer_test("cipher_encrypt_fuzzer") {
+  sources = [
+    "cipher_encrypt_fuzzer.cc",
+  ]
+  deps = [
+    "//components/password_manager/core/browser/leak_detection",
+  ]
+}
+
+fuzzer_test("cypher_encrypt_with_key_fuzzer") {
+  sources = [
+    "cypher_encrypt_with_key_fuzzer.cc",
+  ]
+  deps = [
+    "//components/password_manager/core/browser/leak_detection",
+    "//third_party/private-join-and-compute/src:ec_commutative_cipher",
+  ]
+}
+
+fuzzer_test("cypher_reencrypt_fuzzer") {
+  sources = [
+    "cypher_reencrypt_fuzzer.cc",
+  ]
+  deps = [
+    "//components/password_manager/core/browser/leak_detection",
+  ]
+}
+
+fuzzer_test("cypher_decrypt_fuzzer") {
+  sources = [
+    "cypher_decrypt_fuzzer.cc",
+  ]
+  deps = [
+    "//components/password_manager/core/browser/leak_detection",
+    "//third_party/private-join-and-compute/src:ec_commutative_cipher",
+  ]
+}
diff --git a/components/password_manager/core/browser/leak_detection/cipher_encrypt_fuzzer.cc b/components/password_manager/core/browser/leak_detection/fuzzer/cipher_encrypt_fuzzer.cc
similarity index 100%
rename from components/password_manager/core/browser/leak_detection/cipher_encrypt_fuzzer.cc
rename to components/password_manager/core/browser/leak_detection/fuzzer/cipher_encrypt_fuzzer.cc
diff --git a/components/password_manager/core/browser/leak_detection/fuzzer/cypher_decrypt_fuzzer.cc b/components/password_manager/core/browser/leak_detection/fuzzer/cypher_decrypt_fuzzer.cc
new file mode 100644
index 0000000..206026f
--- /dev/null
+++ b/components/password_manager/core/browser/leak_detection/fuzzer/cypher_decrypt_fuzzer.cc
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/leak_detection/encryption_utils.h"
+
+#include "third_party/boringssl/src/include/openssl/nid.h"
+#include "third_party/private-join-and-compute/src/crypto/ec_commutative_cipher.h"
+
+namespace password_manager {
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  using ::private_join_and_compute::ECCommutativeCipher;
+
+  if (size < 1)
+    return 0;
+  uint8_t key_size = data[0];
+  data++;
+  size--;
+
+  if (size < key_size)
+    return 0;
+
+  std::string key(reinterpret_cast<const char*>(data), key_size);
+  data += key_size;
+  size -= key_size;
+
+  // Check the key correctness. Otherwise, a crash happens.
+  auto cipher = ECCommutativeCipher::CreateFromKey(NID_X9_62_prime256v1, key,
+                                                   ECCommutativeCipher::SHA256);
+  if (!cipher.ok())
+    return 0;
+
+  std::string payload(reinterpret_cast<const char*>(data), size);
+  std::string result = password_manager::CipherDecrypt(payload, key);
+  return 0;
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/leak_detection/fuzzer/cypher_encrypt_with_key_fuzzer.cc b/components/password_manager/core/browser/leak_detection/fuzzer/cypher_encrypt_with_key_fuzzer.cc
new file mode 100644
index 0000000..a94c676
--- /dev/null
+++ b/components/password_manager/core/browser/leak_detection/fuzzer/cypher_encrypt_with_key_fuzzer.cc
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/leak_detection/encryption_utils.h"
+
+#include "third_party/boringssl/src/include/openssl/nid.h"
+#include "third_party/private-join-and-compute/src/crypto/ec_commutative_cipher.h"
+
+namespace password_manager {
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  using ::private_join_and_compute::ECCommutativeCipher;
+
+  if (size < 1)
+    return 0;
+  uint8_t key_size = data[0];
+  data++;
+  size--;
+
+  if (size < key_size)
+    return 0;
+
+  std::string key(reinterpret_cast<const char*>(data), key_size);
+  data += key_size;
+  size -= key_size;
+
+  // Check the key correctness. Otherwise, a crash happens.
+  auto cipher = ECCommutativeCipher::CreateFromKey(NID_X9_62_prime256v1, key,
+                                                   ECCommutativeCipher::SHA256);
+  if (!cipher.ok())
+    return 0;
+
+  std::string payload(reinterpret_cast<const char*>(data), size);
+  std::string result = password_manager::CipherEncryptWithKey(payload, key);
+  return 0;
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/leak_detection/fuzzer/cypher_reencrypt_fuzzer.cc b/components/password_manager/core/browser/leak_detection/fuzzer/cypher_reencrypt_fuzzer.cc
new file mode 100644
index 0000000..4b50a2f
--- /dev/null
+++ b/components/password_manager/core/browser/leak_detection/fuzzer/cypher_reencrypt_fuzzer.cc
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/leak_detection/encryption_utils.h"
+
+namespace password_manager {
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  std::string payload(reinterpret_cast<const char*>(data), size);
+  std::string key;
+  std::string result = password_manager::CipherEncrypt(payload, &key);
+  return 0;
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/leak_detection_delegate.cc b/components/password_manager/core/browser/leak_detection_delegate.cc
index dfb9811..0080969 100644
--- a/components/password_manager/core/browser/leak_detection_delegate.cc
+++ b/components/password_manager/core/browser/leak_detection_delegate.cc
@@ -9,6 +9,8 @@
 #include "components/autofill/core/common/save_password_progress_logger.h"
 #include "components/password_manager/core/browser/leak_detection/leak_detection_check.h"
 #include "components/password_manager/core/browser/leak_detection/leak_detection_check_factory_impl.h"
+#include "components/password_manager/core/browser/leak_detection_delegate_helper.h"
+#include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
 #include "components/password_manager/core/browser/password_manager_util.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
@@ -56,17 +58,37 @@
     logger.LogBoolean(Logger::STRING_LEAK_DETECTION_FINISHED, is_leaked);
   }
   if (is_leaked) {
-    DCHECK(is_leaked_timer_);
-    base::UmaHistogramTimes(
-        "PasswordManager.LeakDetection.NotifyIsLeakedTime",
-        std::exchange(is_leaked_timer_, nullptr)->Elapsed());
-    client_->NotifyUserCredentialsWereLeaked(
-        password_manager::CreateLeakTypeFromBools(
-            /*is_saved=*/false, /*is_reused=*/false, /*is_syncing=*/false),
-        url);
+    if (client_->GetPasswordSyncState() != SYNCING_NORMAL_ENCRYPTION) {
+      // If the credentials are not synced, the |CredentialLeakType| needed to
+      // show the correct notification is already determined.
+      OnShowLeakDetectionNotifiction(
+          CreateLeakTypeFromBools(/*is_saved=*/false, /*is_reused=*/false,
+                                  /*is_synced=*/false),
+          std::move(url), std::move(username));
+    } else {
+      // Otherwise query the helper to asynchronously determine the
+      // |CredentialLeakType|.
+      helper_ = std::make_unique<LeakDetectionDelegateHelper>(std::move(
+          base::BindOnce(&LeakDetectionDelegate::OnShowLeakDetectionNotifiction,
+                         base::Unretained(this))));
+      helper_->GetCredentialLeakType(client_->GetPasswordStore(),
+                                     std::move(url), std::move(username),
+                                     std::move(password));
+    }
   }
 }
 
+void LeakDetectionDelegate::OnShowLeakDetectionNotifiction(
+    CredentialLeakType leak_type,
+    GURL url,
+    base::string16 username) {
+  DCHECK(is_leaked_timer_);
+  base::UmaHistogramTimes("PasswordManager.LeakDetection.NotifyIsLeakedTime",
+                          std::exchange(is_leaked_timer_, nullptr)->Elapsed());
+  helper_.reset();
+  client_->NotifyUserCredentialsWereLeaked(leak_type, url);
+}
+
 void LeakDetectionDelegate::OnError(LeakDetectionError error) {
   leak_check_.reset();
 
diff --git a/components/password_manager/core/browser/leak_detection_delegate.h b/components/password_manager/core/browser/leak_detection_delegate.h
index a7170741..ebb9c2eb 100644
--- a/components/password_manager/core/browser/leak_detection_delegate.h
+++ b/components/password_manager/core/browser/leak_detection_delegate.h
@@ -6,10 +6,12 @@
 #define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LEAK_DETECTION_DELEGATE_H_
 
 #include <memory>
+#include <utility>
 
 #include "base/timer/elapsed_timer.h"
 #include "components/password_manager/core/browser/leak_detection/leak_detection_check_factory.h"
 #include "components/password_manager/core/browser/leak_detection/leak_detection_delegate_interface.h"
+#include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
 
 namespace autofill {
 struct PasswordForm;
@@ -18,9 +20,10 @@
 namespace password_manager {
 
 class LeakDetectionCheck;
+class LeakDetectionDelegateHelper;
 class PasswordManagerClient;
 
-// The helper class that incapsulates the requests and their processing.
+// The helper class that encapsulates the requests and their processing.
 class LeakDetectionDelegate : public LeakDetectionDelegateInterface {
  public:
   explicit LeakDetectionDelegate(PasswordManagerClient* client);
@@ -48,6 +51,14 @@
                            GURL url,
                            base::string16 username,
                            base::string16 password) override;
+
+  // Initiates the showing of the leak detection notification. If the account is
+  // synced, it is called by |helper_| after the |leak_type| was asynchronously
+  // determined.
+  void OnShowLeakDetectionNotifiction(CredentialLeakType leak_type,
+                                      GURL url,
+                                      base::string16 username);
+
   void OnError(LeakDetectionError error) override;
 
   PasswordManagerClient* client_;
@@ -60,6 +71,10 @@
   // Timer measuring the time it takes from StartLeakCheck() until a call to
   // OnLeakDetectionDone() with is_leaked = true.
   std::unique_ptr<base::ElapsedTimer> is_leaked_timer_;
+
+  // Helper class to asynchronously determine |CredentialLeakType| for leaked
+  // credentials.
+  std::unique_ptr<LeakDetectionDelegateHelper> helper_;
 };
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/leak_detection_delegate_helper.cc b/components/password_manager/core/browser/leak_detection_delegate_helper.cc
new file mode 100644
index 0000000..0c9d1f0
--- /dev/null
+++ b/components/password_manager/core/browser/leak_detection_delegate_helper.cc
@@ -0,0 +1,44 @@
+// 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/password_manager/core/browser/leak_detection_delegate_helper.h"
+
+#include "components/password_manager/core/browser/password_store.h"
+
+namespace password_manager {
+
+LeakDetectionDelegateHelper::LeakDetectionDelegateHelper(LeakTypeReply callback)
+    : callback_(std::move(callback)) {}
+
+LeakDetectionDelegateHelper::~LeakDetectionDelegateHelper() = default;
+
+void LeakDetectionDelegateHelper::GetCredentialLeakType(
+    PasswordStore* store,
+    GURL url,
+    base::string16 username,
+    base::string16 password) {
+  DCHECK(store);
+  url_ = std::move(url);
+  username_ = std::move(username);
+  password_ = std::move(password);
+  store->GetLoginsByPassword(password_, this);
+}
+
+void LeakDetectionDelegateHelper::OnGetPasswordStoreResults(
+    std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
+  bool is_saved = false;
+  for (const auto& form : results) {
+    if (form->origin == url_ && form->username_value == username_) {
+      is_saved = true;
+      break;
+    }
+  }
+  bool is_reused = results.size() > (is_saved ? 1 : 0);
+  CredentialLeakType leak_type =
+      CreateLeakTypeFromBools(is_saved, is_reused, /*is_synced=*/true);
+  std::move(callback_).Run(std::move(leak_type), std::move(url_),
+                           std::move(username_));
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/leak_detection_delegate_helper.h b/components/password_manager/core/browser/leak_detection_delegate_helper.h
new file mode 100644
index 0000000..f6d05fd7
--- /dev/null
+++ b/components/password_manager/core/browser/leak_detection_delegate_helper.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 COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LEAK_DETECTION_DELEGATE_HELPER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LEAK_DETECTION_DELEGATE_HELPER_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
+#include "components/password_manager/core/browser/password_store_consumer.h"
+#include "url/gurl.h"
+
+namespace password_manager {
+
+class LeakDetectionCheck;
+class PasswordStore;
+
+// Helper class to asynchronously requests all credentials with
+// a specific password from the |PasswordStore|.
+class LeakDetectionDelegateHelper : public PasswordStoreConsumer {
+ public:
+  // Type alias for |callback_|.
+  using LeakTypeReply = base::OnceCallback<void(int, GURL, base::string16)>;
+
+  explicit LeakDetectionDelegateHelper(LeakTypeReply callback);
+  ~LeakDetectionDelegateHelper() override;
+
+  // Request all credentials with |password| from |store|.
+  // Results are password to |OnGetPasswordStoreResults|.
+  void GetCredentialLeakType(PasswordStore* store,
+                             GURL url,
+                             base::string16 username,
+                             base::string16 password);
+
+ private:
+  // PasswordStoreConsumer:
+  // Is called by the |PasswordStore| once all credentials with the specific
+  // password are retrieved. Determine the |CredentialLeakType and invokes
+  // |callback_| when done.
+  void OnGetPasswordStoreResults(
+      std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
+
+  LeakTypeReply callback_;
+  GURL url_;
+  base::string16 username_;
+  base::string16 password_;
+
+  // Instances should be neither copyable nor assignable.
+  DISALLOW_COPY_AND_ASSIGN(LeakDetectionDelegateHelper);
+};
+
+}  // namespace password_manager
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LEAK_DETECTION_DELEGATE_HELPER_H_
diff --git a/components/password_manager/core/browser/leak_detection_delegate_helper_unittest.cc b/components/password_manager/core/browser/leak_detection_delegate_helper_unittest.cc
new file mode 100644
index 0000000..fa31d16
--- /dev/null
+++ b/components/password_manager/core/browser/leak_detection_delegate_helper_unittest.cc
@@ -0,0 +1,187 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/leak_detection_delegate_helper.h"
+
+#include <memory>
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/mock_callback.h"
+#include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
+#include "components/password_manager/core/browser/mock_password_store.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using autofill::PasswordForm;
+using base::ASCIIToUTF16;
+using base::BindOnce;
+using base::MockCallback;
+using base::Unretained;
+using testing::_;
+using testing::StrictMock;
+using testing::WithArg;
+
+namespace password_manager {
+
+namespace {
+constexpr char kLeakedPassword[] = "leaked_password";
+constexpr char kLeakedUsername[] = "leaked_username";
+constexpr char kOtherUsername[] = "other_username";
+constexpr char kLeakedOrigin[] = "https://www.leaked_origin.de/login";
+constexpr char kOtherOrigin[] = "https://www.other_origin.de/login";
+
+// Creates a |PasswordForm| with the supplied |origin| and |username|. The
+// password is always set to |kLeakedPassword|.
+PasswordForm CreateForm(const char* origin, const char* username) {
+  PasswordForm form;
+  form.origin = GURL(ASCIIToUTF16(origin));
+  form.username_value = std::move(ASCIIToUTF16(username));
+  form.password_value = ASCIIToUTF16(kLeakedPassword);
+  form.signon_realm = form.origin.GetOrigin().spec();
+  return form;
+}
+
+// Used to mimic the callback of the |PasswordStore|.  Converts the vector of
+// |PasswordForm|s to a vector of unique pointers to |PasswordForm|s.
+ACTION_P(InvokeConsumerWithPasswordForms, forms) {
+  std::vector<std::unique_ptr<autofill::PasswordForm>> results;
+  for (const auto& form : forms) {
+    results.push_back(std::make_unique<PasswordForm>(form));
+  }
+  arg0->OnGetPasswordStoreResults(std::move(results));
+}
+
+}  // namespace
+
+class LeakDetectionDelegateHelperTest : public testing::Test {
+ public:
+  LeakDetectionDelegateHelperTest() = default;
+  ~LeakDetectionDelegateHelperTest() override = default;
+
+ protected:
+  void SetUp() override {
+    store_ = new testing::StrictMock<MockPasswordStore>;
+
+    delegate_helper_ =
+        std::make_unique<LeakDetectionDelegateHelper>(callback_.Get());
+  }
+
+  void TearDown() override {
+    store_->ShutdownOnUIThread();
+    store_ = nullptr;
+  }
+
+  // Initiates determining the credential leak type.
+  void InitiateGetCredentialLeakType() {
+    delegate_helper_->GetCredentialLeakType(store_.get(), GURL(kLeakedOrigin),
+                                            ASCIIToUTF16(kLeakedUsername),
+                                            ASCIIToUTF16(kLeakedPassword));
+  }
+
+  // Sets the |PasswordForm|s which are retrieve from the |PasswordStore|.
+  void SetGetLoginByPasswordConsumerInvocation(
+      std::vector<PasswordForm> password_forms) {
+    EXPECT_CALL(*store_.get(), GetLoginsByPassword(_, _))
+        .WillRepeatedly(WithArg<1>(
+            InvokeConsumerWithPasswordForms(std::move(password_forms))));
+  }
+
+  // Set the expectation for the |CredentialLeakType| in the callback_.
+  void SetOnShowLeakDetectionNotificationExpectation(bool is_saved,
+                                                     bool is_reused) {
+    EXPECT_CALL(
+        callback_,
+        Run(CreateLeakTypeFromBools(is_saved, is_reused, /*is_synced=*/true),
+            GURL(kLeakedOrigin), ASCIIToUTF16(kLeakedUsername)))
+        .Times(1);
+  }
+
+  MockCallback<LeakDetectionDelegateHelper::LeakTypeReply> callback_;
+  scoped_refptr<MockPasswordStore> store_;
+  std::unique_ptr<LeakDetectionDelegateHelper> delegate_helper_;
+};
+
+// Credentials are neither saved nor is the password reused.
+TEST_F(LeakDetectionDelegateHelperTest, NeitherSaveNotReused) {
+  std::vector<PasswordForm> password_forms;
+
+  SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+  SetOnShowLeakDetectionNotificationExpectation(/*is_saved=*/false,
+                                                /*is_reused=*/false);
+  InitiateGetCredentialLeakType();
+}
+
+// Credentials are saved but the password is not reused.
+TEST_F(LeakDetectionDelegateHelperTest, SavedLeakedCredentials) {
+  std::vector<PasswordForm> password_forms = {
+      CreateForm(kLeakedOrigin, kLeakedUsername)};
+
+  SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+  SetOnShowLeakDetectionNotificationExpectation(/*is_saved=*/true,
+                                                /*is_reused=*/false);
+  InitiateGetCredentialLeakType();
+}
+
+// Credentials are saved and the password is reused on a different origin.
+TEST_F(LeakDetectionDelegateHelperTest,
+       SavedCredentialsAndReusedPasswordOnOtherOrigin) {
+  std::vector<PasswordForm> password_forms = {
+      CreateForm(kLeakedOrigin, kLeakedUsername),
+      CreateForm(kOtherOrigin, kLeakedUsername)};
+
+  SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+  SetOnShowLeakDetectionNotificationExpectation(/*is_saved=*/true,
+                                                /*is_reused=*/true);
+  InitiateGetCredentialLeakType();
+}
+
+// Credentials are saved and the password is reused on the same origin with
+// a different username.
+TEST_F(LeakDetectionDelegateHelperTest,
+       SavedCredentialsAndReusedPasswordWithOtherUsername) {
+  std::vector<PasswordForm> password_forms = {
+      CreateForm(kLeakedOrigin, kLeakedUsername),
+      CreateForm(kLeakedOrigin, kOtherUsername)};
+
+  SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+  SetOnShowLeakDetectionNotificationExpectation(/*is_saved=*/true,
+                                                /*is_reused=*/true);
+  InitiateGetCredentialLeakType();
+}
+
+// Credentials are not saved but the password is reused.
+TEST_F(LeakDetectionDelegateHelperTest, ReusedPasswordWithOtherUsername) {
+  std::vector<PasswordForm> password_forms = {
+      CreateForm(kLeakedOrigin, kOtherUsername)};
+
+  SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+  SetOnShowLeakDetectionNotificationExpectation(/*is_saved=*/false,
+                                                /*is_reused=*/true);
+  InitiateGetCredentialLeakType();
+}
+
+// Credentials are not saved but the password is reused on a different origin.
+TEST_F(LeakDetectionDelegateHelperTest, ReusedPasswordOnOtherOrigin) {
+  std::vector<PasswordForm> password_forms = {
+      CreateForm(kOtherOrigin, kLeakedUsername)};
+
+  SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+  SetOnShowLeakDetectionNotificationExpectation(/*is_saved=*/false,
+                                                /*is_reused=*/true);
+  InitiateGetCredentialLeakType();
+}
+
+// Credentials are not saved but the password is reused with a different
+// username on a different origin.
+TEST_F(LeakDetectionDelegateHelperTest, ReusedPassword) {
+  std::vector<PasswordForm> password_forms = {
+      CreateForm(kOtherOrigin, kOtherUsername)};
+
+  SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+  SetOnShowLeakDetectionNotificationExpectation(/*is_saved=*/false,
+                                                /*is_reused=*/true);
+  InitiateGetCredentialLeakType();
+}
+
+}  // namespace password_manager
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 229dd158..99d12e7 100644
--- a/components/password_manager/core/browser/new_password_form_manager.cc
+++ b/components/password_manager/core/browser/new_password_form_manager.cc
@@ -638,8 +638,7 @@
 
 bool NewPasswordFormManager::ProvisionallySave(
     const FormData& submitted_form,
-    const PasswordManagerDriver* driver,
-    bool is_gaia_with_skip_save_password_form) {
+    const PasswordManagerDriver* driver) {
   DCHECK(DoesManage(submitted_form, driver));
 
   std::unique_ptr<PasswordForm> parsed_submitted_form =
@@ -654,8 +653,6 @@
     return is_submitted_;
 
   parsed_submitted_form_ = std::move(parsed_submitted_form);
-  parsed_submitted_form_->form_data.is_gaia_with_skip_save_password_form =
-      is_gaia_with_skip_save_password_form;
   submitted_form_ = submitted_form;
   is_submitted_ = true;
   CalculateFillingAssistanceMetric(submitted_form);
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 d65d30d..b7ff3bf 100644
--- a/components/password_manager/core/browser/new_password_form_manager.h
+++ b/components/password_manager/core/browser/new_password_form_manager.h
@@ -87,11 +87,8 @@
   // |submitted_form| and |driver|) then saves |submitted_form| to
   // |submitted_form_| field, sets |is_submitted| = true and returns true.
   // Otherwise returns false.
-  // |is_gaia_with_skip_save_password_form| is true iff this is Gaia form which
-  // should be skipped on saving.
   bool ProvisionallySave(const autofill::FormData& submitted_form,
-                         const PasswordManagerDriver* driver,
-                         bool is_gaia_with_skip_save_password_form);
+                         const PasswordManagerDriver* driver);
 
   // If |submitted_form| is managed by *this then saves |submitted_form| to
   // |submitted_form_| field, sets |is_submitted| = true and returns true.
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 30b4ad3..3990f4fa 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
@@ -556,8 +556,7 @@
 
 TEST_F(NewPasswordFormManagerTest, SetSubmitted) {
   EXPECT_FALSE(form_manager_->is_submitted());
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form_, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form_, &driver_));
   EXPECT_TRUE(form_manager_->is_submitted());
 
   FormData another_form = submitted_form_;
@@ -565,14 +564,13 @@
 #if !defined(OS_IOS)
   // |another_form| is managed because the same |unique_renderer_id| as
   // |observed_form_|.
-  EXPECT_TRUE(form_manager_->ProvisionallySave(another_form, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(another_form, &driver_));
   EXPECT_TRUE(form_manager_->is_submitted());
 #endif
 }
 
 TEST_F(NewPasswordFormManagerTest, SetSubmittedMultipleTimes) {
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form_, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form_, &driver_));
   EXPECT_TRUE(form_manager_->is_submitted());
 
   // Make the submitted form to be invalid password form.
@@ -580,8 +578,7 @@
 
   // Expect that |form_manager_| is still in submitted state because the first
   // time the submited form was valid.
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form_, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form_, &driver_));
   EXPECT_TRUE(form_manager_->is_submitted());
   EXPECT_TRUE(form_manager_->GetSubmittedForm());
 }
@@ -656,8 +653,7 @@
   TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
   fetcher_->NotifyFetchCompleted();
 
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form_, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form_, &driver_));
   CheckPendingCredentials(parsed_submitted_form_,
                           form_manager_->GetPendingCredentials());
   EXPECT_EQ(UserAction::kOverrideUsernameAndPassword,
@@ -670,8 +666,7 @@
   TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
   SetNonFederatedAndNotifyFetchCompleted({&saved_match_});
 
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form_, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form_, &driver_));
   CheckPendingCredentials(parsed_submitted_form_,
                           form_manager_->GetPendingCredentials());
   EXPECT_EQ(UserAction::kOverrideUsernameAndPassword,
@@ -695,8 +690,7 @@
   for (bool is_incognito : {false, true}) {
     EXPECT_CALL(client_, IsIncognito).WillOnce(Return(is_incognito));
     form_manager_->Fill();
-    EXPECT_TRUE(
-        form_manager_->ProvisionallySave(submitted_form_, &driver_, false));
+    EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form_, &driver_));
     CheckPendingCredentials(/* expected */ saved_match_,
                             form_manager_->GetPendingCredentials());
     EXPECT_EQ(is_incognito ? UserAction::kChoose : UserAction::kNone,
@@ -721,8 +715,7 @@
   submitted_form_.fields[kPasswordFieldIndex].value =
       saved_match_.password_value;
 
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form_, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form_, &driver_));
   CheckPendingCredentials(expected, form_manager_->GetPendingCredentials());
   EXPECT_EQ(UserAction::kChoosePslMatch,
             form_manager_->GetMetricsRecorder()->GetUserAction());
@@ -740,8 +733,7 @@
   submitted_form_.fields[kUsernameFieldIndex].value =
       saved_match_.username_value;
   submitted_form_.fields[kPasswordFieldIndex].value = expected.password_value;
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form_, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form_, &driver_));
   CheckPendingCredentials(expected, form_manager_->GetPendingCredentials());
   EXPECT_EQ(UserAction::kOverridePassword,
             form_manager_->GetMetricsRecorder()->GetUserAction());
@@ -760,8 +752,7 @@
   PasswordForm expected = saved_match_;
   expected.password_value = ASCIIToUTF16("verystrongpassword");
 
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_));
   CheckPendingCredentials(expected, form_manager_->GetPendingCredentials());
   EXPECT_EQ(UserAction::kOverridePassword,
             form_manager_->GetMetricsRecorder()->GetUserAction());
@@ -783,8 +774,7 @@
   PasswordForm expected = saved_match_;
   expected.password_value = ASCIIToUTF16("verystrongpassword");
 
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_));
   CheckPendingCredentials(expected, form_manager_->GetPendingCredentials());
 }
 
@@ -803,8 +793,7 @@
 
   form_manager_->ProcessServerPredictions(predictions);
 
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(anonymous_signup, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(anonymous_signup, &driver_));
   EXPECT_EQ(ASCIIToUTF16("a password"),
             form_manager_->GetPendingCredentials().password_value);
 }
@@ -824,7 +813,7 @@
   submitted_form.fields[kPasswordFieldIndex].value = ASCIIToUTF16("password");
 
   // Expect no crash.
-  form_manager_->ProvisionallySave(submitted_form, &driver_, false);
+  form_manager_->ProvisionallySave(submitted_form, &driver_);
 }
 
 TEST_F(NewPasswordFormManagerTest, IsEqualToSubmittedForm) {
@@ -840,8 +829,7 @@
   // No submitted form yet.
   EXPECT_FALSE(form_manager_->IsEqualToSubmittedForm(submitted_form));
 
-  ASSERT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form, &driver_, false));
+  ASSERT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_));
 
   observed_form_.unique_renderer_id += 10;
   observed_form_.fields.clear();
@@ -865,8 +853,7 @@
   submitted_form.fields[kUsernameFieldIndex].value = new_username;
   submitted_form.fields[kPasswordFieldIndex].value = new_password;
 
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_));
   EXPECT_TRUE(form_manager_->IsNewLogin());
 
   MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get());
@@ -915,8 +902,7 @@
   submitted_form.fields[kPasswordFieldIndex].value =
       psl_saved_match_.password_value;
 
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_));
   EXPECT_TRUE(form_manager_->IsNewLogin());
   EXPECT_TRUE(form_manager_->IsPendingCredentialsPublicSuffixMatch());
 
@@ -952,8 +938,7 @@
   submitted_form.fields[kUsernameFieldIndex].value = username;
   submitted_form.fields[kPasswordFieldIndex].value = new_password;
 
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_));
   EXPECT_FALSE(form_manager_->IsNewLogin());
   EXPECT_TRUE(form_manager_->IsPasswordUpdate());
 
@@ -988,8 +973,7 @@
   base::string16 new_password = saved_match_.password_value + ASCIIToUTF16("1");
   submitted_form.fields[1].value = new_password;
 
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_));
   EXPECT_FALSE(form_manager_->IsNewLogin());
   EXPECT_TRUE(form_manager_->IsPasswordUpdate());
 
@@ -1025,8 +1009,7 @@
     auto new_password = saved_match_.password_value + ASCIIToUTF16("1");
     submitted_form.fields[1].value = new_password;
 
-    EXPECT_TRUE(
-        form_manager_->ProvisionallySave(submitted_form, &driver_, false));
+    EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_));
 
     std::map<base::string16, autofill::ServerFieldType> expected_types;
     expected_types[ASCIIToUTF16("password")] = autofill::PASSWORD;
@@ -1056,7 +1039,7 @@
   TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
   fetcher_->NotifyFetchCompleted();
 
-  form_manager_->ProvisionallySave(submitted_form_, &driver_, false);
+  form_manager_->ProvisionallySave(submitted_form_, &driver_);
 
   base::string16 new_username =
       parsed_submitted_form_.username_value + ASCIIToUTF16("1");
@@ -1079,7 +1062,7 @@
       ASCIIToUTF16("automatically_chosen_username");
   submitted_form_.fields[0].value = user_chosen_username;
   submitted_form_.fields[1].value = automatically_chosen_username;
-  form_manager_->ProvisionallySave(submitted_form_, &driver_, false);
+  form_manager_->ProvisionallySave(submitted_form_, &driver_);
 
   EXPECT_EQ(automatically_chosen_username,
             form_manager_->GetPendingCredentials().username_value);
@@ -1108,7 +1091,7 @@
   TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
   SetNonFederatedAndNotifyFetchCompleted({&saved_match_});
 
-  form_manager_->ProvisionallySave(submitted_form_, &driver_, false);
+  form_manager_->ProvisionallySave(submitted_form_, &driver_);
 
   base::string16 new_username = saved_match_.username_value;
   base::string16 expected_password = parsed_submitted_form_.password_value;
@@ -1126,7 +1109,7 @@
   TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
   fetcher_->NotifyFetchCompleted();
 
-  form_manager_->ProvisionallySave(submitted_form_, &driver_, false);
+  form_manager_->ProvisionallySave(submitted_form_, &driver_);
 
   base::string16 new_password =
       parsed_submitted_form_.password_value + ASCIIToUTF16("1");
@@ -1154,7 +1137,7 @@
   // Emulate submitting form with known username and different password.
   submitted_form_.fields[kUsernameFieldIndex].value =
       saved_match_.username_value;
-  form_manager_->ProvisionallySave(submitted_form_, &driver_, false);
+  form_manager_->ProvisionallySave(submitted_form_, &driver_);
 
   // The user changes password to already saved one.
   base::string16 password = saved_match_.password_value;
@@ -1175,7 +1158,7 @@
   base::string16 pin = ASCIIToUTF16("pin");
   form.fields[0].value = password;
   form.fields[1].value = pin;
-  form_manager_->ProvisionallySave(form, &driver_, false);
+  form_manager_->ProvisionallySave(form, &driver_);
 
   // Check that a second password field is chosen for saving.
   EXPECT_EQ(pin, form_manager_->GetPendingCredentials().password_value);
@@ -1232,8 +1215,7 @@
   fetcher_->NotifyFetchCompleted();
 
   // Provisionally save in order to create pending credentials.
-  ASSERT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form_, &driver_, false));
+  ASSERT_TRUE(form_manager_->ProvisionallySave(submitted_form_, &driver_));
 
   std::unique_ptr<NewPasswordFormManager> cloned_manager =
       form_manager_->Clone();
@@ -1328,8 +1310,7 @@
   ukm::TestAutoSetUkmRecorder test_ukm_recorder;
   SetNonFederatedAndNotifyFetchCompleted({&saved_match_});
 
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form_, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form_, &driver_));
 
   // Destroy the form manager to destroy the UKM recorder it owns. The recorder
   // only records metrics in its destructor.
@@ -1353,8 +1334,7 @@
 
   FormData malformed_form = submitted_form_;
   malformed_form.fields.clear();
-  EXPECT_FALSE(
-      form_manager_->ProvisionallySave(malformed_form, &driver_, false));
+  EXPECT_FALSE(form_manager_->ProvisionallySave(malformed_form, &driver_));
 
   // Destroy the form manager to destroy the UKM recorder it owns. The recorder
   // only records metrics in its destructor.
@@ -1499,8 +1479,7 @@
       .WillOnce(SaveArg<0>(&saved_form));
   EXPECT_CALL(client_, UpdateFormManagers());
 
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form_, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form_, &driver_));
   form_manager_->Save();
 
   EXPECT_EQ(submitted_form_.fields[kUsernameFieldIndex].value,
@@ -1733,8 +1712,7 @@
   submitted_form.fields[kUsernameFieldIndex].value = username;
   submitted_form.fields[kPasswordFieldIndex].value = new_password;
 
-  EXPECT_TRUE(
-      form_manager_->ProvisionallySave(submitted_form, &driver_, false));
+  EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_));
 
   MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get());
   PasswordForm updated_form;
@@ -1775,7 +1753,7 @@
 
   base::HistogramTester histogram_tester;
   //  Simulate successful submission.
-  form_manager_->ProvisionallySave(submitted_form_, &driver_, false);
+  form_manager_->ProvisionallySave(submitted_form_, &driver_);
   form_manager_->GetMetricsRecorder()->LogSubmitPassed();
 
   form_manager_.reset();
@@ -1792,8 +1770,7 @@
     CreateFormManager(observed_form_);
     fetcher_->NotifyFetchCompleted();
 
-    EXPECT_TRUE(
-        form_manager_->ProvisionallySave(submitted_form_, &driver_, false));
+    EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form_, &driver_));
 
     if (password_revealed)
       form_manager_->OnPasswordsRevealed();
@@ -1820,8 +1797,7 @@
       form_manager_->SetGenerationPopupWasShown(
           true /*generation_popup_was_shown*/, false /*is_manual_generation*/);
     }
-    EXPECT_TRUE(
-        form_manager_->ProvisionallySave(submitted_form_, &driver_, false));
+    EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form_, &driver_));
 
     EXPECT_CALL(
         mock_autofill_download_manager_,
@@ -1846,8 +1822,7 @@
       form_manager_->SetGenerationPopupWasShown(
           true /*generation_popup_was_shown*/, false /*is_manual_generation*/);
     }
-    EXPECT_TRUE(
-        form_manager_->ProvisionallySave(submitted_form_, &driver_, false));
+    EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form_, &driver_));
 
     EXPECT_CALL(
         mock_autofill_download_manager_,
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index 08d17d4..06ddbd1 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -962,9 +962,7 @@
     return nullptr;
   }
 
-  if (!matched_manager->ProvisionallySave(
-          submitted_form, driver,
-          submitted_form.is_gaia_with_skip_save_password_form))
+  if (!matched_manager->ProvisionallySave(submitted_form, driver))
     return nullptr;
 
   // Set all other form managers to no submission state.
diff --git a/components/password_manager/core/browser/password_manager_constants.cc b/components/password_manager/core/browser/password_manager_constants.cc
index 5729162d..cf38361e 100644
--- a/components/password_manager/core/browser/password_manager_constants.cc
+++ b/components/password_manager/core/browser/password_manager_constants.cc
@@ -14,9 +14,6 @@
 const char kPasswordManagerAccountDashboardURL[] =
     "https://passwords.google.com";
 
-const char kPasswordManagerCheckupURL[] =
-    "https://passwords.google.com/checkup/start";
-
 const char kPasswordManagerHelpCenterSmartLock[] =
     "https://support.google.com/accounts?p=smart_lock_chrome";
 
diff --git a/components/password_manager/core/browser/password_manager_constants.h b/components/password_manager/core/browser/password_manager_constants.h
index 304e4146..35437804 100644
--- a/components/password_manager/core/browser/password_manager_constants.h
+++ b/components/password_manager/core/browser/password_manager_constants.h
@@ -15,9 +15,6 @@
 // URL to the password manager account dashboard.
 extern const char kPasswordManagerAccountDashboardURL[];
 
-// URL to the password manager checkup page.
-extern const char kPasswordManagerCheckupURL[];
-
 // URL to the help center article about Smart Lock;
 // TODO(crbug.com/862269): remove when "Smart Lock" is completely gone.
 extern const char kPasswordManagerHelpCenterSmartLock[];
diff --git a/components/password_manager/core/browser/store_metrics_reporter.cc b/components/password_manager/core/browser/store_metrics_reporter.cc
index a967212..4464ab3 100644
--- a/components/password_manager/core/browser/store_metrics_reporter.cc
+++ b/components/password_manager/core/browser/store_metrics_reporter.cc
@@ -4,23 +4,21 @@
 
 #include "components/password_manager/core/browser/store_metrics_reporter.h"
 
-#include "base/metrics/histogram_macros.h"
-#include "components/password_manager/core/browser/password_bubble_experiment.h"
+#include "base/metrics/histogram_functions.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
 #include "components/password_manager/core/browser/password_store.h"
 #include "components/password_manager/core/browser/password_sync_util.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
 
 namespace password_manager {
 
 StoreMetricsReporter::StoreMetricsReporter(
-    bool password_manager_enabled,
     PasswordManagerClient* client,
     const syncer::SyncService* sync_service,
     const signin::IdentityManager* identity_manager,
     PrefService* prefs) {
-  password_manager::PasswordStore* store = client->GetPasswordStore();
   // May be null in tests.
-  if (store) {
+  if (PasswordStore* store = client->GetPasswordStore()) {
     store->ReportMetrics(
         password_manager::sync_util::GetSyncUsernameIfSyncingPasswords(
             sync_service, identity_manager),
@@ -28,7 +26,13 @@
             password_manager::SYNCING_WITH_CUSTOM_PASSPHRASE,
         client->IsUnderAdvancedProtection());
   }
-  UMA_HISTOGRAM_BOOLEAN("PasswordManager.Enabled", password_manager_enabled);
+  base::UmaHistogramBoolean(
+      "PasswordManager.Enabled",
+      prefs->GetBoolean(password_manager::prefs::kCredentialsEnableService));
+  base::UmaHistogramBoolean(
+      "PasswordManager.LeakDetection.Enabled",
+      prefs->GetBoolean(
+          password_manager::prefs::kPasswordLeakDetectionEnabled));
 }
 
 StoreMetricsReporter::~StoreMetricsReporter() = default;
diff --git a/components/password_manager/core/browser/store_metrics_reporter.h b/components/password_manager/core/browser/store_metrics_reporter.h
index 2d61d82..21d0ed2 100644
--- a/components/password_manager/core/browser/store_metrics_reporter.h
+++ b/components/password_manager/core/browser/store_metrics_reporter.h
@@ -33,10 +33,10 @@
   // Reports various metrics based on whether password manager is enabled. Uses
   // |client| to obtain the password store and password syncing state. Uses
   // |sync_service| and |identity_manager| to obtain the sync username to report
-  // about its presence among saved credentials. Uses the |prefs| to obtain the
-  // state of the first-run-experience bubble.
-  StoreMetricsReporter(bool password_manager_enabled,
-                       PasswordManagerClient* client,
+  // about its presence among saved credentials. Uses the |prefs| to obtain
+  // information wither the password manager and the leak detection feature is
+  // enabled.
+  StoreMetricsReporter(PasswordManagerClient* client,
                        const syncer::SyncService* sync_service,
                        const signin::IdentityManager* identity_manager,
                        PrefService* prefs);
diff --git a/components/password_manager/core/browser/store_metrics_reporter_unittest.cc b/components/password_manager/core/browser/store_metrics_reporter_unittest.cc
index 9083651..3b9929b 100644
--- a/components/password_manager/core/browser/store_metrics_reporter_unittest.cc
+++ b/components/password_manager/core/browser/store_metrics_reporter_unittest.cc
@@ -16,6 +16,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::testing::Bool;
 using ::testing::Return;
 
 namespace password_manager {
@@ -28,12 +29,21 @@
   MOCK_CONST_METHOD0(IsUnderAdvancedProtection, bool());
 };
 
-class StoreMetricsReporterTest : public SyncUsernameTestBase {
+// The test fixture defines two tests, one that doesn't require a password store
+// and one that does. Each of these tests depend on two boolean parameters,
+// which are declared here. Each test then assigns the desired semantics to
+// them.
+class StoreMetricsReporterTest
+    : public SyncUsernameTestBase,
+      public ::testing::WithParamInterface<std::tuple<bool, bool>> {
  public:
   StoreMetricsReporterTest() {
+    prefs_.registry()->RegisterBooleanPref(prefs::kCredentialsEnableService,
+                                           false);
+    prefs_.registry()->RegisterBooleanPref(prefs::kPasswordLeakDetectionEnabled,
+                                           false);
     prefs_.registry()->RegisterBooleanPref(
-        password_manager::prefs::kWasAutoSignInFirstRunExperienceShown, false,
-        PrefRegistry::NO_REGISTRATION_FLAGS);
+        password_manager::prefs::kWasAutoSignInFirstRunExperienceShown, false);
   }
 
   ~StoreMetricsReporterTest() override = default;
@@ -45,77 +55,52 @@
 };
 
 // Test that store-independent metrics are reported correctly.
-TEST_F(StoreMetricsReporterTest, StoreIndependentMetrics) {
-  for (const bool password_manager_enabled : {true, false}) {
-    for (const bool first_run_ui_shown : {true, false}) {
-      SCOPED_TRACE(testing::Message()
-                   << "password_manager_enabled=" << password_manager_enabled
-                   << ", first_run_ui_shown=" << first_run_ui_shown);
+TEST_P(StoreMetricsReporterTest, StoreIndependentMetrics) {
+  const bool password_manager_enabled = std::get<0>(GetParam());
+  const bool leak_detection_enabled = std::get<1>(GetParam());
 
-      prefs_.SetBoolean(
-          password_manager::prefs::kWasAutoSignInFirstRunExperienceShown,
-          first_run_ui_shown);
-      base::HistogramTester histogram_tester;
-      EXPECT_CALL(client_, GetPasswordStore()).WillOnce(Return(nullptr));
-      StoreMetricsReporter reporter(password_manager_enabled, &client_,
-                                    sync_service(), identity_manager(),
-                                    &prefs_);
+  prefs_.SetBoolean(password_manager::prefs::kCredentialsEnableService,
+                    password_manager_enabled);
+  prefs_.SetBoolean(password_manager::prefs::kPasswordLeakDetectionEnabled,
+                    leak_detection_enabled);
+  base::HistogramTester histogram_tester;
+  EXPECT_CALL(client_, GetPasswordStore()).WillOnce(Return(nullptr));
+  StoreMetricsReporter reporter(&client_, sync_service(), identity_manager(),
+                                &prefs_);
 
-      histogram_tester.ExpectBucketCount("PasswordManager.Enabled",
-                                         password_manager_enabled, 1);
-    }
-  }
+  histogram_tester.ExpectUniqueSample("PasswordManager.Enabled",
+                                      password_manager_enabled, 1);
+  histogram_tester.ExpectUniqueSample("PasswordManager.LeakDetection.Enabled",
+                                      leak_detection_enabled, 1);
 }
 
 // Test that sync username and syncing state are passed correctly to the
-// PasswordStore when not under advanced protection.
-TEST_F(StoreMetricsReporterTest, PasswordStore) {
-  for (const bool syncing_with_passphrase : {true, false}) {
-    SCOPED_TRACE(testing::Message()
-                 << "syncing_with_passphrase=" << syncing_with_passphrase);
+// PasswordStore.
+TEST_P(StoreMetricsReporterTest, StoreDependentMetrics) {
+  const bool syncing_with_passphrase = std::get<0>(GetParam());
+  const bool is_under_advanced_protection = std::get<1>(GetParam());
 
-    auto store = base::MakeRefCounted<MockPasswordStore>();
-    const auto sync_state =
-        syncing_with_passphrase
-            ? password_manager::SYNCING_WITH_CUSTOM_PASSPHRASE
-            : password_manager::SYNCING_NORMAL_ENCRYPTION;
-    EXPECT_CALL(client_, GetPasswordSyncState()).WillOnce(Return(sync_state));
-    EXPECT_CALL(client_, GetPasswordStore()).WillOnce(Return(store.get()));
-    EXPECT_CALL(client_, IsUnderAdvancedProtection()).WillOnce(Return(false));
-    EXPECT_CALL(*store, ReportMetrics("some.user@gmail.com",
-                                      syncing_with_passphrase, false));
-    FakeSigninAs("some.user@gmail.com");
+  auto store = base::MakeRefCounted<MockPasswordStore>();
+  const auto sync_state = syncing_with_passphrase
+                              ? password_manager::SYNCING_WITH_CUSTOM_PASSPHRASE
+                              : password_manager::SYNCING_NORMAL_ENCRYPTION;
+  EXPECT_CALL(client_, GetPasswordSyncState()).WillOnce(Return(sync_state));
+  EXPECT_CALL(client_, GetPasswordStore()).WillOnce(Return(store.get()));
+  EXPECT_CALL(client_, IsUnderAdvancedProtection())
+      .WillOnce(Return(is_under_advanced_protection));
+  EXPECT_CALL(*store,
+              ReportMetrics("some.user@gmail.com", syncing_with_passphrase,
+                            is_under_advanced_protection));
+  FakeSigninAs("some.user@gmail.com");
 
-    StoreMetricsReporter reporter(true, &client_, sync_service(),
-                                  identity_manager(), &prefs_);
-    store->ShutdownOnUIThread();
-  }
+  StoreMetricsReporter reporter(&client_, sync_service(), identity_manager(),
+                                &prefs_);
+  store->ShutdownOnUIThread();
 }
 
-// Test that sync username and syncing state are passed correctly to the
-// PasswordStore when under advanced protection.
-TEST_F(StoreMetricsReporterTest, PasswordStoreForUnderAdvancedProtection) {
-  for (const bool syncing_with_passphrase : {true, false}) {
-    SCOPED_TRACE(testing::Message()
-                 << "syncing_with_passphrase=" << syncing_with_passphrase);
-
-    auto store = base::MakeRefCounted<MockPasswordStore>();
-    const auto sync_state =
-        syncing_with_passphrase
-            ? password_manager::SYNCING_WITH_CUSTOM_PASSPHRASE
-            : password_manager::SYNCING_NORMAL_ENCRYPTION;
-    EXPECT_CALL(client_, GetPasswordSyncState()).WillOnce(Return(sync_state));
-    EXPECT_CALL(client_, GetPasswordStore()).WillOnce(Return(store.get()));
-    EXPECT_CALL(client_, IsUnderAdvancedProtection()).WillOnce(Return(true));
-    EXPECT_CALL(*store, ReportMetrics("some.user@gmail.com",
-                                      syncing_with_passphrase, true));
-    FakeSigninAs("some.user@gmail.com");
-
-    StoreMetricsReporter reporter(true, &client_, sync_service(),
-                                  identity_manager(), &prefs_);
-    store->ShutdownOnUIThread();
-  }
-}
+INSTANTIATE_TEST_SUITE_P(/*InstantiationName*/,
+                         StoreMetricsReporterTest,
+                         testing::Combine(Bool(), Bool()));
 
 }  // namespace
 }  // namespace password_manager
diff --git a/components/pdf/renderer/pdf_accessibility_tree.cc b/components/pdf/renderer/pdf_accessibility_tree.cc
index 8792632..701c45043 100644
--- a/components/pdf/renderer/pdf_accessibility_tree.cc
+++ b/components/pdf/renderer/pdf_accessibility_tree.cc
@@ -574,6 +574,8 @@
   std::string chars_utf8 = GetTextRunCharsAsUTF8(text_run, chars, char_index);
   inline_text_box_node->AddStringAttribute(ax::mojom::StringAttribute::kName,
                                            chars_utf8);
+  inline_text_box_node->AddIntAttribute(ax::mojom::IntAttribute::kTextDirection,
+                                        text_run.direction);
   inline_text_box_node->relative_bounds.bounds =
       ToGfxRectF(text_run.bounds) + page_bounds.OffsetFromOrigin();
   std::vector<int32_t> char_offsets =
diff --git a/components/policy/core/browser/android/policy_converter.cc b/components/policy/core/browser/android/policy_converter.cc
index d503d17..b07df6b 100644
--- a/components/policy/core/browser/android/policy_converter.cc
+++ b/components/policy/core/browser/android/policy_converter.cc
@@ -31,8 +31,9 @@
 PolicyConverter::PolicyConverter(const Schema* policy_schema)
     : policy_schema_(policy_schema), policy_bundle_(new PolicyBundle) {
   JNIEnv* env = base::android::AttachCurrentThread();
-  java_obj_.Reset(env, Java_PolicyConverter_create(
-                           env, reinterpret_cast<long>(this)).obj());
+  java_obj_.Reset(
+      env,
+      Java_PolicyConverter_create(env, reinterpret_cast<intptr_t>(this)).obj());
   DCHECK(!java_obj_.is_null());
 }
 
diff --git a/components/policy/core/common/cloud/cloud_policy_client.cc b/components/policy/core/common/cloud/cloud_policy_client.cc
index 56db1d4..cc10a64 100644
--- a/components/policy/core/common/cloud/cloud_policy_client.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client.cc
@@ -623,8 +623,6 @@
   for (const auto& command_result : command_results)
     *request->add_command_results() = command_result;
 
-  request->set_send_secure_commands(true);
-
   request_jobs_.push_back(service_->CreateJob(std::move(config)));
 }
 
diff --git a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
index fc21397..e5c5d2c0 100644
--- a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
@@ -245,8 +245,6 @@
         em::RemoteCommandResult_ResultType_RESULT_SUCCESS);
     command_result->set_payload(kResultPayload);
     command_result->set_timestamp(kTimestamp);
-    remote_command_request_.mutable_remote_command_request()
-        ->set_send_secure_commands(true);
 
     em::RemoteCommand* command =
         remote_command_response_.mutable_remote_command_response()
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 51a553d3..58c4377 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -5886,19 +5886,19 @@
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:65-'],
       'features': {
-        # The case where a user is already signed into an unmanaged account from the content area cannot be handled right now and hence this is not dynamic.
+        # The case where a user is already signed into an unmanaged account cannot be handled right now and hence this is not dynamic.
         'dynamic_refresh': False,
         'per_profile': True,
       },
       'example_value': False,
       'id': 406,
-      'caption': '''Allow Multiple Sign-in Within the Browser''',
+      'caption': '''Allow Sign-in To Additional Google Accounts''',
       'tags': [],
-      'desc': '''This setting allows users to switch between Google accounts within the content area of their browser window after they sign into their <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> device.
+      'desc': '''This setting allows users to switch between Google Accounts within the content area of their browser window and in Android applications, after they sign into their <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> device.
 
-      If this policy is set to false, signing in to a different account from non-Incognito browser content area will not be allowed.
+      If this policy is set to false, signing in to a different Google Account from a non-Incognito browser content area and Android applications will not be allowed.
 
-      If this policy is unset or set to true, the default behavior will be used: signing in to a different account from the browser content area will be allowed, except for child accounts where it will be blocked for non-Incognito content area.
+      If this policy is unset or set to true, the default behavior will be used: signing in to a different Google Account from the browser content area and Android applications will be allowed, except for child accounts where it will be blocked for non-Incognito content area.
 
       In case signing in to a different account shouldn't be allowed via the Incognito mode, consider blocking that mode using the IncognitoModeAvailability policy.
 
diff --git a/components/safe_browsing/password_protection/password_protection_request.cc b/components/safe_browsing/password_protection/password_protection_request.cc
index 50f01d40..15ff2a1f 100644
--- a/components/safe_browsing/password_protection/password_protection_request.cc
+++ b/components/safe_browsing/password_protection/password_protection_request.cc
@@ -49,6 +49,9 @@
 // the size of the report. UMA suggests 99.9% will have < 200 domains.
 const int kMaxReusedDomains = 200;
 
+// The maximum time to wait for DOM features to be collected, in milliseconds.
+const int kDomFeatureTimeoutMs = 3000;
+
 // Parameters chosen to ensure privacy is preserved by visual features.
 const int kMinWidthForVisualFeatures = 576;
 const int kMinHeightForVisualFeatures = 576;
@@ -265,14 +268,25 @@
   content::RenderFrameHost* rfh = web_contents_->GetMainFrame();
   password_protection_service_->GetPhishingDetector(rfh->GetRemoteInterfaces(),
                                                     &phishing_detector_);
+  dom_features_collection_complete_ = false;
   phishing_detector_->StartPhishingDetection(
       main_frame_url_,
       base::BindRepeating(&PasswordProtectionRequest::OnGetDomFeatures,
                           GetWeakPtr()));
+  base::PostDelayedTask(
+      FROM_HERE, {BrowserThread::UI},
+      base::BindOnce(&PasswordProtectionRequest::OnGetDomFeatureTimeout,
+                     GetWeakPtr()),
+      base::TimeDelta::FromMilliseconds(kDomFeatureTimeoutMs));
   dom_feature_start_time_ = base::TimeTicks::Now();
 }
 
 void PasswordProtectionRequest::OnGetDomFeatures(const std::string& verdict) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (dom_features_collection_complete_)
+    return;
+
+  dom_features_collection_complete_ = true;
   ClientPhishingRequest dom_features_request;
   if (dom_features_request.ParseFromString(verdict)) {
     for (const ClientPhishingRequest::Feature& feature :
@@ -300,6 +314,18 @@
   UMA_HISTOGRAM_TIMES("PasswordProtection.DomFeatureExtractionDuration",
                       base::TimeTicks::Now() - dom_feature_start_time_);
 
+  MaybeCollectVisualFeatures();
+}
+
+void PasswordProtectionRequest::OnGetDomFeatureTimeout() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (!dom_features_collection_complete_) {
+    dom_features_collection_complete_ = true;
+    MaybeCollectVisualFeatures();
+  }
+}
+
+void PasswordProtectionRequest::MaybeCollectVisualFeatures() {
   // Once the DOM features are collected, either collect visual features, or go
   // straight to sending the ping.
   if (trigger_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
diff --git a/components/safe_browsing/password_protection/password_protection_request.h b/components/safe_browsing/password_protection/password_protection_request.h
index b211c65..84f9378 100644
--- a/components/safe_browsing/password_protection/password_protection_request.h
+++ b/components/safe_browsing/password_protection/password_protection_request.h
@@ -174,6 +174,13 @@
   // Called when the DOM feature extraction is complete.
   void OnGetDomFeatures(const std::string& verdict);
 
+  // Called when the DOM feature extraction times out.
+  void OnGetDomFeatureTimeout();
+
+  // If appropriate, collects visual features, otherwise continues on to sending
+  // the request.
+  void MaybeCollectVisualFeatures();
+
   // WebContents of the password protection event.
   content::WebContents* web_contents_;
 
@@ -249,6 +256,10 @@
   // PasswordProtection.DomFeatureExtractionDuration.
   base::TimeTicks dom_feature_start_time_;
 
+  // Whether the DOM features collection is finished, either by timeout or by
+  // successfully gathering the features.
+  bool dom_features_collection_complete_;
+
   base::WeakPtrFactory<PasswordProtectionRequest> weakptr_factory_{this};
   DISALLOW_COPY_AND_ASSIGN(PasswordProtectionRequest);
 };
diff --git a/components/safe_browsing/password_protection/password_protection_service_unittest.cc b/components/safe_browsing/password_protection/password_protection_service_unittest.cc
index 9741d4b0..35f0b12 100644
--- a/components/safe_browsing/password_protection/password_protection_service_unittest.cc
+++ b/components/safe_browsing/password_protection/password_protection_service_unittest.cc
@@ -12,6 +12,8 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/null_task_runner.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/browser/password_reuse_detector.h"
@@ -79,7 +81,7 @@
 
 class TestPhishingDetector : public mojom::PhishingDetector {
  public:
-  TestPhishingDetector() : binding_(this) {}
+  TestPhishingDetector() : should_timeout_(false), binding_(this) {}
   ~TestPhishingDetector() override {}
 
   void Bind(mojo::ScopedMessagePipeHandle handle) {
@@ -89,13 +91,25 @@
   void StartPhishingDetection(
       const GURL& url,
       StartPhishingDetectionCallback callback) override {
+    if (should_timeout_) {
+      deferred_callbacks_.push_back(std::move(callback));
+    } else {
+      ReturnFeatures(url, std::move(callback));
+    }
+  }
+  void ReturnFeatures(const GURL& url,
+                      StartPhishingDetectionCallback callback) {
     ClientPhishingRequest verdict;
     verdict.set_is_phishing(false);
     verdict.set_client_score(0.1);
     std::move(callback).Run(verdict.SerializeAsString());
   }
 
+  void set_should_timeout(bool timeout) { should_timeout_ = timeout; }
+
  private:
+  bool should_timeout_;
+  std::vector<StartPhishingDetectionCallback> deferred_callbacks_;
   mojo::Binding<mojom::PhishingDetector> binding_;
 
   DISALLOW_COPY_AND_ASSIGN(TestPhishingDetector);
@@ -178,6 +192,10 @@
     return cache_manager_->GetStoredPhishGuardVerdictCount(trigger_type);
   }
 
+  void SetDomFeatureCollectionTimeout(bool should_timeout) {
+    test_phishing_detector_.set_should_timeout(should_timeout);
+  }
+
  private:
   PasswordProtectionRequest* latest_request_;
   base::RunLoop run_loop_;
@@ -208,7 +226,8 @@
 
 class PasswordProtectionServiceTest : public ::testing::TestWithParam<bool> {
  public:
-  PasswordProtectionServiceTest() {}
+  PasswordProtectionServiceTest()
+      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
 
   LoginReputationClientResponse CreateVerdictProto(
       LoginReputationClientResponse::VerdictType verdict,
@@ -1283,6 +1302,28 @@
                   ->has_dom_features());
 }
 
+TEST_P(PasswordProtectionServiceTest, TestDomFeaturesTimeout) {
+  password_protection_service_->SetDomFeatureCollectionTimeout(true);
+  LoginReputationClientResponse expected_response =
+      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
+                         GURL("about:blank").host());
+  test_url_loader_factory_.AddResponse(url_.spec(),
+                                       expected_response.SerializeAsString());
+  EXPECT_CALL(*password_protection_service_, GetCurrentContentAreaSize())
+      .Times(AnyNumber())
+      .WillOnce(Return(gfx::Size(1000, 1000)));
+  password_protection_service_->StartRequest(
+      GetWebContents(), GURL("about:blank"), GURL(), GURL(), kUserName,
+      PasswordType::SAVED_PASSWORD, {"example.com"},
+      LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
+  task_environment_.FastForwardUntilNoTasksRemain();
+
+  password_protection_service_->WaitForResponse();
+  ASSERT_NE(nullptr, password_protection_service_->GetLatestRequestProto());
+  EXPECT_FALSE(password_protection_service_->GetLatestRequestProto()
+                   ->has_dom_features());
+}
+
 TEST_P(PasswordProtectionServiceTest, TestRequestCancelOnTimeout) {
   content::WebContents* web_contents = GetWebContents();
   InitializeAndStartPasswordOnFocusRequest(
diff --git a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.h b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.h
index affc624..a538d24b 100644
--- a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.h
+++ b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.h
@@ -66,9 +66,9 @@
   void OnConnectionChanged(network::mojom::ConnectionType type) override;
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(CrOSOAuthDelegateTest,
+  FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceDelegateChromeOSTest,
                            BackOffIsTriggerredForTransientErrors);
-  FRIEND_TEST_ALL_PREFIXES(CrOSOAuthDelegateTest,
+  FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceDelegateChromeOSTest,
                            BackOffIsResetOnNetworkChange);
 
   // A utility class to keep track of |GoogleServiceAuthError|s for an account.
diff --git a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos_unittest.cc b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos_unittest.cc
index 08d0b615..eeb1c6a4 100644
--- a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos_unittest.cc
+++ b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos_unittest.cc
@@ -146,10 +146,10 @@
 
 }  // namespace
 
-class CrOSOAuthDelegateTest : public testing::Test {
+class ProfileOAuth2TokenServiceDelegateChromeOSTest : public testing::Test {
  public:
-  CrOSOAuthDelegateTest() {}
-  ~CrOSOAuthDelegateTest() override = default;
+  ProfileOAuth2TokenServiceDelegateChromeOSTest() {}
+  ~ProfileOAuth2TokenServiceDelegateChromeOSTest() override = default;
 
  protected:
   void SetUp() override {
@@ -223,12 +223,13 @@
   std::unique_ptr<TestSigninClient> client_;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(CrOSOAuthDelegateTest);
+  DISALLOW_COPY_AND_ASSIGN(ProfileOAuth2TokenServiceDelegateChromeOSTest);
 };
 
 // Refresh tokens should load successfully for non-regular (Signin and Lock
 // Screen) Profiles.
-TEST_F(CrOSOAuthDelegateTest, RefreshTokensAreLoadedForNonRegularProfiles) {
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+       RefreshTokensAreLoadedForNonRegularProfiles) {
   // Create an instance of Account Manager but do not
   // |AccountManager::Initialize| it. This mimics Signin and Lock Screen Profile
   // behaviour.
@@ -248,7 +249,7 @@
             delegate->load_credentials_state());
 }
 
-TEST_F(CrOSOAuthDelegateTest,
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
        RefreshTokenIsAvailableReturnsTrueForValidGaiaTokens) {
   EXPECT_EQ(LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS,
             delegate_->load_credentials_state());
@@ -264,7 +265,7 @@
       base::Contains(delegate_->GetAccounts(), account_info_.account_id));
 }
 
-TEST_F(CrOSOAuthDelegateTest,
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
        RefreshTokenIsAvailableReturnsTrueForInvalidGaiaTokens) {
   EXPECT_EQ(LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS,
             delegate_->load_credentials_state());
@@ -281,7 +282,8 @@
       base::Contains(delegate_->GetAccounts(), account_info_.account_id));
 }
 
-TEST_F(CrOSOAuthDelegateTest, ObserversAreNotifiedOnAuthErrorChange) {
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+       ObserversAreNotifiedOnAuthErrorChange) {
   TestOAuth2TokenServiceObserver observer(delegate_.get());
   auto error =
       GoogleServiceAuthError(GoogleServiceAuthError::State::SERVICE_ERROR);
@@ -292,7 +294,8 @@
   EXPECT_EQ(error, observer.last_err_);
 }
 
-TEST_F(CrOSOAuthDelegateTest, ObserversAreNotNotifiedIfErrorDidntChange) {
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+       ObserversAreNotNotifiedIfErrorDidntChange) {
   TestOAuth2TokenServiceObserver observer(delegate_.get());
   auto error =
       GoogleServiceAuthError(GoogleServiceAuthError::State::SERVICE_ERROR);
@@ -303,7 +306,8 @@
   EXPECT_EQ(1, observer.on_auth_error_changed_calls);
 }
 
-TEST_F(CrOSOAuthDelegateTest, ObserversAreNotifiedIfErrorDidChange) {
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+       ObserversAreNotifiedIfErrorDidChange) {
   TestOAuth2TokenServiceObserver observer(delegate_.get());
   delegate_->UpdateAuthError(
       account_info_.account_id,
@@ -317,7 +321,8 @@
   EXPECT_EQ(2, observer.on_auth_error_changed_calls);
 }
 
-TEST_F(CrOSOAuthDelegateTest, ObserversAreNotifiedOnCredentialsInsertion) {
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+       ObserversAreNotifiedOnCredentialsInsertion) {
   TestOAuth2TokenServiceObserver observer(delegate_.get());
   delegate_->UpdateCredentials(account_info_.account_id, kGaiaToken);
 
@@ -327,7 +332,7 @@
   EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), observer.last_err_);
 }
 
-TEST_F(CrOSOAuthDelegateTest,
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
        ObserversDoNotSeeCachedErrorsOnCredentialsUpdate) {
   TestOAuth2TokenServiceObserver observer(delegate_.get());
   auto error =
@@ -340,7 +345,8 @@
   delegate_->UpdateCredentials(account_info_.account_id, "new-token");
 }
 
-TEST_F(CrOSOAuthDelegateTest, DummyTokensArePreEmptivelyRejected) {
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+       DummyTokensArePreEmptivelyRejected) {
   TestOAuth2TokenServiceObserver observer(delegate_.get());
   delegate_->UpdateCredentials(account_info_.account_id,
                                chromeos::AccountManager::kInvalidToken);
@@ -358,7 +364,8 @@
   EXPECT_EQ(account_info_.account_id, observer.last_err_account_id_);
 }
 
-TEST_F(CrOSOAuthDelegateTest, ObserversAreNotifiedOnCredentialsUpdate) {
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+       ObserversAreNotifiedOnCredentialsUpdate) {
   TestOAuth2TokenServiceObserver observer(delegate_.get());
   delegate_->UpdateCredentials(account_info_.account_id, kGaiaToken);
 
@@ -368,7 +375,7 @@
   EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), observer.last_err_);
 }
 
-TEST_F(CrOSOAuthDelegateTest,
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
        ObserversAreNotNotifiedIfCredentialsAreNotUpdated) {
   TestOAuth2TokenServiceObserver observer(delegate_.get());
 
@@ -381,7 +388,7 @@
   EXPECT_EQ(std::string(), observer.last_err_account_id_);
 }
 
-TEST_F(CrOSOAuthDelegateTest,
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
        BatchChangeObserversAreNotifiedOnCredentialsUpdate) {
   TestOAuth2TokenServiceObserver observer(delegate_.get());
   delegate_->UpdateCredentials(account_info_.account_id, kGaiaToken);
@@ -394,7 +401,8 @@
 // If observers register themselves with |ProfileOAuth2TokenServiceDelegate|
 // before |chromeos::AccountManager| has been initialized, they should receive
 // all the accounts stored in |chromeos::AccountManager| in a single batch.
-TEST_F(CrOSOAuthDelegateTest, BatchChangeObserversAreNotifiedOncePerBatch) {
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+       BatchChangeObserversAreNotifiedOncePerBatch) {
   // Setup
   AccountInfo account1 = CreateAccountInfoTestFixture(
       "1" /* gaia_id */, "user1@example.com" /* email */);
@@ -447,7 +455,8 @@
   EXPECT_TRUE(base::Contains(first_batch, account2.account_id));
 }
 
-TEST_F(CrOSOAuthDelegateTest, GetAccountsShouldNotReturnAdAccounts) {
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+       GetAccountsShouldNotReturnAdAccounts) {
   EXPECT_TRUE(delegate_->GetAccounts().empty());
 
   // Insert an Active Directory account into chromeos::AccountManager.
@@ -459,7 +468,8 @@
   EXPECT_TRUE(delegate_->GetAccounts().empty());
 }
 
-TEST_F(CrOSOAuthDelegateTest, GetAccountsReturnsGaiaAccounts) {
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+       GetAccountsReturnsGaiaAccounts) {
   EXPECT_TRUE(delegate_->GetAccounts().empty());
 
   account_manager_.UpsertAccount(gaia_account_key_, kUserEmail, kGaiaToken);
@@ -471,7 +481,8 @@
 
 // |GetAccounts| should return all known Gaia accounts, whether or not they have
 // a "valid" refresh token stored against them.
-TEST_F(CrOSOAuthDelegateTest, GetAccountsReturnsGaiaAccountsWithInvalidTokens) {
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+       GetAccountsReturnsGaiaAccountsWithInvalidTokens) {
   EXPECT_TRUE(delegate_->GetAccounts().empty());
 
   account_manager_.UpsertAccount(gaia_account_key_, kUserEmail,
@@ -482,7 +493,7 @@
   EXPECT_EQ(account_info_.account_id, accounts[0]);
 }
 
-TEST_F(CrOSOAuthDelegateTest,
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
        RefreshTokenMustBeAvailableForAllAccountsReturnedByGetAccounts) {
   EXPECT_EQ(LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS,
             delegate_->load_credentials_state());
@@ -515,7 +526,8 @@
   }
 }
 
-TEST_F(CrOSOAuthDelegateTest, UpdateCredentialsSucceeds) {
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+       UpdateCredentialsSucceeds) {
   EXPECT_TRUE(delegate_->GetAccounts().empty());
 
   delegate_->UpdateCredentials(account_info_.account_id, kGaiaToken);
@@ -525,7 +537,8 @@
   EXPECT_EQ(account_info_.account_id, accounts[0]);
 }
 
-TEST_F(CrOSOAuthDelegateTest, ObserversAreNotifiedOnAccountRemoval) {
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+       ObserversAreNotifiedOnAccountRemoval) {
   delegate_->UpdateCredentials(account_info_.account_id, kGaiaToken);
 
   TestOAuth2TokenServiceObserver observer(delegate_.get());
@@ -537,7 +550,7 @@
   EXPECT_TRUE(observer.account_ids_.empty());
 }
 
-TEST_F(CrOSOAuthDelegateTest,
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
        SigninErrorObserversAreNotifiedOnAuthErrorChange) {
   auto error =
       GoogleServiceAuthError(GoogleServiceAuthError::State::SERVICE_ERROR);
@@ -547,7 +560,8 @@
   EXPECT_EQ(error, delegate_->GetAuthError(account_info_.account_id));
 }
 
-TEST_F(CrOSOAuthDelegateTest, TransientErrorsAreNotShown) {
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+       TransientErrorsAreNotShown) {
   auto transient_error = GoogleServiceAuthError(
       GoogleServiceAuthError::State::SERVICE_UNAVAILABLE);
   EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(),
@@ -559,7 +573,8 @@
             delegate_->GetAuthError(account_info_.account_id));
 }
 
-TEST_F(CrOSOAuthDelegateTest, BackOffIsTriggerredForTransientErrors) {
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+       BackOffIsTriggerredForTransientErrors) {
   delegate_->UpdateCredentials(account_info_.account_id, kGaiaToken);
   auto transient_error = GoogleServiceAuthError(
       GoogleServiceAuthError::State::SERVICE_UNAVAILABLE);
@@ -596,7 +611,8 @@
   EXPECT_EQ(1, access_token_consumer.num_access_token_fetch_failure_);
 }
 
-TEST_F(CrOSOAuthDelegateTest, BackOffIsResetOnNetworkChange) {
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+       BackOffIsResetOnNetworkChange) {
   delegate_->UpdateCredentials(account_info_.account_id, kGaiaToken);
   auto transient_error = GoogleServiceAuthError(
       GoogleServiceAuthError::State::SERVICE_UNAVAILABLE);
diff --git a/components/subresource_filter/tools/BUILD.gn b/components/subresource_filter/tools/BUILD.gn
index 2b0f9e1..6228e13 100644
--- a/components/subresource_filter/tools/BUILD.gn
+++ b/components/subresource_filter/tools/BUILD.gn
@@ -98,6 +98,7 @@
 
     outputs = [
       "$target_gen_dir/GeneratedRulesetData",
+      "$target_gen_dir/default_local_state.json",
     ]
 
     inputs = [
@@ -109,10 +110,8 @@
 
     args = [
       rebase_path(inputs[0], root_build_dir),
-      rebase_path("$target_gen_dir/GeneratedRulesetData", root_build_dir),
-      "--version_output=" +
-          rebase_path("$target_gen_dir/default_local_state.json",
-                      root_build_dir),
+      rebase_path(outputs[0], root_build_dir),
+      "--version_output=" + rebase_path(outputs[1], root_build_dir),
       "--content_version=1000",
     ]
   }
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index ce7ceb3..a3fc1b3 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -274,6 +274,10 @@
     public_deps += [ "//ui/ozone" ]
   }
 
+  if (is_chromecast) {
+    defines += [ "ALWAYS_ENABLE_BLENDING_FOR_PRIMARY" ]
+  }
+
   if (is_win) {
     sources += [
       "display_embedder/output_device_backing.cc",
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index 324ed866..57afe81 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -114,6 +114,9 @@
 DirectRenderer::~DirectRenderer() = default;
 
 void DirectRenderer::Initialize() {
+  // Create an overlay validator based on the platform and set it on the newly
+  // created processor. This would initialize the strategies on the validator as
+  // well.
   // Create overlay validator based on the platform and set it on the newly
   // created processor. This would initialize the strategies on the validator as
   // well.
@@ -373,19 +376,16 @@
   // TODO(weiliangc): Remove once reflector code is removed.
   overlay_processor_->SetSoftwareMirrorMode(
       output_surface_->IsSoftwareMirrorMode());
-  // Create the overlay candidate for the output surface, and mark it as
-  // always handled.
+
+  // Before ProcessForOverlay calls into the hardware to ask about whether the
+  // overlay setup can be handled, we need to set up the primary plane.
+  OverlayProcessor::OutputSurfaceOverlayPlane* primary_plane = nullptr;
   if (output_surface_->IsDisplayedAsOverlayPlane()) {
-    OverlayCandidate output_surface_plane;
-    output_surface_plane.display_rect =
-        gfx::RectF(device_viewport_size.width(), device_viewport_size.height());
-    output_surface_plane.resource_size_in_pixels = device_viewport_size;
-    output_surface_plane.format = output_surface_->GetOverlayBufferFormat();
-    output_surface_plane.color_space = reshape_device_color_space_;
-    output_surface_plane.use_output_surface_for_resource = true;
-    output_surface_plane.overlay_handled = true;
-    output_surface_plane.is_opaque = true;
-    current_frame()->overlay_list.push_back(output_surface_plane);
+    current_frame()->output_surface_plane =
+        overlay_processor_->ProcessOutputSurfaceAsOverlay(
+            device_viewport_size, output_surface_->GetOverlayBufferFormat(),
+            reshape_device_color_space_);
+    primary_plane = &(current_frame()->output_surface_plane.value());
   }
 
   // Attempt to replace some or all of the quads of the root render pass with
@@ -393,12 +393,15 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_, render_passes_in_draw_order,
       output_surface_->color_matrix(), render_pass_filters_,
-      render_pass_backdrop_filters_, &current_frame()->overlay_list,
-      &current_frame()->ca_layer_overlay_list,
+      render_pass_backdrop_filters_, primary_plane,
+      &current_frame()->overlay_list, &current_frame()->ca_layer_overlay_list,
       &current_frame()->dc_layer_overlay_list,
       &current_frame()->root_damage_rect,
       &current_frame()->root_content_bounds);
 
+  overlay_processor_->AdjustOutputSurfaceOverlay(
+      &(current_frame()->output_surface_plane));
+
   bool was_using_dc_layers = using_dc_layers_;
   if (!current_frame()->dc_layer_overlay_list.empty()) {
     DCHECK(supports_dc_layers_);
@@ -445,12 +448,9 @@
   // through other means on the service side.
   // TODO(afrantzis): Consider using per-overlay fences instead of the one
   // associated with the output surface when possible.
-  if (!current_frame()->overlay_list.empty()) {
-    for (auto& overlay : current_frame()->overlay_list) {
-      if (overlay.use_output_surface_for_resource)
-        overlay.gpu_fence_id = output_surface_->UpdateGpuFence();
-    }
-  }
+  if (current_frame()->output_surface_plane)
+    current_frame()->output_surface_plane->gpu_fence_id =
+        output_surface_->UpdateGpuFence();
 
   FinishDrawingFrame();
   render_passes_in_draw_order->clear();
diff --git a/components/viz/service/display/direct_renderer.h b/components/viz/service/display/direct_renderer.h
index 173515a..4454da5 100644
--- a/components/viz/service/display/direct_renderer.h
+++ b/components/viz/service/display/direct_renderer.h
@@ -93,6 +93,11 @@
     OverlayCandidateList overlay_list;
     CALayerOverlayList ca_layer_overlay_list;
     DCLayerOverlayList dc_layer_overlay_list;
+    // When we have a buffer queue, the output surface could be treated as an
+    // overlay plane, and the struct to store that information is in
+    // |output_surface_plane|.
+    base::Optional<OverlayProcessor::OutputSurfaceOverlayPlane>
+        output_surface_plane;
   };
 
   void SetCurrentFrameForTesting(const DrawingFrame& frame);
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc
index 841e83f..8c67161c 100644
--- a/components/viz/service/display/gl_renderer.cc
+++ b/components/viz/service/display/gl_renderer.cc
@@ -2712,6 +2712,10 @@
   gl_->Disable(GL_BLEND);
   blend_shadow_ = false;
 
+  // Schedule output surface as overlay first to preserve existing ordering
+  // semantics during overlay refactoring.
+  ScheduleOutputSurfaceAsOverlay();
+
   ScheduleCALayers();
   ScheduleDCLayers();
   ScheduleOverlays();
@@ -3464,16 +3468,10 @@
 
   OverlayCandidateList& overlays = current_frame()->overlay_list;
   for (const auto& overlay_candidate : overlays) {
-    unsigned texture_id = 0;
-    if (overlay_candidate.use_output_surface_for_resource) {
-      texture_id = output_surface_->GetOverlayTextureId();
-      DCHECK(texture_id || IsContextLost());
-    } else {
-      pending_overlay_resources_.push_back(
-          std::make_unique<DisplayResourceProvider::ScopedReadLockGL>(
-              resource_provider_, overlay_candidate.resource_id));
-      texture_id = pending_overlay_resources_.back()->texture_id();
-    }
+    pending_overlay_resources_.push_back(
+        std::make_unique<DisplayResourceProvider::ScopedReadLockGL>(
+            resource_provider_, overlay_candidate.resource_id));
+    unsigned texture_id = pending_overlay_resources_.back()->texture_id();
 
     context_support_->ScheduleOverlayPlane(
         overlay_candidate.plane_z_order, overlay_candidate.transform,
@@ -3483,6 +3481,25 @@
   }
 }
 
+void GLRenderer::ScheduleOutputSurfaceAsOverlay() {
+  if (!current_frame()->output_surface_plane)
+    return;
+
+  // Initialize correct values to use an output surface as overlay candidate.
+  auto& overlay_candidate = *(current_frame()->output_surface_plane);
+  unsigned texture_id = output_surface_->GetOverlayTextureId();
+  DCHECK(texture_id || IsContextLost());
+  // Output surface is also z-order 0.
+  int plane_z_order = 0;
+  // Output surface always uses the full texture.
+  gfx::RectF uv_rect(0.f, 0.f, 1.f, 1.f);
+
+  context_support_->ScheduleOverlayPlane(
+      plane_z_order, overlay_candidate.transform, texture_id,
+      ToNearestRect(overlay_candidate.display_rect), uv_rect,
+      overlay_candidate.enable_blending, overlay_candidate.gpu_fence_id);
+}
+
 // This function draws the RenderPassDrawQuad into a temporary
 // texture/framebuffer, and then copies the result into an IOSurface. The
 // inefficient (but simple) way to do this would be to:
diff --git a/components/viz/service/display/gl_renderer.h b/components/viz/service/display/gl_renderer.h
index 4a43066e..4e79bab 100644
--- a/components/viz/service/display/gl_renderer.h
+++ b/components/viz/service/display/gl_renderer.h
@@ -293,6 +293,13 @@
   void ReinitializeGLState();
   void RestoreGLState();
 
+  // TODO(weiliangc): Once the overlay processor could schedule overlays, remove
+  // these functions.
+  // Sends over output surface information as it is a overlay plane. This is
+  // used for BufferQueue. For non-BufferQueue cases, this function will do
+  // nothing.
+  void ScheduleOutputSurfaceAsOverlay();
+  // Schedule overlays sends overlay candidate to the GPU.
   void ScheduleCALayers();
   void ScheduleDCLayers();
   void ScheduleOverlays();
diff --git a/components/viz/service/display/gl_renderer_unittest.cc b/components/viz/service/display/gl_renderer_unittest.cc
index 8bfc863..ca6d7b9 100644
--- a/components/viz/service/display/gl_renderer_unittest.cc
+++ b/components/viz/service/display/gl_renderer_unittest.cc
@@ -2179,14 +2179,16 @@
     Strategy() = default;
     ~Strategy() override = default;
 
-    MOCK_METHOD6(Attempt,
-                 bool(const SkMatrix44& output_color_matrix,
-                      const OverlayProcessor::FilterOperationsMap&
-                          render_pass_backdrop_filters,
-                      DisplayResourceProvider* resource_provider,
-                      RenderPassList* render_pass_list,
-                      OverlayCandidateList* candidates,
-                      std::vector<gfx::Rect>* content_bounds));
+    MOCK_METHOD7(
+        Attempt,
+        bool(const SkMatrix44& output_color_matrix,
+             const OverlayProcessor::FilterOperationsMap&
+                 render_pass_backdrop_filters,
+             DisplayResourceProvider* resource_provider,
+             RenderPassList* render_pass_list,
+             const OverlayProcessor::OutputSurfaceOverlayPlane* primary_surface,
+             OverlayCandidateList* candidates,
+             std::vector<gfx::Rect>* content_bounds));
   };
 
   class Validator : public OverlayCandidateValidator {
@@ -2206,7 +2208,8 @@
     // to be traditionally composited. Candidates with |overlay_handled| set to
     // true must also have their |display_rect| converted to integer
     // coordinates if necessary.
-    void CheckOverlaySupport(OverlayCandidateList* surfaces) override {}
+    void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                             OverlayCandidateList* surfaces) override {}
 
     Strategy& strategy() {
       auto* strategy = strategies_.back().get();
@@ -2317,7 +2320,7 @@
   // added a fake strategy, so checking for Attempt calls checks if there was
   // any attempt to overlay, which there shouldn't be. We can't use the quad
   // list because the render pass is cleaned up by DrawFrame.
-  EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _)).Times(0);
+  EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _, _)).Times(0);
   EXPECT_CALL(*validator, AllowCALayerOverlays()).Times(0);
   EXPECT_CALL(*validator, AllowDCLayerOverlays()).Times(0);
   DrawFrame(&renderer, viewport_size);
@@ -2344,7 +2347,7 @@
   EXPECT_CALL(*validator, AllowDCLayerOverlays())
       .Times(1)
       .WillOnce(::testing::Return(false));
-  EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _)).Times(1);
+  EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _, _)).Times(1);
   DrawFrame(&renderer, viewport_size);
 
   // If the CALayerOverlay path is taken, then the ordinary overlay path should
@@ -2364,7 +2367,7 @@
   EXPECT_CALL(*validator, AllowCALayerOverlays())
       .Times(1)
       .WillOnce(::testing::Return(true));
-  EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _)).Times(0);
+  EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _, _)).Times(0);
   DrawFrame(&renderer, viewport_size);
 
   // Transfer resources back from the parent to the child. Set no resources as
@@ -2389,7 +2392,8 @@
     bool AllowDCLayerOverlays() const override { return false; }
     bool NeedsSurfaceOccludingDamageRect() const override { return true; }
 
-    void CheckOverlaySupport(OverlayCandidateList* surfaces) override {
+    void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                             OverlayCandidateList* surfaces) override {
       if (!multiple_candidates_)
         ASSERT_EQ(1U, surfaces->size());
       OverlayCandidate& candidate = surfaces->back();
@@ -2818,7 +2822,8 @@
   bool AllowCALayerOverlays() const override { return false; }
   bool AllowDCLayerOverlays() const override { return true; }
   bool NeedsSurfaceOccludingDamageRect() const override { return true; }
-  void CheckOverlaySupport(OverlayCandidateList* surfaces) override {}
+  void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                           OverlayCandidateList* surfaces) override {}
 };
 
 // Test that SetEnableDCLayersCHROMIUM is properly called when enabling
@@ -3000,6 +3005,7 @@
                      render_pass_backdrop_filters,
                  DisplayResourceProvider* resource_provider,
                  RenderPassList* render_pass_list,
+                 const PrimaryPlane* primary_plane,
                  OverlayCandidateList* candidates,
                  std::vector<gfx::Rect>* content_bounds) override {
       content_bounds->insert(content_bounds->end(), content_bounds_.begin(),
@@ -3031,7 +3037,8 @@
     // to be traditionally composited. Candidates with |overlay_handled| set to
     // true must also have their |display_rect| converted to integer
     // coordinates if necessary.
-    void CheckOverlaySupport(OverlayCandidateList* surfaces) override {}
+    void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                             OverlayCandidateList* surfaces) override {}
 
     Strategy& strategy() { return static_cast<Strategy&>(*strategies_.back()); }
 
@@ -3117,7 +3124,8 @@
   bool AllowCALayerOverlays() const override { return true; }
   bool AllowDCLayerOverlays() const override { return false; }
   bool NeedsSurfaceOccludingDamageRect() const override { return false; }
-  void CheckOverlaySupport(OverlayCandidateList* surfaces) override {}
+  void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                           OverlayCandidateList* surfaces) override {}
 };
 
 class MockCALayerGLES2Interface : public TestGLES2Interface {
@@ -4181,8 +4189,9 @@
     processor->AllowMultipleCandidates();
     renderer_->SetOverlayProcessor(processor);
 
-    test_context_support_->SetScheduleOverlayPlaneCallback(base::BindRepeating(
-        &MockOverlayScheduler::Schedule, base::Unretained(&overlay_scheduler)));
+    test_context_support_->SetScheduleOverlayPlaneCallback(
+        base::BindRepeating(&MockOverlayScheduler::Schedule,
+                            base::Unretained(&overlay_scheduler_)));
   }
 
   ~GLRendererWithGpuFenceTest() override {
@@ -4221,7 +4230,7 @@
   std::unique_ptr<ClientResourceProvider> child_resource_provider_;
   RendererSettings settings_;
   std::unique_ptr<FakeRendererGL> renderer_;
-  MockOverlayScheduler overlay_scheduler;
+  MockOverlayScheduler overlay_scheduler_;
 };
 
 TEST_F(GLRendererWithGpuFenceTest, GpuFenceIdIsUsedWithRootRenderPassOverlay) {
@@ -4231,7 +4240,7 @@
       gfx::Transform(), cc::FilterOperations());
   root_pass->has_transparent_background = false;
 
-  EXPECT_CALL(overlay_scheduler,
+  EXPECT_CALL(overlay_scheduler_,
               Schedule(0, gfx::OVERLAY_TRANSFORM_NONE, kSurfaceOverlayTextureId,
                        gfx::Rect(viewport_size), _, _, kGpuFenceId))
       .Times(1);
@@ -4268,11 +4277,11 @@
       flipped, nearest_neighbor,
       /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear);
 
-  EXPECT_CALL(overlay_scheduler,
+  EXPECT_CALL(overlay_scheduler_,
               Schedule(0, gfx::OVERLAY_TRANSFORM_NONE, kSurfaceOverlayTextureId,
                        gfx::Rect(viewport_size), _, _, kGpuFenceId))
       .Times(1);
-  EXPECT_CALL(overlay_scheduler,
+  EXPECT_CALL(overlay_scheduler_,
               Schedule(1, gfx::OVERLAY_TRANSFORM_NONE, _,
                        gfx::Rect(viewport_size), _, _, kGpuNoFenceId))
       .Times(1);
diff --git a/components/viz/service/display/overlay_candidate.cc b/components/viz/service/display/overlay_candidate.cc
index 75f797e..63157d8 100644
--- a/components/viz/service/display/overlay_candidate.cc
+++ b/components/viz/service/display/overlay_candidate.cc
@@ -87,7 +87,6 @@
       uv_rect(0.f, 0.f, 1.f, 1.f),
       is_clipped(false),
       is_opaque(false),
-      use_output_surface_for_resource(false),
       no_occluding_damage(false),
       resource_id(0),
 #if defined(OS_ANDROID)
diff --git a/components/viz/service/display/overlay_candidate.h b/components/viz/service/display/overlay_candidate.h
index 1399fe2..e2604e8f 100644
--- a/components/viz/service/display/overlay_candidate.h
+++ b/components/viz/service/display/overlay_candidate.h
@@ -86,9 +86,6 @@
   bool is_clipped;
   // If the quad doesn't require blending.
   bool is_opaque;
-  // True if the texture for this overlay should be the same one used by the
-  // output surface's main overlay.
-  bool use_output_surface_for_resource;
   // The quad's occluding damage rect is empty.
   bool no_occluding_damage;
   // Texture resource to present in an overlay.
diff --git a/components/viz/service/display/overlay_candidate_validator.cc b/components/viz/service/display/overlay_candidate_validator.cc
index 664cbf5..d21c312 100644
--- a/components/viz/service/display/overlay_candidate_validator.cc
+++ b/components/viz/service/display/overlay_candidate_validator.cc
@@ -142,14 +142,20 @@
     const OverlayProcessor::FilterOperationsMap& render_pass_backdrop_filters,
     DisplayResourceProvider* resource_provider,
     RenderPassList* render_pass_list,
+    PrimaryPlane* primary_plane,
     OverlayCandidateList* candidates,
-    std::vector<gfx::Rect>* content_bounds) const {
+    std::vector<gfx::Rect>* content_bounds) {
+  last_successful_strategy_ = nullptr;
   for (const auto& strategy : strategies_) {
     if (strategy->Attempt(output_color_matrix, render_pass_backdrop_filters,
-                          resource_provider, render_pass_list, candidates,
-                          content_bounds)) {
+                          resource_provider, render_pass_list, primary_plane,
+                          candidates, content_bounds)) {
+      // This function is used by the underlay strategy to mark the primary
+      // plane as enable_blending.
+      strategy->AdjustOutputSurfaceOverlay(primary_plane);
       UMA_HISTOGRAM_ENUMERATION("Viz.DisplayCompositor.OverlayStrategy",
                                 strategy->GetUMAEnum());
+      last_successful_strategy_ = strategy.get();
       return true;
     }
   }
@@ -163,4 +169,13 @@
   return ToEnclosedRect(candidate.display_rect);
 }
 
+bool OverlayCandidateValidator::StrategyNeedsOutputSurfacePlaneRemoved() {
+  // The full screen strategy will remove the output surface as an overlay
+  // plane.
+  if (last_successful_strategy_)
+    return last_successful_strategy_->RemoveOutputSurfaceAsOverlay();
+
+  return false;
+}
+
 }  // namespace viz
diff --git a/components/viz/service/display/overlay_candidate_validator.h b/components/viz/service/display/overlay_candidate_validator.h
index 224c8174..af389dc5 100644
--- a/components/viz/service/display/overlay_candidate_validator.h
+++ b/components/viz/service/display/overlay_candidate_validator.h
@@ -25,6 +25,10 @@
       const RendererSettings& renderer_settings);
   virtual ~OverlayCandidateValidator();
 
+  // A primary plane is generated when |OutputSurface|'s buffer is supplied by
+  // |BufferQueue|. This is considered as an overlay plane.
+  using PrimaryPlane = OverlayProcessor::OutputSurfaceOverlayPlane;
+
   // Populates a list of strategies that may work with this validator. Should be
   // called at most once.
   virtual void InitializeStrategies() {}
@@ -46,12 +50,16 @@
   // plane are marked with |overlay_handled| set to true, otherwise they are
   // to be traditionally composited. Candidates with |overlay_handled| set to
   // true must also have their |display_rect| converted to integer
-  // coordinates if necessary.
-  virtual void CheckOverlaySupport(OverlayCandidateList* surfaces) = 0;
+  // coordinates if necessary. When the output surface uses buffer from the
+  // buffer queue, it generates a |primary_plane|. The |primary_plane| is
+  // always handled, but its information needs to be passed to the hardware
+  // overlay system though this function.
+  virtual void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                                   OverlayCandidateList* surfaces) = 0;
 
   // The OverlayCandidate for the OutputSurface. Allows the validator to update
   // any properties of the |surface| required by the platform.
-  virtual void AdjustOutputSurfaceOverlay(OverlayCandidate* candidate) {}
+  virtual void AdjustOutputSurfaceOverlay(PrimaryPlane* output_surface_plane) {}
 
   // Set the overlay display transform and viewport size. Value only used for
   // Android Surface Control.
@@ -61,7 +69,7 @@
   // Returns the overlay damage rect covering the main plane rendered by the
   // OutputSurface. This rect is in the same space where the OutputSurface
   // renders the content for the main plane, including the display transform if
-  // needed.
+  // needed. Should only be called after the overlays are processed.
   virtual gfx::Rect GetOverlayDamageRectForOutputSurface(
       const OverlayCandidate& candidate) const;
 
@@ -71,19 +79,29 @@
 
   // Iterate through a list of strategies and attempt to overlay with each.
   // Returns true if one of the attempts is successful. Has to be called after
-  // InitializeStrategies().
+  // InitializeStrategies(). A |primary_plane| represents the output surface's
+  // buffer that comes from |BufferQueue|. It is passed in here so it could be
+  // pass through to hardware through CheckOverlaySupport. It is not passed in
+  // as a const member because the underlay strategy changes the
+  // |primary_plane|'s blending setting.
   bool AttemptWithStrategies(
       const SkMatrix44& output_color_matrix,
       const OverlayProcessor::FilterOperationsMap& render_pass_backdrop_filters,
       DisplayResourceProvider* resource_provider,
       RenderPassList* render_pass_list,
+      PrimaryPlane* primary_plane,
       OverlayCandidateList* candidates,
-      std::vector<gfx::Rect>* content_bounds) const;
+      std::vector<gfx::Rect>* content_bounds);
+
+  // If the full screen strategy is successful, we no longer need to overlay the
+  // output surface since it will be fully covered.
+  bool StrategyNeedsOutputSurfacePlaneRemoved();
 
  protected:
   OverlayCandidateValidator();
 
   OverlayProcessor::StrategyList strategies_;
+  OverlayProcessor::Strategy* last_successful_strategy_ = nullptr;
 };
 
 }  // namespace viz
diff --git a/components/viz/service/display/overlay_processor.cc b/components/viz/service/display/overlay_processor.cc
index 16951a68..12d6fc9 100644
--- a/components/viz/service/display/overlay_processor.cc
+++ b/components/viz/service/display/overlay_processor.cc
@@ -48,6 +48,12 @@
 
 }  // namespace
 
+// Default implementation of whether a strategy would remove the output surface
+// as overlay plane.
+bool OverlayProcessor::Strategy::RemoveOutputSurfaceAsOverlay() {
+  return false;
+}
+
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
 enum class UnderlayDamage {
@@ -139,7 +145,6 @@
     RenderPass* render_pass,
     const OverlayProcessor::FilterOperationsMap& render_pass_filters,
     const OverlayProcessor::FilterOperationsMap& render_pass_backdrop_filters,
-    OverlayCandidateList* overlay_candidates,
     CALayerOverlayList* ca_layer_overlays,
     gfx::Rect* damage_rect) {
   if (!overlay_validator_ || !overlay_validator_->AllowCALayerOverlays())
@@ -154,7 +159,7 @@
   // CALayer overlays are all-or-nothing. If all quads were replaced with
   // layers then clear the list and remove the backbuffer from the overcandidate
   // list.
-  overlay_candidates->clear();
+  output_surface_already_handled_ = true;
   overlay_damage_rect_ = render_pass->output_rect;
   *damage_rect = gfx::Rect();
   return true;
@@ -182,6 +187,7 @@
     const SkMatrix44& output_color_matrix,
     const OverlayProcessor::FilterOperationsMap& render_pass_filters,
     const OverlayProcessor::FilterOperationsMap& render_pass_backdrop_filters,
+    OutputSurfaceOverlayPlane* output_surface_plane,
     OverlayCandidateList* candidates,
     CALayerOverlayList* ca_layer_overlays,
     DCLayerOverlayList* dc_layer_overlays,
@@ -195,6 +201,9 @@
   SendPromotionHintsBeforeReturning notifier(resource_provider, candidates);
 #endif
 
+  // Clear to get ready to handle output surface as overlay.
+  output_surface_already_handled_ = false;
+
   // Reset |previous_frame_underlay_rect_| in case UpdateDamageRect() not being
   // invoked.  Also reset |previous_frame_underlay_was_unoccluded_|.
   const gfx::Rect previous_frame_underlay_rect = previous_frame_underlay_rect_;
@@ -220,24 +229,23 @@
   // First attempt to process for CALayers.
   if (ProcessForCALayers(resource_provider, root_render_pass,
                          render_pass_filters, render_pass_backdrop_filters,
-                         candidates, ca_layer_overlays, damage_rect)) {
-    DCHECK(candidates->empty());
+                         ca_layer_overlays, damage_rect)) {
     return;
   }
 
   if (ProcessForDCLayers(resource_provider, render_passes, render_pass_filters,
                          render_pass_backdrop_filters, dc_layer_overlays,
                          damage_rect)) {
-    DCHECK(candidates->empty());
     return;
   }
 
+  DCHECK(candidates->empty());
   // Only if that fails, attempt hardware overlay strategies.
   bool success = false;
   if (overlay_validator_) {
     success = overlay_validator_->AttemptWithStrategies(
         output_color_matrix, render_pass_backdrop_filters, resource_provider,
-        render_passes, candidates, content_bounds);
+        render_passes, output_surface_plane, candidates, content_bounds);
   }
 
   if (success) {
@@ -248,16 +256,7 @@
     if (!previous_frame_underlay_rect.IsEmpty())
       damage_rect->Union(previous_frame_underlay_rect);
 
-    // If no strategy worked the only remaining overlay in the list is the one
-    // backed by the OutputSurface. Make sure the OverlayCandidateValidator
-    // applies any modifications if needed.
-    if (!candidates->empty() && overlay_validator_) {
-      DCHECK_EQ(candidates->size(), 1u);
-      DCHECK(candidates->back().use_output_surface_for_resource);
-
-      overlay_validator_->AdjustOutputSurfaceOverlay(&candidates->back());
-      DCHECK_EQ(candidates->size(), 1u);
-    }
+    DCHECK(candidates->empty());
   }
 
   TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("viz.debug.overlay_planes"),
@@ -281,16 +280,12 @@
   for (const OverlayCandidate& overlay : *candidates) {
     if (overlay.plane_z_order >= 0) {
       const gfx::Rect overlay_display_rect =
-          ToEnclosedRect(overlay.display_rect);
+          overlay_validator_->GetOverlayDamageRectForOutputSurface(overlay);
       // If an overlay candidate comes from output surface, its z-order should
       // be 0.
-      DCHECK(!overlay.use_output_surface_for_resource ||
-             overlay.plane_z_order == 0);
-      if (!overlay.use_output_surface_for_resource) {
-        overlay_damage_rect_.Union(overlay_display_rect);
-        if (overlay.is_opaque)
-          damage_rect->Subtract(overlay_display_rect);
-      }
+      overlay_damage_rect_.Union(overlay_display_rect);
+      if (overlay.is_opaque)
+        damage_rect->Subtract(overlay_display_rect);
     } else {
       // Process underlay candidates:
       // Track the underlay_rect from frame to frame. If it is the same and
@@ -341,6 +336,51 @@
   previous_frame_underlay_rect_ = this_frame_underlay_rect;
 }
 
+OverlayProcessor::OutputSurfaceOverlayPlane
+OverlayProcessor::ProcessOutputSurfaceAsOverlay(
+    const gfx::Size& viewport_size,
+    const gfx::BufferFormat& buffer_format,
+    const gfx::ColorSpace& color_space) const {
+  OutputSurfaceOverlayPlane overlay_plane;
+  overlay_plane.transform = gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE;
+  overlay_plane.resource_size = viewport_size;
+  overlay_plane.format = buffer_format;
+  overlay_plane.color_space = color_space;
+  overlay_plane.enable_blending = false;
+
+  // Adjust transformation and display_rect based on display rotation.
+  overlay_plane.display_rect =
+      gfx::RectF(viewport_size.width(), viewport_size.height());
+
+#if defined(ALWAYS_ENABLE_BLENDING_FOR_PRIMARY)
+  // On Chromecast, always use RGBA as the scanout format for the primary plane.
+  overlay_plane.enable_blending = true;
+#endif
+  return overlay_plane;
+}
+
+void OverlayProcessor::AdjustOutputSurfaceOverlay(
+    base::Optional<OutputSurfaceOverlayPlane>* output_surface_plane) {
+  if (!output_surface_plane->has_value())
+    return;
+
+  // This is used by the surface control implementation to adjust the display
+  // transform and the display rect.
+  if (overlay_validator_)
+    overlay_validator_->AdjustOutputSurfaceOverlay(
+        &(output_surface_plane->value()));
+
+  // If the overlay candidates cover the entire screen, the
+  // |output_surface_plane| could be removed.
+  if (overlay_validator_) {
+    output_surface_already_handled_ |=
+        overlay_validator_->StrategyNeedsOutputSurfacePlaneRemoved();
+  }
+
+  if (output_surface_already_handled_)
+    output_surface_plane->reset();
+}
+
 bool OverlayProcessor::NeedsSurfaceOccludingDamageRect() const {
   return overlay_validator_ &&
          overlay_validator_->NeedsSurfaceOccludingDamageRect();
diff --git a/components/viz/service/display/overlay_processor.h b/components/viz/service/display/overlay_processor.h
index 6373eab..12a39503 100644
--- a/components/viz/service/display/overlay_processor.h
+++ b/components/viz/service/display/overlay_processor.h
@@ -37,9 +37,31 @@
       bool zero_damage_rect,
       bool occluding_damage_equal_to_damage_rect);
 
+  // Data needed to represent |OutputSurface| as an overlay plane. Due to the
+  // default values for the primary plane, this is a partial list of
+  // OverlayCandidate.
+  struct VIZ_SERVICE_EXPORT OutputSurfaceOverlayPlane {
+    // Display's rotation information.
+    gfx::OverlayTransform transform;
+    // Rect on the display to position to. This takes in account of Display's
+    // rotation.
+    gfx::RectF display_rect;
+    // Size of output surface in pixels.
+    gfx::Size resource_size;
+    // Format of the buffer to scanout.
+    gfx::BufferFormat format;
+    // ColorSpace of the buffer for scanout.
+    gfx::ColorSpace color_space;
+    // Enable blending when we have underlay.
+    bool enable_blending;
+    // Gpu fence to wait for before overlay is ready for display.
+    unsigned gpu_fence_id;
+  };
+
   class VIZ_SERVICE_EXPORT Strategy {
    public:
     virtual ~Strategy() {}
+    using PrimaryPlane = OverlayProcessor::OutputSurfaceOverlayPlane;
     // Returns false if the strategy cannot be made to work with the
     // current set of render passes. Returns true if the strategy was successful
     // and adds any additional passes necessary to represent overlays to
@@ -50,9 +72,21 @@
         const FilterOperationsMap& render_pass_backdrop_filters,
         DisplayResourceProvider* resource_provider,
         RenderPassList* render_pass_list,
+        const PrimaryPlane* primary_plane,
         OverlayCandidateList* candidates,
         std::vector<gfx::Rect>* content_bounds) = 0;
 
+    // Currently this is only overridden by the Underlay strategy: the underlay
+    // strategy needs to enable blending for the primary plane in order to show
+    // content underneath.
+    virtual void AdjustOutputSurfaceOverlay(
+        OutputSurfaceOverlayPlane* output_surface_plane) {}
+
+    // Currently this is only overridden by the Fullscreen strategy: the
+    // fullscreen strategy covers the entire screen and there is no need to use
+    // the primary plane.
+    virtual bool RemoveOutputSurfaceAsOverlay();
+
     virtual OverlayStrategy GetUMAEnum() const;
   };
   using StrategyList = std::vector<std::unique_ptr<Strategy>>;
@@ -87,12 +121,29 @@
       const SkMatrix44& output_color_matrix,
       const FilterOperationsMap& render_pass_filters,
       const FilterOperationsMap& render_pass_backdrop_filters,
+      OutputSurfaceOverlayPlane* output_surface_plane,
       OverlayCandidateList* overlay_candidates,
       CALayerOverlayList* ca_layer_overlays,
       DCLayerOverlayList* dc_layer_overlays,
       gfx::Rect* damage_rect,
       std::vector<gfx::Rect>* content_bounds);
 
+  // TODO(weiliangc): Eventually the asymmetry between primary plane and
+  // non-primary places should be internalized and should not have a special
+  // API.
+  OutputSurfaceOverlayPlane ProcessOutputSurfaceAsOverlay(
+      const gfx::Size& viewport_size,
+      const gfx::BufferFormat& buffer_format,
+      const gfx::ColorSpace& color_space) const;
+
+  // For Mac, if we successfully generated a candidate list for CALayerOverlay,
+  // we no longer need the |output_surface_plane|. This function takes a pointer
+  // to the base::Optional instance so the instance can be reset.
+  // TODO(weiliangc): Internalize the |output_surface_plane| inside the overlay
+  // processor.
+  void AdjustOutputSurfaceOverlay(
+      base::Optional<OutputSurfaceOverlayPlane>* output_surface_plane);
+
  protected:
   // For testing.
   explicit OverlayProcessor(
@@ -115,7 +166,6 @@
       RenderPass* render_pass,
       const FilterOperationsMap& render_pass_filters,
       const FilterOperationsMap& render_pass_backdrop_filters,
-      OverlayCandidateList* overlay_candidates,
       CALayerOverlayList* ca_layer_overlays,
       gfx::Rect* damage_rect);
   bool ProcessForDCLayers(
@@ -134,6 +184,7 @@
 
   std::unique_ptr<DCLayerOverlayProcessor> dc_layer_overlay_processor_;
 
+  bool output_surface_already_handled_;
   DISALLOW_COPY_AND_ASSIGN(OverlayProcessor);
 };
 
diff --git a/components/viz/service/display/overlay_strategy_fullscreen.cc b/components/viz/service/display/overlay_strategy_fullscreen.cc
index 7515ad52..f5ef58f 100644
--- a/components/viz/service/display/overlay_strategy_fullscreen.cc
+++ b/components/viz/service/display/overlay_strategy_fullscreen.cc
@@ -25,8 +25,11 @@
     const OverlayProcessor::FilterOperationsMap& render_pass_backdrop_filters,
     DisplayResourceProvider* resource_provider,
     RenderPassList* render_pass_list,
+    const PrimaryPlane* primary_plane,
     OverlayCandidateList* candidate_list,
     std::vector<gfx::Rect>* content_bounds) {
+  // Before we attempt an overlay strategy, the candidate list should be empty.
+  DCHECK(candidate_list->empty());
   RenderPass* render_pass = render_pass_list->back().get();
   QuadList* quad_list = &render_pass->quad_list;
   // First quad of quad_list is the top most quad.
@@ -59,7 +62,7 @@
   candidate.plane_z_order = 0;
   OverlayCandidateList new_candidate_list;
   new_candidate_list.push_back(candidate);
-  capability_checker_->CheckOverlaySupport(&new_candidate_list);
+  capability_checker_->CheckOverlaySupport(primary_plane, &new_candidate_list);
   if (!new_candidate_list.front().overlay_handled)
     return false;
 
@@ -73,4 +76,11 @@
   return OverlayStrategy::kFullscreen;
 }
 
+bool OverlayStrategyFullscreen::RemoveOutputSurfaceAsOverlay() {
+  // This is called when the strategy is successful. In this case the entire
+  // screen is covered by the overlay candidate and there is no need to overlay
+  // the output surface.
+  return true;
+}
+
 }  // namespace viz
diff --git a/components/viz/service/display/overlay_strategy_fullscreen.h b/components/viz/service/display/overlay_strategy_fullscreen.h
index a3a76a9..258eba2 100644
--- a/components/viz/service/display/overlay_strategy_fullscreen.h
+++ b/components/viz/service/display/overlay_strategy_fullscreen.h
@@ -27,9 +27,11 @@
       const OverlayProcessor::FilterOperationsMap& render_pass_backdrop_filters,
       DisplayResourceProvider* resource_provider,
       RenderPassList* render_pass,
+      const PrimaryPlane* primary_plane,
       OverlayCandidateList* candidate_list,
       std::vector<gfx::Rect>* content_bounds) override;
 
+  bool RemoveOutputSurfaceAsOverlay() override;
   OverlayStrategy GetUMAEnum() const override;
 
  private:
diff --git a/components/viz/service/display/overlay_strategy_single_on_top.cc b/components/viz/service/display/overlay_strategy_single_on_top.cc
index 937350a..727a60f 100644
--- a/components/viz/service/display/overlay_strategy_single_on_top.cc
+++ b/components/viz/service/display/overlay_strategy_single_on_top.cc
@@ -23,8 +23,11 @@
     const OverlayProcessor::FilterOperationsMap& render_pass_backdrop_filters,
     DisplayResourceProvider* resource_provider,
     RenderPassList* render_pass_list,
+    const PrimaryPlane* primary_plane,
     OverlayCandidateList* candidate_list,
     std::vector<gfx::Rect>* content_bounds) {
+  // Before we attempt an overlay strategy, we shouldn't have a candidate.
+  DCHECK(candidate_list->empty());
   RenderPass* render_pass = render_pass_list->back().get();
   QuadList* quad_list = &render_pass->quad_list;
   // Build a list of candidates with the associated quad.
@@ -54,7 +57,8 @@
   if (best_quad_it == quad_list->end())
     return false;
 
-  if (TryOverlay(quad_list, candidate_list, best_candidate, best_quad_it)) {
+  if (TryOverlay(quad_list, primary_plane, candidate_list, best_candidate,
+                 best_quad_it)) {
     if (previous_frame_resource_id_ != best_candidate.resource_id) {
       previous_frame_resource_id_ = best_candidate.resource_id;
       same_resource_id_frames_count_ = 1;
@@ -69,16 +73,19 @@
 
 bool OverlayStrategySingleOnTop::TryOverlay(
     QuadList* quad_list,
+    const PrimaryPlane* primary_plane,
     OverlayCandidateList* candidate_list,
     const OverlayCandidate& candidate,
     QuadList::Iterator candidate_iterator) {
+  // SingleOnTop strategy means we should have one candidate.
+  DCHECK(candidate_list->empty());
   // Add the overlay.
   OverlayCandidateList new_candidate_list = *candidate_list;
   new_candidate_list.push_back(candidate);
   new_candidate_list.back().plane_z_order = 1;
 
   // Check for support.
-  capability_checker_->CheckOverlaySupport(&new_candidate_list);
+  capability_checker_->CheckOverlaySupport(primary_plane, &new_candidate_list);
 
   const OverlayCandidate& overlay_candidate = new_candidate_list.back();
   // If the candidate can be handled by an overlay, create a pass for it.
diff --git a/components/viz/service/display/overlay_strategy_single_on_top.h b/components/viz/service/display/overlay_strategy_single_on_top.h
index a7ad251..b218411f 100644
--- a/components/viz/service/display/overlay_strategy_single_on_top.h
+++ b/components/viz/service/display/overlay_strategy_single_on_top.h
@@ -25,6 +25,7 @@
       const OverlayProcessor::FilterOperationsMap& render_pass_backdrop_filters,
       DisplayResourceProvider* resource_provider,
       RenderPassList* render_pass,
+      const PrimaryPlane* primary_plane,
       OverlayCandidateList* candidate_list,
       std::vector<gfx::Rect>* content_bounds) override;
 
@@ -34,6 +35,7 @@
   static constexpr size_t kMaxFrameCandidateWithSameResourceId = 3;
 
   bool TryOverlay(QuadList* quad_list,
+                  const PrimaryPlane* primary_plane,
                   OverlayCandidateList* candidate_list,
                   const OverlayCandidate& candidate,
                   QuadList::Iterator candidate_iterator);
diff --git a/components/viz/service/display/overlay_strategy_underlay.cc b/components/viz/service/display/overlay_strategy_underlay.cc
index 86be7fd7..985c34d3 100644
--- a/components/viz/service/display/overlay_strategy_underlay.cc
+++ b/components/viz/service/display/overlay_strategy_underlay.cc
@@ -26,8 +26,11 @@
     const OverlayProcessor::FilterOperationsMap& render_pass_backdrop_filters,
     DisplayResourceProvider* resource_provider,
     RenderPassList* render_pass_list,
+    const PrimaryPlane* primary_plane,
     OverlayCandidateList* candidate_list,
     std::vector<gfx::Rect>* content_bounds) {
+  // Before we attempt an overlay strategy, the candidate list should be empty.
+  DCHECK(candidate_list->empty());
   RenderPass* render_pass = render_pass_list->back().get();
   QuadList& quad_list = render_pass->quad_list;
   const bool compute_hints =
@@ -62,10 +65,21 @@
     OverlayCandidateList new_candidate_list = *candidate_list;
     new_candidate_list.push_back(candidate);
     new_candidate_list.back().plane_z_order = -1;
-    new_candidate_list.front().is_opaque = false;
 
-    // Check for support.
-    capability_checker_->CheckOverlaySupport(&new_candidate_list);
+    if (primary_plane) {
+      // Since there is a list of strategies to go through, each strategy should
+      // not change the input parameters. In this case, we need to keep the
+      // |primary_plane| unchanged. The underlay strategy only works when the
+      // |primary_plane| supports blending. In order to check the hardware
+      // support, make a copy of the |primary_plane| with blending enabled.
+      PrimaryPlane new_plane_candidate(*primary_plane);
+      new_plane_candidate.enable_blending = true;
+      // Check for support.
+      capability_checker_->CheckOverlaySupport(&new_plane_candidate,
+                                               &new_candidate_list);
+    } else {
+      capability_checker_->CheckOverlaySupport(nullptr, &new_candidate_list);
+    }
 
     // If the candidate can be handled by an overlay, create a pass for it. We
     // need to switch out the video quad with a black transparent one.
@@ -100,6 +114,14 @@
   return false;
 }
 
+// Turn on blending for the output surface plane so the underlay could show
+// through.
+void OverlayStrategyUnderlay::AdjustOutputSurfaceOverlay(
+    OverlayProcessor::OutputSurfaceOverlayPlane* output_surface_plane) {
+  if (output_surface_plane)
+    output_surface_plane->enable_blending = true;
+}
+
 OverlayStrategy OverlayStrategyUnderlay::GetUMAEnum() const {
   return OverlayStrategy::kUnderlay;
 }
diff --git a/components/viz/service/display/overlay_strategy_underlay.h b/components/viz/service/display/overlay_strategy_underlay.h
index 338b8f75..263f4ddb 100644
--- a/components/viz/service/display/overlay_strategy_underlay.h
+++ b/components/viz/service/display/overlay_strategy_underlay.h
@@ -41,9 +41,13 @@
       const OverlayProcessor::FilterOperationsMap& render_pass_backdrop_filters,
       DisplayResourceProvider* resource_provider,
       RenderPassList* render_pass,
+      const PrimaryPlane* primary_plane,
       OverlayCandidateList* candidate_list,
       std::vector<gfx::Rect>* content_bounds) override;
 
+  void AdjustOutputSurfaceOverlay(OverlayProcessor::OutputSurfaceOverlayPlane*
+                                      output_surface_plane) override;
+
   OverlayStrategy GetUMAEnum() const override;
 
  private:
diff --git a/components/viz/service/display/overlay_strategy_underlay_cast.cc b/components/viz/service/display/overlay_strategy_underlay_cast.cc
index 2cd6e883..b5da829 100644
--- a/components/viz/service/display/overlay_strategy_underlay_cast.cc
+++ b/components/viz/service/display/overlay_strategy_underlay_cast.cc
@@ -31,8 +31,11 @@
     const OverlayProcessor::FilterOperationsMap& render_pass_backdrop_filters,
     DisplayResourceProvider* resource_provider,
     RenderPassList* render_pass_list,
+    const PrimaryPlane* primary_plane,
     OverlayCandidateList* candidate_list,
     std::vector<gfx::Rect>* content_bounds) {
+  // Before we attempt an overlay strategy, the candidate list should be empty.
+  DCHECK(candidate_list->empty());
   RenderPass* render_pass = render_pass_list->back().get();
   QuadList& quad_list = render_pass->quad_list;
   bool found_underlay = false;
@@ -81,14 +84,6 @@
     VLOG(1) << (found_underlay ? "Overlay activated" : "Overlay deactivated");
   }
 
-  // If the primary plane shows up in the candidates list make sure it isn't
-  // opaque otherwise the content underneath won't be visible.
-  if (!candidate_list->empty()) {
-    DCHECK_EQ(1u, candidate_list->size());
-    DCHECK(candidate_list->front().use_output_surface_for_resource);
-    candidate_list->front().is_opaque = false;
-  }
-
   if (found_underlay) {
     for (auto it = quad_list.begin(); it != quad_list.end(); ++it) {
       OverlayCandidate candidate;
diff --git a/components/viz/service/display/overlay_strategy_underlay_cast.h b/components/viz/service/display/overlay_strategy_underlay_cast.h
index 284df81..83d67f3 100644
--- a/components/viz/service/display/overlay_strategy_underlay_cast.h
+++ b/components/viz/service/display/overlay_strategy_underlay_cast.h
@@ -28,6 +28,7 @@
       const OverlayProcessor::FilterOperationsMap& render_pass_backdrop_filters,
       DisplayResourceProvider* resource_provider,
       RenderPassList* render_pass,
+      const PrimaryPlane* primary_plane,
       OverlayCandidateList* candidate_list,
       std::vector<gfx::Rect>* content_bounds) override;
 
diff --git a/components/viz/service/display/overlay_unittest.cc b/components/viz/service/display/overlay_unittest.cc
index 2ab0312..74c2eca 100644
--- a/components/viz/service/display/overlay_unittest.cc
+++ b/components/viz/service/display/overlay_unittest.cc
@@ -67,6 +67,7 @@
     gfx::Transform(-0.9f, 0, 0, -0.8f, 1.0f, 1.0f);  // x,y -> 1-x,1-y.
 const gfx::Transform kSwapTransform =
     gfx::Transform(0, 1, 1, 0, 0, 0);  // x,y -> y,x.
+const gfx::BufferFormat kDefaultBufferFormat = gfx::BufferFormat::RGBA_8888;
 
 class TestOverlayCandidateValidator : public OverlayCandidateValidator {
  public:
@@ -75,7 +76,8 @@
   bool AllowCALayerOverlays() const override { return false; }
   bool AllowDCLayerOverlays() const override { return false; }
   bool NeedsSurfaceOccludingDamageRect() const override { return false; }
-  void CheckOverlaySupport(OverlayCandidateList* surfaces) override {}
+  void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                           OverlayCandidateList* surfaces) override {}
 };
 
 class FullscreenOverlayValidator : public TestOverlayCandidateValidator {
@@ -86,7 +88,8 @@
   bool AllowCALayerOverlays() const override { return false; }
   bool AllowDCLayerOverlays() const override { return false; }
   bool NeedsSurfaceOccludingDamageRect() const override { return true; }
-  void CheckOverlaySupport(OverlayCandidateList* surfaces) override {
+  void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                           OverlayCandidateList* surfaces) override {
     surfaces->back().overlay_handled = true;
   }
 };
@@ -102,14 +105,14 @@
   bool AllowCALayerOverlays() const override { return false; }
   bool AllowDCLayerOverlays() const override { return false; }
   bool NeedsSurfaceOccludingDamageRect() const override { return true; }
-  void CheckOverlaySupport(OverlayCandidateList* surfaces) override {
-    // We may have 1 or 2 surfaces depending on whether this ran through the
-    // full renderer and picked up the output surface, or not.
-    ASSERT_LE(1U, surfaces->size());
-    ASSERT_GE(2U, surfaces->size());
+  void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                           OverlayCandidateList* surfaces) override {
+    // We have one overlay surface to test. The output surface as primary plane
+    // is optional, depending on whether this ran
+    // through the full renderer and picked up the output surface, or not.
+    ASSERT_EQ(1U, surfaces->size());
 
     OverlayCandidate& candidate = surfaces->back();
-    EXPECT_TRUE(!candidate.use_output_surface_for_resource);
     for (const auto& r : expected_rects_) {
       const float kAbsoluteError = 0.01f;
       if (std::abs(r.x() - candidate.display_rect.x()) <= kAbsoluteError &&
@@ -145,7 +148,8 @@
   bool AllowCALayerOverlays() const override { return true; }
   bool AllowDCLayerOverlays() const override { return false; }
   bool NeedsSurfaceOccludingDamageRect() const override { return false; }
-  void CheckOverlaySupport(OverlayCandidateList* surfaces) override {}
+  void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                           OverlayCandidateList* surfaces) override {}
 };
 
 class DCLayerValidator : public TestOverlayCandidateValidator {
@@ -153,7 +157,8 @@
   bool AllowCALayerOverlays() const override { return false; }
   bool AllowDCLayerOverlays() const override { return true; }
   bool NeedsSurfaceOccludingDamageRect() const override { return true; }
-  void CheckOverlaySupport(OverlayCandidateList* surfaces) override {}
+  void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                           OverlayCandidateList* surfaces) override {}
 };
 
 class SingleOnTopOverlayValidator : public SingleOverlayValidator {
@@ -638,8 +643,8 @@
 
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 
   // Check that all the quads are gone.
@@ -675,8 +680,8 @@
   // overlays since color matrices are not supported yet.
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetNonIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(0U, candidate_list.size());
 
   // Check that the 2 quads are not gone.
@@ -700,8 +705,8 @@
 
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
 
   // Check that all the quads are gone.
   EXPECT_EQ(1U, main_pass->quad_list.size());
@@ -726,8 +731,8 @@
 
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 
   // Check that the quad is gone.
@@ -756,8 +761,8 @@
 
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(0U, candidate_list.size());
 
   // Check that the 2 quads are not gone.
@@ -782,8 +787,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(0U, candidate_list.size());
 
   // Check that the quad is not gone.
@@ -814,8 +819,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 
   // Check that the fullscreen quad is gone.
@@ -846,8 +851,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 
   // Check that the quad is gone.
@@ -895,8 +900,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 
   // Check that one quad is gone.
@@ -923,21 +928,14 @@
   // Check for potential candidates.
   OverlayCandidateList candidate_list;
 
-  // Primary plane.
-  OverlayCandidate output_surface_plane;
-  output_surface_plane.display_rect = gfx::RectF(kOverlayRect);
-  output_surface_plane.use_output_surface_for_resource = true;
-  output_surface_plane.overlay_handled = true;
-  candidate_list.push_back(output_surface_plane);
-
   OverlayProcessor::FilterOperationsMap render_pass_filters;
   OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
   RenderPassList pass_list;
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_TRUE(damage_rect_.IsEmpty());
 }
 
@@ -959,8 +957,8 @@
   OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
   // There should be nothing new here.
   CompareRenderPassLists(pass_list, original_pass_list);
@@ -988,8 +986,8 @@
   OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
   // There should be nothing new here.
   CompareRenderPassLists(pass_list, original_pass_list);
@@ -1016,8 +1014,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(1U, candidate_list.size());
 }
 
@@ -1036,8 +1034,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(1U, candidate_list.size());
   EXPECT_FALSE(damage_rect_.IsEmpty());
   gfx::Rect overlay_damage_rect =
@@ -1059,8 +1057,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
 }
 
@@ -1078,8 +1076,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(1U, candidate_list.size());
 }
 
@@ -1098,8 +1096,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
 }
 
@@ -1117,8 +1115,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
 }
 
@@ -1136,8 +1134,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
 }
 
@@ -1156,8 +1154,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
 }
 
@@ -1176,8 +1174,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(1U, candidate_list.size());
 }
 
@@ -1198,8 +1196,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
   EXPECT_EQ(gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL,
             candidate_list.back().transform);
@@ -1223,8 +1221,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
   EXPECT_EQ(gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL,
             candidate_list.back().transform);
@@ -1246,8 +1244,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(1U, candidate_list.size());
 }
 
@@ -1268,8 +1266,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 }
 
@@ -1290,8 +1288,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
   EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_90, candidate_list.back().transform);
 }
@@ -1313,8 +1311,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
   EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_180, candidate_list.back().transform);
 }
@@ -1336,8 +1334,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
   EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_270, candidate_list.back().transform);
 }
@@ -1357,8 +1355,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 }
 
@@ -1376,8 +1374,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(0U, candidate_list.size());
 }
 
@@ -1410,8 +1408,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(0U, candidate_list.size());
 }
 
@@ -1430,8 +1428,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 }
 
@@ -1449,8 +1447,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 }
 
@@ -1473,8 +1471,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(1U, candidate_list.size());
 }
 
@@ -1499,8 +1497,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(1U, candidate_list.size());
 }
 
@@ -1523,8 +1521,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(1U, candidate_list.size());
 }
 
@@ -1547,8 +1545,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
 }
 
@@ -1569,8 +1567,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
 }
 
@@ -1622,8 +1620,8 @@
     pass_list.push_back(std::move(pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-        nullptr, nullptr, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
 
     if (i <= kFramesSkippedBeforeNotPromoting) {
       EXPECT_EQ(1U, candidate_list.size());
@@ -1657,8 +1655,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
   EXPECT_EQ(-1, candidate_list[0].plane_z_order);
   EXPECT_EQ(2U, main_pass->quad_list.size());
@@ -1688,8 +1686,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
   EXPECT_EQ(-1, candidate_list[0].plane_z_order);
   // The overlay quad should have changed to a SOLID_COLOR quad.
@@ -1717,8 +1715,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(kOverlayRect, damage_rect_);
 }
@@ -1745,8 +1743,8 @@
     pass_list.push_back(std::move(pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-        nullptr, nullptr, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   }
 
   // The second time the same overlay rect is scheduled it will be subtracted
@@ -1777,8 +1775,8 @@
     pass_list.push_back(std::move(pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-        nullptr, nullptr, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
 
     EXPECT_EQ(overlay_rects[i], damage_rect_);
   }
@@ -1812,8 +1810,8 @@
     pass_list.push_back(std::move(pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-        nullptr, nullptr, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   }
 
   EXPECT_EQ(kOverlayRect, damage_rect_);
@@ -1843,8 +1841,8 @@
     pass_list.push_back(std::move(pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-        nullptr, nullptr, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
 
     // The damage rect should not be subtracted if the underlay is occluded
     // (i==0) or it is unoccluded for the first time (i==1).
@@ -1875,8 +1873,8 @@
     pass_list.push_back(std::move(pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-        nullptr, nullptr, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   }
 
   EXPECT_EQ(kOverlayRect, damage_rect_);
@@ -1905,8 +1903,8 @@
     pass_list.push_back(std::move(pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-        nullptr, nullptr, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   }
 
   EXPECT_TRUE(damage_rect_.IsEmpty());
@@ -1925,22 +1923,24 @@
                         kOverlayRect);
 
   OverlayCandidateList candidate_list;
-  OverlayCandidate candidate;
-  candidate.use_output_surface_for_resource = true;
-  candidate.is_opaque = true;
-  candidate_list.push_back(candidate);
 
   OverlayProcessor::FilterOperationsMap render_pass_filters;
   OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
   RenderPassList pass_list;
   pass_list.push_back(std::move(pass));
+
+  auto output_surface_plane = overlay_processor_->ProcessOutputSurfaceAsOverlay(
+      kDisplaySize, kDefaultBufferFormat, gfx::ColorSpace());
+  OverlayProcessor::OutputSurfaceOverlayPlane* primary_plane =
+      &output_surface_plane;
+
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, primary_plane,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
 
-  EXPECT_EQ(2U, candidate_list.size());
-  ASSERT_EQ(false, candidate_list[0].is_opaque);
+  EXPECT_EQ(1U, candidate_list.size());
+  ASSERT_EQ(true, output_surface_plane.enable_blending);
 }
 
 TEST_F(UnderlayTest, UpdateDamageWhenChangingUnderlays) {
@@ -1968,8 +1968,8 @@
     pass_list.push_back(std::move(pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-        nullptr, nullptr, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   }
 
   EXPECT_EQ(kOverlayRect, damage_rect_);
@@ -2010,8 +2010,8 @@
     pass_list.push_back(std::move(pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_background_filters, &candidate_list,
-        nullptr, nullptr, &damage_rect, &content_bounds_);
+        render_pass_filters, render_pass_background_filters, nullptr,
+        &candidate_list, nullptr, nullptr, &damage_rect, &content_bounds_);
 
     EXPECT_EQ(expected_damages[i], damage_rect);
     ASSERT_EQ(expected_candidate_size[i], candidate_list.size());
@@ -2053,8 +2053,8 @@
     pass_list.push_back(std::move(pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-        nullptr, nullptr, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
 
     if (i == 0 || i == 1 || i == 3)
       EXPECT_FALSE(damage_rect_.IsEmpty());
@@ -2077,8 +2077,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, content_bounds_.size());
 }
 
@@ -2094,8 +2094,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
   EXPECT_TRUE(content_bounds_[0].IsEmpty());
@@ -2124,8 +2124,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
   EXPECT_TRUE(content_bounds_[0].IsEmpty());
@@ -2146,8 +2146,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
   EXPECT_EQ(kOverlayTopLeftRect, content_bounds_[0]);
@@ -2171,8 +2171,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
   EXPECT_EQ(kOverlayRect, content_bounds_[0]);
@@ -2201,8 +2201,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
   EXPECT_EQ(gfx::Rect(0, 0, 11, 11), content_bounds_[0]);
@@ -2232,13 +2232,14 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
   EXPECT_EQ(kOverlayRect, content_bounds_[0]);
 }
 
+#if defined(ALWAYS_ENABLE_BLENDING_FOR_PRIMARY)
 TEST_F(UnderlayCastTest, PrimaryPlaneOverlayIsAlwaysTransparent) {
   std::unique_ptr<RenderPass> pass = CreateRenderPass();
   gfx::Rect output_rect = pass->output_rect;
@@ -2247,23 +2248,23 @@
                      output_rect, SK_ColorWHITE);
 
   OverlayCandidateList candidate_list;
-  OverlayCandidate candidate;
-  candidate.use_output_surface_for_resource = true;
-  candidate.is_opaque = true;
-  candidate_list.push_back(candidate);
 
   OverlayProcessor::FilterOperationsMap render_pass_filters;
   OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
   RenderPassList pass_list;
   pass_list.push_back(std::move(pass));
+  auto output_surface_plane = overlay_processor_->ProcessOutputSurfaceAsOverlay(
+      kDisplaySize, kDefaultBufferFormat, gfx::ColorSpace());
+
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, &output_surface_plane,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
 
-  ASSERT_EQ(false, candidate_list[0].is_opaque);
+  ASSERT_EQ(true, output_surface_plane.enable_blending);
   EXPECT_EQ(0U, content_bounds_.size());
 }
+#endif
 
 TEST_F(UnderlayCastTest, NoOverlayPromotionWithoutProtectedContent) {
   std::unique_ptr<RenderPass> pass = CreateRenderPass();
@@ -2279,23 +2280,13 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &candidate_list,
-      nullptr, nullptr, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
 
   ASSERT_TRUE(candidate_list.empty());
   EXPECT_TRUE(content_bounds_.empty());
 }
 
-OverlayCandidateList BackbufferOverlayList(const RenderPass* root_render_pass) {
-  OverlayCandidateList list;
-  OverlayCandidate output_surface_plane;
-  output_surface_plane.display_rect = gfx::RectF(root_render_pass->output_rect);
-  output_surface_plane.use_output_surface_for_resource = true;
-  output_surface_plane.overlay_handled = true;
-  list.push_back(output_surface_plane);
-  return list;
-}
-
 TEST_F(CALayerOverlayTest, AllowNonAxisAlignedTransform) {
   std::unique_ptr<RenderPass> pass = CreateRenderPass();
   CreateFullscreenCandidateQuad(
@@ -2303,18 +2294,20 @@
       child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
   pass->shared_quad_state_list.back()
       ->quad_to_target_transform.RotateAboutZAxis(45.f);
-
+  gfx::Size display_size(pass->output_rect.width(), pass->output_rect.height());
   gfx::Rect damage_rect;
   CALayerOverlayList ca_layer_list;
-  OverlayCandidateList overlay_list(BackbufferOverlayList(pass.get()));
+  OverlayCandidateList overlay_list;
   OverlayProcessor::FilterOperationsMap render_pass_filters;
   OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
   RenderPassList pass_list;
   pass_list.push_back(std::move(pass));
+
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &overlay_list,
+      render_pass_filters, render_pass_backdrop_filters, nullptr, &overlay_list,
       &ca_layer_list, nullptr, &damage_rect_, &content_bounds_);
+
   EXPECT_EQ(gfx::Rect(), damage_rect);
   EXPECT_EQ(0U, overlay_list.size());
   EXPECT_EQ(1U, ca_layer_list.size());
@@ -2328,22 +2321,25 @@
       child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
   pass->shared_quad_state_list.back()
       ->quad_to_target_transform.RotateAboutXAxis(45.f);
+  gfx::Size display_size(pass->output_rect.width(), pass->output_rect.height());
 
   CALayerOverlayList ca_layer_list;
-  OverlayCandidateList overlay_list(BackbufferOverlayList(pass.get()));
+  OverlayCandidateList overlay_list;
   OverlayProcessor::FilterOperationsMap render_pass_filters;
   OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
   RenderPassList pass_list;
   pass_list.push_back(std::move(pass));
+
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &overlay_list,
+      render_pass_filters, render_pass_backdrop_filters, nullptr, &overlay_list,
       &ca_layer_list, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, overlay_list.size());
   EXPECT_EQ(1U, ca_layer_list.size());
   gfx::Transform expected_transform;
   expected_transform.RotateAboutXAxis(45.f);
   gfx::Transform actual_transform(ca_layer_list.back().shared_state->transform);
+
   EXPECT_EQ(expected_transform.ToString(), actual_transform.ToString());
   EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
 }
@@ -2355,18 +2351,21 @@
       child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
   pass->shared_quad_state_list.back()->is_clipped = true;
   pass->shared_quad_state_list.back()->clip_rect = kOverlayRect;
+  gfx::Size display_size(pass->output_rect.width(), pass->output_rect.height());
 
   gfx::Rect damage_rect;
   CALayerOverlayList ca_layer_list;
-  OverlayCandidateList overlay_list(BackbufferOverlayList(pass.get()));
+  OverlayCandidateList overlay_list;
   OverlayProcessor::FilterOperationsMap render_pass_filters;
   OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
   RenderPassList pass_list;
   pass_list.push_back(std::move(pass));
+
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &overlay_list,
+      render_pass_filters, render_pass_backdrop_filters, nullptr, &overlay_list,
       &ca_layer_list, nullptr, &damage_rect_, &content_bounds_);
+
   EXPECT_EQ(gfx::Rect(), damage_rect);
   EXPECT_EQ(0U, overlay_list.size());
   EXPECT_EQ(1U, ca_layer_list.size());
@@ -2380,18 +2379,21 @@
       child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
   pass->shared_quad_state_list.back()->is_clipped = true;
   pass->shared_quad_state_list.back()->clip_rect = gfx::Rect(64, 64, 128, 128);
+  gfx::Size display_size(pass->output_rect.width(), pass->output_rect.height());
 
   gfx::Rect damage_rect;
   CALayerOverlayList ca_layer_list;
-  OverlayCandidateList overlay_list(BackbufferOverlayList(pass.get()));
+  OverlayCandidateList overlay_list;
   OverlayProcessor::FilterOperationsMap render_pass_filters;
   OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
   RenderPassList pass_list;
   pass_list.push_back(std::move(pass));
+
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &overlay_list,
+      render_pass_filters, render_pass_backdrop_filters, nullptr, &overlay_list,
       &ca_layer_list, nullptr, &damage_rect_, &content_bounds_);
+
   EXPECT_EQ(gfx::Rect(), damage_rect);
   EXPECT_EQ(0U, overlay_list.size());
   EXPECT_EQ(1U, ca_layer_list.size());
@@ -2407,17 +2409,19 @@
       resource_provider_.get(), child_resource_provider_.get(),
       child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
   pass->shared_quad_state_list.back()->opacity = 0;
+  gfx::Size display_size(pass->output_rect.width(), pass->output_rect.height());
 
   gfx::Rect damage_rect;
   CALayerOverlayList ca_layer_list;
-  OverlayCandidateList overlay_list(BackbufferOverlayList(pass.get()));
+  OverlayCandidateList overlay_list;
   OverlayProcessor::FilterOperationsMap render_pass_filters;
   OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
   RenderPassList pass_list;
   pass_list.push_back(std::move(pass));
+
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &overlay_list,
+      render_pass_filters, render_pass_backdrop_filters, nullptr, &overlay_list,
       &ca_layer_list, nullptr, &damage_rect_, &content_bounds_);
   EXPECT_EQ(gfx::Rect(), damage_rect);
   EXPECT_EQ(0U, overlay_list.size());
@@ -2447,8 +2451,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &overlay_list, nullptr,
-      &dc_layer_list, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr, &overlay_list,
+      nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, overlay_list.size());
   EXPECT_EQ(1U, dc_layer_list.size());
   EXPECT_EQ(1, dc_layer_list.back().z_order);
@@ -2479,8 +2483,8 @@
   pass_list.push_back(std::move(pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &overlay_list, nullptr,
-      &dc_layer_list, &damage_rect_, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr, &overlay_list,
+      nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(gfx::Rect(), damage_rect);
   EXPECT_EQ(0U, overlay_list.size());
   ASSERT_EQ(1U, dc_layer_list.size());
@@ -2524,8 +2528,9 @@
     pass_list.push_back(std::move(pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &overlay_list,
-        nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
+        &content_bounds_);
     EXPECT_EQ(0U, overlay_list.size());
     EXPECT_EQ(2U, dc_layer_list.size());
     EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
@@ -2565,8 +2570,9 @@
     pass_list.push_back(std::move(pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &overlay_list,
-        nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
+        &content_bounds_);
     EXPECT_EQ(0U, overlay_list.size());
     EXPECT_EQ(2U, dc_layer_list.size());
     EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
@@ -2616,8 +2622,9 @@
     pass_list.push_back(std::move(pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &overlay_list,
-        nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
+        &content_bounds_);
     EXPECT_EQ(0U, overlay_list.size());
     EXPECT_EQ(1U, dc_layer_list.size());
     EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
@@ -2654,8 +2661,9 @@
     pass_list.push_back(std::move(pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &overlay_list,
-        nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
+        &content_bounds_);
     EXPECT_EQ(0U, overlay_list.size());
     EXPECT_EQ(1U, dc_layer_list.size());
     EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
@@ -2697,8 +2705,9 @@
     pass_list.push_back(std::move(root_pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &overlay_list,
-        nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
+        &content_bounds_);
     EXPECT_EQ(0U, overlay_list.size());
     EXPECT_EQ(1U, dc_layer_list.size());
     EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
@@ -2734,8 +2743,9 @@
     pass_list.push_back(std::move(root_pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &overlay_list,
-        nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
+        &content_bounds_);
     EXPECT_EQ(0U, overlay_list.size());
     EXPECT_EQ(1U, dc_layer_list.size());
     EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
@@ -2762,8 +2772,9 @@
     pass_list.push_back(std::move(pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &overlay_list,
-        nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
+        &content_bounds_);
     EXPECT_EQ(gfx::Rect(), damage_rect);
     EXPECT_EQ(0U, overlay_list.size());
     EXPECT_EQ(1U, dc_layer_list.size());
@@ -2859,8 +2870,8 @@
   pass_list.push_back(std::move(root_pass));
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, &overlay_list, nullptr,
-      &dc_layer_list, &root_damage_rect, &content_bounds_);
+      render_pass_filters, render_pass_backdrop_filters, nullptr, &overlay_list,
+      nullptr, &dc_layer_list, &root_damage_rect, &content_bounds_);
   EXPECT_EQ(0U, overlay_list.size());
   EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
 
@@ -2954,8 +2965,9 @@
     damage_rect_ = gfx::Rect(1, 1, 10, 10);
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &overlay_list,
-        nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
+        &content_bounds_);
     EXPECT_EQ(0U, overlay_list.size());
     EXPECT_EQ(1U, dc_layer_list.size());
     // Because of clip rects the overlay isn't occluded and shouldn't be an
@@ -2991,8 +3003,9 @@
     pass_list.push_back(std::move(pass));
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &overlay_list,
-        nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
+        &content_bounds_);
     EXPECT_EQ(0U, overlay_list.size());
     EXPECT_EQ(1U, dc_layer_list.size());
     EXPECT_EQ(1, dc_layer_list.back().z_order);
@@ -3034,8 +3047,9 @@
 
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, &overlay_list,
-        nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
+        &content_bounds_);
     EXPECT_EQ(0U, overlay_list.size());
     EXPECT_EQ(1U, dc_layer_list.size());
     EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
@@ -3080,7 +3094,8 @@
       return;
     }
 
-    ASSERT_EQ(2U, current_frame()->overlay_list.size());
+    ASSERT_TRUE(current_frame()->output_surface_plane.has_value());
+    ASSERT_EQ(1U, current_frame()->overlay_list.size());
     EXPECT_GE(current_frame()->overlay_list.back().resource_id, 0U);
   }
 
@@ -3389,24 +3404,24 @@
 
   DirectRenderer::DrawingFrame frame1;
   frame1.render_passes_in_draw_order = &pass_list;
-  frame1.overlay_list.resize(2);
-  frame1.overlay_list.front().use_output_surface_for_resource = true;
+  frame1.overlay_list.resize(1);
+  frame1.output_surface_plane = OverlayProcessor::OutputSurfaceOverlayPlane();
   OverlayCandidate& overlay1 = frame1.overlay_list.back();
   overlay1.resource_id = mapped_resource1;
   overlay1.plane_z_order = 1;
 
   DirectRenderer::DrawingFrame frame2;
   frame2.render_passes_in_draw_order = &pass_list;
-  frame2.overlay_list.resize(2);
-  frame2.overlay_list.front().use_output_surface_for_resource = true;
+  frame2.overlay_list.resize(1);
+  frame2.output_surface_plane = OverlayProcessor::OutputSurfaceOverlayPlane();
   OverlayCandidate& overlay2 = frame2.overlay_list.back();
   overlay2.resource_id = mapped_resource2;
   overlay2.plane_z_order = 1;
 
   DirectRenderer::DrawingFrame frame3;
   frame3.render_passes_in_draw_order = &pass_list;
-  frame3.overlay_list.resize(2);
-  frame3.overlay_list.front().use_output_surface_for_resource = true;
+  frame3.overlay_list.resize(1);
+  frame3.output_surface_plane = OverlayProcessor::OutputSurfaceOverlayPlane();
   OverlayCandidate& overlay3 = frame3.overlay_list.back();
   overlay3.resource_id = mapped_resource3;
   overlay3.plane_z_order = 1;
@@ -3577,24 +3592,24 @@
 
   DirectRenderer::DrawingFrame frame1;
   frame1.render_passes_in_draw_order = &pass_list;
-  frame1.overlay_list.resize(2);
-  frame1.overlay_list.front().use_output_surface_for_resource = true;
+  frame1.overlay_list.resize(1);
+  frame1.output_surface_plane = OverlayProcessor::OutputSurfaceOverlayPlane();
   OverlayCandidate& overlay1 = frame1.overlay_list.back();
   overlay1.resource_id = mapped_resource1;
   overlay1.plane_z_order = 1;
 
   DirectRenderer::DrawingFrame frame2;
   frame2.render_passes_in_draw_order = &pass_list;
-  frame2.overlay_list.resize(2);
-  frame2.overlay_list.front().use_output_surface_for_resource = true;
+  frame2.overlay_list.resize(1);
+  frame2.output_surface_plane = OverlayProcessor::OutputSurfaceOverlayPlane();
   OverlayCandidate& overlay2 = frame2.overlay_list.back();
   overlay2.resource_id = mapped_resource2;
   overlay2.plane_z_order = 1;
 
   DirectRenderer::DrawingFrame frame3;
   frame3.render_passes_in_draw_order = &pass_list;
-  frame3.overlay_list.resize(2);
-  frame3.overlay_list.front().use_output_surface_for_resource = true;
+  frame3.overlay_list.resize(1);
+  frame3.output_surface_plane = OverlayProcessor::OutputSurfaceOverlayPlane();
   OverlayCandidate& overlay3 = frame3.overlay_list.back();
   overlay3.resource_id = mapped_resource3;
   overlay3.plane_z_order = 1;
@@ -3674,11 +3689,12 @@
   }
 
   void ProcessForOverlays() {
-    overlay_list_ = BackbufferOverlayList(pass_);
+    overlay_list_.clear();
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list_, GetIdentityColorMatrix(),
-        render_pass_filters_, render_pass_backdrop_filters_, &overlay_list_,
-        &ca_layer_list_, nullptr, &damage_rect_, &content_bounds_);
+        render_pass_filters_, render_pass_backdrop_filters_, nullptr,
+        &overlay_list_, &ca_layer_list_, nullptr, &damage_rect_,
+        &content_bounds_);
   }
   RenderPassList pass_list_;
   RenderPass* pass_;
diff --git a/components/viz/service/display/skia_output_surface.h b/components/viz/service/display/skia_output_surface.h
index 5cb66cf..7e5023de 100644
--- a/components/viz/service/display/skia_output_surface.h
+++ b/components/viz/service/display/skia_output_surface.h
@@ -75,8 +75,10 @@
   // Swaps the current backbuffer to the screen.
   virtual void SkiaSwapBuffers(OutputSurfaceFrame frame) = 0;
 
-  // Schedule overlay planes to be displayed
-  virtual void ScheduleOverlays(OverlayCandidateList overlays) = 0;
+  // TODO(weiliangc): This API should move to OverlayProcessor.
+  // Schedule |output_surface_plane| as an overlay plane to be displayed.
+  virtual void ScheduleOutputSurfaceAsOverlay(
+      OverlayProcessor::OutputSurfaceOverlayPlane output_surface_plane) = 0;
 
   // Begin painting a render pass. This method will create a
   // SkDeferredDisplayListRecorder and return a SkCanvas of it. The SkiaRenderer
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index db42a8f..b5da41b 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -687,8 +687,10 @@
   if (use_swap_with_bounds_)
     swap_content_bounds_ = current_frame()->root_content_bounds;
 
-  skia_output_surface_->ScheduleOverlays(
-      std::move(current_frame()->overlay_list));
+  // TODO(weiliangc): Remove this once OverlayProcessor schedules overlays.
+  if (current_frame()->output_surface_plane)
+    skia_output_surface_->ScheduleOutputSurfaceAsOverlay(
+        current_frame()->output_surface_plane.value());
 }
 
 void SkiaRenderer::SwapBuffers(std::vector<ui::LatencyInfo> latency_info) {
diff --git a/components/viz/service/display_embedder/overlay_candidate_validator_android.cc b/components/viz/service/display_embedder/overlay_candidate_validator_android.cc
index 0dde343..66b6a78c2 100644
--- a/components/viz/service/display_embedder/overlay_candidate_validator_android.cc
+++ b/components/viz/service/display_embedder/overlay_candidate_validator_android.cc
@@ -30,6 +30,7 @@
 }
 
 void OverlayCandidateValidatorAndroid::CheckOverlaySupport(
+    const PrimaryPlane* primary_plane,
     OverlayCandidateList* candidates) {
   // There should only be at most a single overlay candidate: the video quad.
   // There's no check that the presented candidate is really a video frame for
diff --git a/components/viz/service/display_embedder/overlay_candidate_validator_android.h b/components/viz/service/display_embedder/overlay_candidate_validator_android.h
index b877843b..68379c47 100644
--- a/components/viz/service/display_embedder/overlay_candidate_validator_android.h
+++ b/components/viz/service/display_embedder/overlay_candidate_validator_android.h
@@ -26,7 +26,8 @@
   ~OverlayCandidateValidatorAndroid() override;
 
   void InitializeStrategies() override;
-  void CheckOverlaySupport(OverlayCandidateList* surfaces) override;
+  void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                           OverlayCandidateList* surfaces) override;
   bool AllowCALayerOverlays() const override;
   bool AllowDCLayerOverlays() const override;
   bool NeedsSurfaceOccludingDamageRect() const override;
diff --git a/components/viz/service/display_embedder/overlay_candidate_validator_mac.cc b/components/viz/service/display_embedder/overlay_candidate_validator_mac.cc
index c95051a..3e74eba 100644
--- a/components/viz/service/display_embedder/overlay_candidate_validator_mac.cc
+++ b/components/viz/service/display_embedder/overlay_candidate_validator_mac.cc
@@ -27,6 +27,7 @@
 }
 
 void OverlayCandidateValidatorMac::CheckOverlaySupport(
+    const PrimaryPlane* primary_plane,
     OverlayCandidateList* surfaces) {}
 
 }  // namespace viz
diff --git a/components/viz/service/display_embedder/overlay_candidate_validator_mac.h b/components/viz/service/display_embedder/overlay_candidate_validator_mac.h
index 7dce0433..4c6b26f 100644
--- a/components/viz/service/display_embedder/overlay_candidate_validator_mac.h
+++ b/components/viz/service/display_embedder/overlay_candidate_validator_mac.h
@@ -23,7 +23,8 @@
   bool AllowCALayerOverlays() const override;
   bool AllowDCLayerOverlays() const override;
   bool NeedsSurfaceOccludingDamageRect() const override;
-  void CheckOverlaySupport(OverlayCandidateList* surfaces) override;
+  void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                           OverlayCandidateList* surfaces) override;
 
  private:
   const bool ca_layer_disabled_;
diff --git a/components/viz/service/display_embedder/overlay_candidate_validator_ozone.cc b/components/viz/service/display_embedder/overlay_candidate_validator_ozone.cc
index bb8ce5c..13647fa8 100644
--- a/components/viz/service/display_embedder/overlay_candidate_validator_ozone.cc
+++ b/components/viz/service/display_embedder/overlay_candidate_validator_ozone.cc
@@ -12,9 +12,44 @@
 #include "components/viz/service/display/overlay_strategy_single_on_top.h"
 #include "components/viz/service/display/overlay_strategy_underlay.h"
 #include "components/viz/service/display/overlay_strategy_underlay_cast.h"
+#include "ui/gfx/geometry/rect_conversions.h"
 
 namespace viz {
 
+namespace {
+
+// TODO(weiliangc): When difference between primary plane and non-primary plane
+// can be internalized, merge these two helper functions.
+void ConvertToOzoneOverlaySurface(
+    const OverlayCandidateValidatorOzone::PrimaryPlane& primary_plane,
+    ui::OverlaySurfaceCandidate* ozone_candidate) {
+  ozone_candidate->transform = primary_plane.transform;
+  ozone_candidate->format = primary_plane.format;
+  ozone_candidate->display_rect = primary_plane.display_rect;
+  ozone_candidate->crop_rect = gfx::RectF(0.f, 0.f, 1.f, 1.f);
+  ozone_candidate->clip_rect = gfx::ToEnclosingRect(primary_plane.display_rect);
+  ozone_candidate->is_clipped = false;
+  ozone_candidate->is_opaque = !primary_plane.enable_blending;
+  ozone_candidate->plane_z_order = 0;
+  ozone_candidate->buffer_size = primary_plane.resource_size;
+}
+
+void ConvertToOzoneOverlaySurface(
+    const OverlayCandidate& overlay_candidate,
+    ui::OverlaySurfaceCandidate* ozone_candidate) {
+  ozone_candidate->transform = overlay_candidate.transform;
+  ozone_candidate->format = overlay_candidate.format;
+  ozone_candidate->display_rect = overlay_candidate.display_rect;
+  ozone_candidate->crop_rect = overlay_candidate.uv_rect;
+  ozone_candidate->clip_rect = overlay_candidate.clip_rect;
+  ozone_candidate->is_clipped = overlay_candidate.is_clipped;
+  ozone_candidate->is_opaque = overlay_candidate.is_opaque;
+  ozone_candidate->plane_z_order = overlay_candidate.plane_z_order;
+  ozone_candidate->buffer_size = overlay_candidate.resource_size_in_pixels;
+}
+
+}  // namespace
+
 // |overlay_candidates| is an object used to answer questions about possible
 // overlays configurations.
 // |available_strategies| is a list of overlay strategies that should be
@@ -64,6 +99,7 @@
 }
 
 void OverlayCandidateValidatorOzone::CheckOverlaySupport(
+    const PrimaryPlane* primary_plane,
     OverlayCandidateList* surfaces) {
   // SW mirroring copies out of the framebuffer, so we can't remove any
   // quads for overlaying, otherwise the output is incorrect.
@@ -74,29 +110,53 @@
     return;
   }
 
-  DCHECK_GE(2U, surfaces->size());
-  ui::OverlayCandidatesOzone::OverlaySurfaceCandidateList ozone_surface_list;
-  ozone_surface_list.resize(surfaces->size());
+  // This number is depended on what type of strategies we have. Currently we
+  // only overlay one video.
+  DCHECK_EQ(1U, surfaces->size());
+  auto full_size = surfaces->size();
+  if (primary_plane)
+    full_size += 1;
 
-  for (size_t i = 0; i < surfaces->size(); i++) {
-    ozone_surface_list.at(i).transform = surfaces->at(i).transform;
-    ozone_surface_list.at(i).format = surfaces->at(i).format;
-    ozone_surface_list.at(i).display_rect = surfaces->at(i).display_rect;
-    ozone_surface_list.at(i).crop_rect = surfaces->at(i).uv_rect;
-    ozone_surface_list.at(i).clip_rect = surfaces->at(i).clip_rect;
-    ozone_surface_list.at(i).is_clipped = surfaces->at(i).is_clipped;
-    ozone_surface_list.at(i).is_opaque = surfaces->at(i).is_opaque;
-    ozone_surface_list.at(i).plane_z_order = surfaces->at(i).plane_z_order;
-    ozone_surface_list.at(i).buffer_size =
-        surfaces->at(i).resource_size_in_pixels;
+  ui::OverlayCandidatesOzone::OverlaySurfaceCandidateList ozone_surface_list(
+      full_size);
+
+  // Convert OverlayCandidateList to OzoneSurfaceCandidateList.
+  {
+    auto ozone_surface_iterator = ozone_surface_list.begin();
+
+    // For ozone-cast, there will not be a primary_plane.
+    if (primary_plane) {
+      ConvertToOzoneOverlaySurface(*primary_plane, &(*ozone_surface_iterator));
+      ozone_surface_iterator++;
+    }
+
+    auto surface_iterator = surfaces->cbegin();
+    for (; ozone_surface_iterator < ozone_surface_list.end() &&
+           surface_iterator < surfaces->cend();
+         ozone_surface_iterator++, surface_iterator++) {
+      ConvertToOzoneOverlaySurface(*surface_iterator,
+                                   &(*ozone_surface_iterator));
+    }
   }
-
   overlay_candidates_->CheckOverlaySupport(&ozone_surface_list);
-  DCHECK_EQ(surfaces->size(), ozone_surface_list.size());
 
-  for (size_t i = 0; i < surfaces->size(); i++) {
-    surfaces->at(i).overlay_handled = ozone_surface_list.at(i).overlay_handled;
-    surfaces->at(i).display_rect = ozone_surface_list.at(i).display_rect;
+  // Copy information from OzoneSurfaceCandidatelist back to
+  // OverlayCandidateList.
+  {
+    DCHECK_EQ(full_size, ozone_surface_list.size());
+    auto ozone_surface_iterator = ozone_surface_list.cbegin();
+    // The primary plane is always handled, and don't need to copy information.
+    if (primary_plane)
+      ozone_surface_iterator++;
+
+    auto surface_iterator = surfaces->begin();
+    for (; surface_iterator < surfaces->end() &&
+           ozone_surface_iterator < ozone_surface_list.cend();
+         surface_iterator++, ozone_surface_iterator++) {
+      surface_iterator->overlay_handled =
+          ozone_surface_iterator->overlay_handled;
+      surface_iterator->display_rect = ozone_surface_iterator->display_rect;
+    }
   }
 }
 
diff --git a/components/viz/service/display_embedder/overlay_candidate_validator_ozone.h b/components/viz/service/display_embedder/overlay_candidate_validator_ozone.h
index 31567d4..27b44e5b 100644
--- a/components/viz/service/display_embedder/overlay_candidate_validator_ozone.h
+++ b/components/viz/service/display_embedder/overlay_candidate_validator_ozone.h
@@ -30,7 +30,8 @@
   bool AllowCALayerOverlays() const override;
   bool AllowDCLayerOverlays() const override;
   bool NeedsSurfaceOccludingDamageRect() const override;
-  void CheckOverlaySupport(OverlayCandidateList* surfaces) override;
+  void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                           OverlayCandidateList* surfaces) override;
   void SetSoftwareMirrorMode(bool enabled) override;
 
  private:
diff --git a/components/viz/service/display_embedder/overlay_candidate_validator_surface_control.cc b/components/viz/service/display_embedder/overlay_candidate_validator_surface_control.cc
index c023b161..afc8a2e5 100644
--- a/components/viz/service/display_embedder/overlay_candidate_validator_surface_control.cc
+++ b/components/viz/service/display_embedder/overlay_candidate_validator_surface_control.cc
@@ -56,23 +56,16 @@
 }
 
 void OverlayCandidateValidatorSurfaceControl::CheckOverlaySupport(
+    const PrimaryPlane* primary_plane,
     OverlayCandidateList* surfaces) {
   DCHECK(!surfaces->empty());
 
   for (auto& candidate : *surfaces) {
     if (!gl::SurfaceControl::SupportsColorSpace(candidate.color_space)) {
-      DCHECK(!candidate.use_output_surface_for_resource)
-          << "The main overlay must only use color space supported by the "
-             "device";
       candidate.overlay_handled = false;
       return;
     }
 
-    if (candidate.use_output_surface_for_resource) {
-      AdjustOutputSurfaceOverlay(&candidate);
-      continue;
-    }
-
     if (candidate.transform != display_transform_) {
       candidate.overlay_handled = false;
       return;
@@ -106,18 +99,23 @@
 }
 
 void OverlayCandidateValidatorSurfaceControl::AdjustOutputSurfaceOverlay(
-    OverlayCandidate* candidate) {
-  DCHECK_EQ(candidate->transform, gfx::OVERLAY_TRANSFORM_NONE);
-  DCHECK(!candidate->is_clipped);
-  DCHECK(candidate->display_rect == ClipFromOrigin(candidate->display_rect));
+    PrimaryPlane* output_surface_plane) {
+  DCHECK(output_surface_plane);
+  DCHECK(
+      gl::SurfaceControl::SupportsColorSpace(output_surface_plane->color_space))
+      << "The main overlay must only use color space supported by the "
+         "device";
 
-  candidate->transform = display_transform_;
+  DCHECK_EQ(output_surface_plane->transform, gfx::OVERLAY_TRANSFORM_NONE);
+  DCHECK(output_surface_plane->display_rect ==
+         ClipFromOrigin(output_surface_plane->display_rect));
+
+  output_surface_plane->transform = display_transform_;
   const gfx::Transform display_inverse = gfx::OverlayTransformToTransform(
       gfx::InvertOverlayTransform(display_transform_), viewport_size_);
-  display_inverse.TransformRect(&candidate->display_rect);
-  candidate->display_rect =
-      gfx::RectF(gfx::ToEnclosingRect(candidate->display_rect));
-  candidate->overlay_handled = true;
+  display_inverse.TransformRect(&output_surface_plane->display_rect);
+  output_surface_plane->display_rect =
+      gfx::RectF(gfx::ToEnclosingRect(output_surface_plane->display_rect));
 }
 
 void OverlayCandidateValidatorSurfaceControl::SetDisplayTransform(
@@ -133,6 +131,8 @@
 gfx::Rect
 OverlayCandidateValidatorSurfaceControl::GetOverlayDamageRectForOutputSurface(
     const OverlayCandidate& candidate) const {
+  // Should only be called after ProcessForOverlays on handled candidates.
+  DCHECK(candidate.overlay_handled);
   // When the overlay is handled by the validator, we transform its display rect
   // to the logical screen space (used by the ui when preparing the frame) that
   // the SurfaceControl expects it to be in. So in order to provide a damage
@@ -140,11 +140,6 @@
   // transformation.
   // But only if the overlay is in handled state, since the modification above
   // is only applied when we mark the overlay as handled.
-  if (!candidate.overlay_handled) {
-    return OverlayCandidateValidator::GetOverlayDamageRectForOutputSurface(
-        candidate);
-  }
-
   gfx::Size viewport_size_pre_display_transform(viewport_size_.height(),
                                                 viewport_size_.width());
   auto transform = gfx::OverlayTransformToTransform(
diff --git a/components/viz/service/display_embedder/overlay_candidate_validator_surface_control.h b/components/viz/service/display_embedder/overlay_candidate_validator_surface_control.h
index 79fc57b..da94b83 100644
--- a/components/viz/service/display_embedder/overlay_candidate_validator_surface_control.h
+++ b/components/viz/service/display_embedder/overlay_candidate_validator_surface_control.h
@@ -23,8 +23,9 @@
   bool AllowCALayerOverlays() const override;
   bool AllowDCLayerOverlays() const override;
   bool NeedsSurfaceOccludingDamageRect() const override;
-  void CheckOverlaySupport(OverlayCandidateList* surfaces) override;
-  void AdjustOutputSurfaceOverlay(OverlayCandidate* candidate) override;
+  void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                           OverlayCandidateList* surfaces) override;
+  void AdjustOutputSurfaceOverlay(PrimaryPlane* output_surface_plane) override;
   void SetDisplayTransform(gfx::OverlayTransform transform) override;
   void SetViewportSize(const gfx::Size& viewport_size) override;
   gfx::Rect GetOverlayDamageRectForOutputSurface(
diff --git a/components/viz/service/display_embedder/overlay_candidate_validator_surface_control_unittest.cc b/components/viz/service/display_embedder/overlay_candidate_validator_surface_control_unittest.cc
index 5a36faa..8ef0985 100644
--- a/components/viz/service/display_embedder/overlay_candidate_validator_surface_control_unittest.cc
+++ b/components/viz/service/display_embedder/overlay_candidate_validator_surface_control_unittest.cc
@@ -21,7 +21,7 @@
   candidates.push_back(candidate);
 
   OverlayCandidateValidatorSurfaceControl validator;
-  validator.CheckOverlaySupport(&candidates);
+  validator.CheckOverlaySupport(nullptr, &candidates);
   EXPECT_TRUE(candidates.at(0).overlay_handled);
   EXPECT_RECTF_EQ(candidates.at(0).display_rect, gfx::RectF(10.f, 10.f));
 }
@@ -38,7 +38,7 @@
   candidates.push_back(candidate);
 
   OverlayCandidateValidatorSurfaceControl validator;
-  validator.CheckOverlaySupport(&candidates);
+  validator.CheckOverlaySupport(nullptr, &candidates);
   EXPECT_TRUE(candidates.at(0).overlay_handled);
   EXPECT_RECTF_EQ(candidates.at(0).display_rect,
                   gfx::RectF(2.f, 2.f, 5.f, 5.f));
@@ -57,7 +57,7 @@
   candidates.push_back(candidate);
 
   OverlayCandidateValidatorSurfaceControl validator;
-  validator.CheckOverlaySupport(&candidates);
+  validator.CheckOverlaySupport(nullptr, &candidates);
   EXPECT_TRUE(candidates.at(0).overlay_handled);
   EXPECT_RECTF_EQ(candidates.at(0).display_rect,
                   gfx::RectF(0.f, 0.f, 8.f, 6.f));
@@ -76,7 +76,7 @@
   candidates.push_back(candidate);
 
   OverlayCandidateValidatorSurfaceControl validator;
-  validator.CheckOverlaySupport(&candidates);
+  validator.CheckOverlaySupport(nullptr, &candidates);
   EXPECT_TRUE(candidates.at(0).overlay_handled);
   EXPECT_RECTF_EQ(candidates.at(0).display_rect,
                   gfx::RectF(0.f, 0.f, 5.f, 5.f));
@@ -87,7 +87,6 @@
 TEST(OverlayCandidateValidatorSurfaceControlTest, DisplayTransformOverlay) {
   OverlayCandidate candidate;
   candidate.display_rect = gfx::RectF(10, 10, 50, 100);
-  candidate.use_output_surface_for_resource = false;
   candidate.overlay_handled = false;
 
   OverlayCandidateList candidates;
@@ -100,11 +99,11 @@
   // First use a different transform than the display transform, the overlay is
   // rejected.
   candidates.back().transform = gfx::OVERLAY_TRANSFORM_NONE;
-  validator.CheckOverlaySupport(&candidates);
+  validator.CheckOverlaySupport(nullptr, &candidates);
   EXPECT_FALSE(candidates.back().overlay_handled);
 
   candidates.back().transform = gfx::OVERLAY_TRANSFORM_ROTATE_90;
-  validator.CheckOverlaySupport(&candidates);
+  validator.CheckOverlaySupport(nullptr, &candidates);
   EXPECT_TRUE(candidates.back().overlay_handled);
   EXPECT_EQ(candidates.back().transform, gfx::OVERLAY_TRANSFORM_NONE);
   EXPECT_RECTF_EQ(candidates.back().display_rect, gfx::RectF(10, 40, 100, 50));
@@ -112,17 +111,14 @@
 
 TEST(OverlayCandidateValidatorSurfaceControlTest,
      DisplayTransformOutputSurfaceOverlay) {
-  OverlayCandidate candidate;
+  OverlayProcessor::OutputSurfaceOverlayPlane candidate;
   candidate.display_rect = gfx::RectF(100, 200);
-  candidate.use_output_surface_for_resource = true;
-  candidate.overlay_handled = false;
   candidate.transform = gfx::OVERLAY_TRANSFORM_NONE;
 
   OverlayCandidateValidatorSurfaceControl validator;
   validator.SetViewportSize(gfx::Size(100, 200));
   validator.SetDisplayTransform(gfx::OVERLAY_TRANSFORM_ROTATE_90);
   validator.AdjustOutputSurfaceOverlay(&candidate);
-  EXPECT_TRUE(candidate.overlay_handled);
   EXPECT_RECTF_EQ(candidate.display_rect, gfx::RectF(200, 100));
   EXPECT_EQ(candidate.transform, gfx::OVERLAY_TRANSFORM_ROTATE_90);
 }
@@ -130,7 +126,6 @@
 TEST(OverlayCandidateValidatorTest, OverlayDamageRectForOutputSurface) {
   OverlayCandidate candidate;
   candidate.display_rect = gfx::RectF(10, 10, 50, 100);
-  candidate.use_output_surface_for_resource = false;
   candidate.transform = gfx::OVERLAY_TRANSFORM_ROTATE_90;
   candidate.overlay_handled = false;
 
@@ -138,12 +133,9 @@
   validator.SetViewportSize(gfx::Size(100, 200));
   validator.SetDisplayTransform(gfx::OVERLAY_TRANSFORM_ROTATE_90);
 
-  EXPECT_EQ(validator.GetOverlayDamageRectForOutputSurface(candidate),
-            gfx::Rect(10, 10, 50, 100));
-
   OverlayCandidateList candidates;
   candidates.push_back(candidate);
-  validator.CheckOverlaySupport(&candidates);
+  validator.CheckOverlaySupport(nullptr, &candidates);
   EXPECT_TRUE(candidates.back().overlay_handled);
   EXPECT_RECTF_EQ(candidates.back().display_rect, gfx::RectF(10, 40, 100, 50));
   EXPECT_EQ(validator.GetOverlayDamageRectForOutputSurface(candidates.back()),
diff --git a/components/viz/service/display_embedder/overlay_candidate_validator_win.cc b/components/viz/service/display_embedder/overlay_candidate_validator_win.cc
index 5e365c1c..e8213f2 100644
--- a/components/viz/service/display_embedder/overlay_candidate_validator_win.cc
+++ b/components/viz/service/display_embedder/overlay_candidate_validator_win.cc
@@ -13,6 +13,7 @@
 OverlayCandidateValidatorWin::~OverlayCandidateValidatorWin() = default;
 
 void OverlayCandidateValidatorWin::CheckOverlaySupport(
+    const PrimaryPlane* primary_plane,
     OverlayCandidateList* candidates) {
   NOTIMPLEMENTED();
 }
diff --git a/components/viz/service/display_embedder/overlay_candidate_validator_win.h b/components/viz/service/display_embedder/overlay_candidate_validator_win.h
index 1d51a7d..f0e3972 100644
--- a/components/viz/service/display_embedder/overlay_candidate_validator_win.h
+++ b/components/viz/service/display_embedder/overlay_candidate_validator_win.h
@@ -20,7 +20,8 @@
   ~OverlayCandidateValidatorWin() override;
 
   // OverlayCandidateValidator implementation.
-  void CheckOverlaySupport(OverlayCandidateList* surfaces) override;
+  void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                           OverlayCandidateList* surfaces) override;
   bool AllowCALayerOverlays() const override;
   bool AllowDCLayerOverlays() const override;
   bool NeedsSurfaceOccludingDamageRect() const override;
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 9675d54..4272b93 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -330,13 +330,14 @@
   ScheduleGpuTask(std::move(callback), std::vector<gpu::SyncToken>());
 }
 
-void SkiaOutputSurfaceImpl::ScheduleOverlays(OverlayCandidateList overlays) {
+void SkiaOutputSurfaceImpl::ScheduleOutputSurfaceAsOverlay(
+    OverlayProcessor::OutputSurfaceOverlayPlane output_surface_plane) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // impl_on_gpu_ is released on the GPU thread by a posted task from
   // SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
-  auto callback =
-      base::BindOnce(&SkiaOutputSurfaceImplOnGpu::ScheduleOverlays,
-                     base::Unretained(impl_on_gpu_.get()), std::move(overlays));
+  auto callback = base::BindOnce(
+      &SkiaOutputSurfaceImplOnGpu::ScheduleOutputSurfaceAsOverlay,
+      base::Unretained(impl_on_gpu_.get()), std::move(output_surface_plane));
   ScheduleGpuTask(std::move(callback), std::vector<gpu::SyncToken>());
 }
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.h b/components/viz/service/display_embedder/skia_output_surface_impl.h
index 8a7eb26..0170edfc 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -90,7 +90,9 @@
       sk_sp<SkColorSpace> dst_color_space,
       bool has_alpha) override;
   void SkiaSwapBuffers(OutputSurfaceFrame frame) override;
-  void ScheduleOverlays(OverlayCandidateList overlays) override;
+  void ScheduleOutputSurfaceAsOverlay(
+      OverlayProcessor::OutputSurfaceOverlayPlane output_surface_plane)
+      override;
 
   SkCanvas* BeginPaintRenderPass(const RenderPassId& id,
                                  const gfx::Size& surface_size,
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index ed16a6d..27ca8d6 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -782,33 +782,27 @@
   ReleaseFenceSyncAndPushTextureUpdates(sync_fence_release);
 }
 
-void SkiaOutputSurfaceImplOnGpu::ScheduleOverlays(
-    const OverlayCandidateList& overlays) {
-  if (overlays.empty())
-    return;
-
+void SkiaOutputSurfaceImplOnGpu::ScheduleOutputSurfaceAsOverlay(
+    const OverlayProcessor::OutputSurfaceOverlayPlane& output_surface_plane) {
   DCHECK(!is_using_vulkan());
 
   if (!MakeCurrent(!dependency_->IsOffscreen() /* need_fbo0 */))
     return;
 
-  for (const auto& overlay_candidate : overlays) {
-    gl::GLImage* image = nullptr;
-    std::unique_ptr<gfx::GpuFence> gpu_fence;
-    if (overlay_candidate.use_output_surface_for_resource) {
-      image = output_device_->GetOverlayImage();
-      gpu_fence = output_device_->SubmitOverlayGpuFence();
-    } else {
-      NOTIMPLEMENTED_LOG_ONCE();
-    }
+  gl::GLImage* image = output_device_->GetOverlayImage();
+  std::unique_ptr<gfx::GpuFence> gpu_fence =
+      output_device_->SubmitOverlayGpuFence();
 
-    if (image) {
-      gl_surface_->ScheduleOverlayPlane(
-          overlay_candidate.plane_z_order, overlay_candidate.transform, image,
-          ToNearestRect(overlay_candidate.display_rect),
-          overlay_candidate.uv_rect, !overlay_candidate.is_opaque,
-          std::move(gpu_fence));
-    }
+  if (image) {
+    // Output surface is also z-order 0.
+    int plane_z_order = 0;
+    // Output surface always uses the full texture.
+    gfx::RectF uv_rect(0.f, 0.f, 1.f, 1.f);
+
+    gl_surface_->ScheduleOverlayPlane(
+        plane_z_order, output_surface_plane.transform, image,
+        ToNearestRect(output_surface_plane.display_rect), uv_rect,
+        output_surface_plane.enable_blending, std::move(gpu_fence));
   }
 }
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
index ca088e8e..0c1b7da 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -122,7 +122,8 @@
       uint64_t sync_fence_release,
       base::OnceClosure on_finished,
       base::Optional<gfx::Rect> draw_rectangle);
-  void ScheduleOverlays(const OverlayCandidateList& overlays);
+  void ScheduleOutputSurfaceAsOverlay(
+      const OverlayProcessor::OutputSurfaceOverlayPlane& output_surface_plane);
   void SwapBuffers(OutputSurfaceFrame frame);
   void EnsureBackbuffer() { output_device_->EnsureBackbuffer(); }
   void DiscardBackbuffer() { output_device_->DiscardBackbuffer(); }
diff --git a/components/viz/test/fake_skia_output_surface.cc b/components/viz/test/fake_skia_output_surface.cc
index 434d3814..3ec5261 100644
--- a/components/viz/test/fake_skia_output_surface.cc
+++ b/components/viz/test/fake_skia_output_surface.cc
@@ -83,7 +83,8 @@
   NOTIMPLEMENTED();
 }
 
-void FakeSkiaOutputSurface::ScheduleOverlays(OverlayCandidateList overlays) {
+void FakeSkiaOutputSurface::ScheduleOutputSurfaceAsOverlay(
+    OverlayProcessor::OutputSurfaceOverlayPlane output_surface_plane) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   NOTIMPLEMENTED();
 }
diff --git a/components/viz/test/fake_skia_output_surface.h b/components/viz/test/fake_skia_output_surface.h
index e6877e1..35cd1a5 100644
--- a/components/viz/test/fake_skia_output_surface.h
+++ b/components/viz/test/fake_skia_output_surface.h
@@ -49,7 +49,9 @@
                bool has_alpha,
                bool use_stencil) override;
   void SwapBuffers(OutputSurfaceFrame frame) override;
-  void ScheduleOverlays(OverlayCandidateList overlays) override;
+  void ScheduleOutputSurfaceAsOverlay(
+      OverlayProcessor::OutputSurfaceOverlayPlane output_surface_plane)
+      override;
   uint32_t GetFramebufferCopyTextureFormat() override;
   bool IsDisplayedAsOverlayPlane() const override;
   unsigned GetOverlayTextureId() const override;
diff --git a/content/browser/accessibility/accessibility_auralinux_browsertest.cc b/content/browser/accessibility/accessibility_auralinux_browsertest.cc
index 8fb6eee..98ff8df 100644
--- a/content/browser/accessibility/accessibility_auralinux_browsertest.cc
+++ b/content/browser/accessibility/accessibility_auralinux_browsertest.cc
@@ -17,15 +17,6 @@
 #include "content/test/content_browser_test_utils_internal.h"
 #include "ui/accessibility/platform/ax_platform_node_auralinux.h"
 
-// TODO(crbug.com/981913): This flakes on linux tsan and msan.
-#if defined(THREAD_SANITIZER) || defined(MEMORY_SANITIZER)
-#define MAYBE_TestSetCaretSetsSequentialFocusNavigationStartingPoint \
-  DISABLED_TestSetCaretSetsSequentialFocusNavigationStartingPoint
-#else
-#define MAYBE_TestSetCaretSetsSequentialFocusNavigationStartingPoint \
-  TestSetCaretSetsSequentialFocusNavigationStartingPoint
-#endif
-
 namespace content {
 
 namespace {
@@ -833,9 +824,10 @@
   g_object_unref(div);
 }
 
+// TODO(crbug.com/981913): This flakes on linux.
 IN_PROC_BROWSER_TEST_F(
     AccessibilityAuraLinuxBrowserTest,
-    MAYBE_TestSetCaretSetsSequentialFocusNavigationStartingPoint) {
+    DISABLED_TestSetCaretSetsSequentialFocusNavigationStartingPoint) {
   auto is_focused = [](AtkObject* object) {
     AtkStateSet* state_set = atk_object_ref_state_set(object);
     bool result = atk_state_set_contains_state(state_set, ATK_STATE_FOCUSED);
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 473a5207..7ffe230 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -421,7 +421,15 @@
   // Screen readers might not do the right thing if they're not aware of what
   // has focus, so always try that first. Nothing will be fired if the window
   // itself isn't focused or if focus hasn't changed.
-  FireFocusEventsIfNeeded();
+  //
+  // We need to fire focus events specifically from the root manager, since we
+  // need the top document's delegate to check if its view has focus.
+  //
+  // If this manager is disconnected from the top document, then root_manager
+  // will be a null pointer and FireFocusEventsIfNeeded won't be able to
+  // retrieve the global focus (not firing an event anyway).
+  if (root_manager)
+    root_manager->FireFocusEventsIfNeeded();
 
   bool received_load_complete_event = false;
   // Fire any events related to changes to the tree.
diff --git a/content/browser/compositor/reflector_impl_unittest.cc b/content/browser/compositor/reflector_impl_unittest.cc
index dba5880..0f7173af 100644
--- a/content/browser/compositor/reflector_impl_unittest.cc
+++ b/content/browser/compositor/reflector_impl_unittest.cc
@@ -59,7 +59,8 @@
   ~TestOverlayCandidatesOzone() override {}
 
   void CheckOverlaySupport(OverlaySurfaceCandidateList* surfaces) override {
-    (*surfaces)[0].overlay_handled = true;
+    for (auto& surface : *surfaces)
+      surface.overlay_handled = true;
   }
 };
 #endif  // defined(USE_OZONE)
@@ -191,11 +192,14 @@
   }
 
 #if defined(USE_OZONE)
-  void ProcessForOverlays(viz::OverlayCandidateList* surfaces) {
+  void ProcessForOverlays(
+      viz::OverlayProcessor::OutputSurfaceOverlayPlane* output_surface_plane,
+      viz::OverlayCandidateList* surfaces) {
     overlay_processor_->SetSoftwareMirrorMode(
         output_surface_->IsSoftwareMirrorMode());
     DCHECK(overlay_processor_->get_overlay_validator());
-    overlay_processor_->get_overlay_validator()->CheckOverlaySupport(surfaces);
+    overlay_processor_->get_overlay_validator()->CheckOverlaySupport(
+        output_surface_plane, surfaces);
   }
 #endif  // defined(USE_OZONE)
 
@@ -247,12 +251,11 @@
   if (features::IsVizDisplayCompositorEnabled())
     return;
   viz::OverlayCandidateList list;
-  viz::OverlayCandidate plane_1, plane_2;
-  plane_1.plane_z_order = 0;
-  plane_2.plane_z_order = 1;
-  list.push_back(plane_1);
-  list.push_back(plane_2);
-  ProcessForOverlays(&list);
+  viz::OverlayProcessor::OutputSurfaceOverlayPlane output_surface_plane;
+  viz::OverlayCandidate overlay_plane;
+  overlay_plane.plane_z_order = 1;
+  list.push_back(overlay_plane);
+  ProcessForOverlays(&output_surface_plane, &list);
   EXPECT_TRUE(list[0].overlay_handled);
 }
 
@@ -263,12 +266,11 @@
     return;
   SetUpReflector();
   viz::OverlayCandidateList list;
-  viz::OverlayCandidate plane_1, plane_2;
-  plane_1.plane_z_order = 0;
-  plane_2.plane_z_order = 1;
-  list.push_back(plane_1);
-  list.push_back(plane_2);
-  ProcessForOverlays(&list);
+  viz::OverlayProcessor::OutputSurfaceOverlayPlane output_surface_plane;
+  viz::OverlayCandidate overlay_plane;
+  overlay_plane.plane_z_order = 1;
+  list.push_back(overlay_plane);
+  ProcessForOverlays(&output_surface_plane, &list);
   EXPECT_FALSE(list[0].overlay_handled);
 }
 #endif  // defined(USE_OZONE)
diff --git a/content/browser/contacts/contacts_manager_impl.cc b/content/browser/contacts/contacts_manager_impl.cc
index 033b729..a75f475a 100644
--- a/content/browser/contacts/contacts_manager_impl.cc
+++ b/content/browser/contacts/contacts_manager_impl.cc
@@ -38,12 +38,14 @@
     blink::mojom::ContactsManager::SelectCallback callback,
     ukm::SourceId source_id,
     base::Optional<std::vector<blink::mojom::ContactInfoPtr>> contacts,
-    int percentage_shared) {
+    int percentage_shared,
+    int properties_requested) {
   if (contacts != base::nullopt) {
     int select_count = contacts.value().size();
     ukm::builders::ContactsPicker_ShareStatistics(source_id)
         .SetSelectCount(ukm::GetExponentialBucketMinForCounts1000(select_count))
         .SetSelectPercentage(percentage_shared)
+        .SetPropertiesRequested(properties_requested)
         .Record(ukm::UkmRecorder::Get());
   }
   std::move(callback).Run(std::move(contacts));
diff --git a/content/browser/contacts/contacts_provider.h b/content/browser/contacts/contacts_provider.h
index dd73f5e3..d6ff41309 100644
--- a/content/browser/contacts/contacts_provider.h
+++ b/content/browser/contacts/contacts_provider.h
@@ -13,7 +13,8 @@
  public:
   using ContactsSelectedCallback = base::OnceCallback<void(
       base::Optional<std::vector<blink::mojom::ContactInfoPtr>> contacts,
-      int percentage_shared)>;
+      int percentage_shared,
+      int properties_requested)>;
 
   ContactsProvider() = default;
   virtual ~ContactsProvider() = default;
diff --git a/content/browser/contacts/contacts_provider_android.cc b/content/browser/contacts/contacts_provider_android.cc
index 9749361..5975be4 100644
--- a/content/browser/contacts/contacts_provider_android.cc
+++ b/content/browser/contacts/contacts_provider_android.cc
@@ -51,7 +51,8 @@
                                      bool include_tel,
                                      ContactsSelectedCallback callback) {
   if (!dialog_) {
-    std::move(callback).Run(base::nullopt, /*percentage_shared=*/-1);
+    std::move(callback).Run(base::nullopt, /*percentage_shared=*/-1,
+                            /*properties_requested*/ -1);
     return;
   }
 
@@ -101,14 +102,17 @@
 }
 
 void ContactsProviderAndroid::EndContactsList(JNIEnv* env,
-                                              jint percentage_shared) {
+                                              jint percentage_shared,
+                                              jint properties_requested) {
   DCHECK(callback_);
-  std::move(callback_).Run(std::move(contacts_), percentage_shared);
+  std::move(callback_).Run(std::move(contacts_), percentage_shared,
+                           properties_requested);
 }
 
 void ContactsProviderAndroid::EndWithPermissionDenied(JNIEnv* env) {
   DCHECK(callback_);
-  std::move(callback_).Run(base::nullopt, /*percentage_shared=*/-1);
+  std::move(callback_).Run(base::nullopt, /*percentage_shared=*/-1,
+                           /*properties_requested*/ -1);
 }
 
 }  // namespace content
diff --git a/content/browser/contacts/contacts_provider_android.h b/content/browser/contacts/contacts_provider_android.h
index 954530b7..a7a46e0 100644
--- a/content/browser/contacts/contacts_provider_android.h
+++ b/content/browser/contacts/contacts_provider_android.h
@@ -40,8 +40,10 @@
                   const base::android::JavaParamRef<jobjectArray>& tel_java);
 
   // Signals the end of adding contacts to the list. The contact list is
-  // returned to the web page.
-  void EndContactsList(JNIEnv* env, jint percentageShared);
+  // returned to the web page, the other params are logged via UKM.
+  void EndContactsList(JNIEnv* env,
+                       jint percentage_shared,
+                       jint properties_requested);
 
   // Signals the end (due to a permission error).
   void EndWithPermissionDenied(JNIEnv* env);
diff --git a/content/browser/devtools/devtools_session.cc b/content/browser/devtools/devtools_session.cc
index d3dc240b..f6e98ab 100644
--- a/content/browser/devtools/devtools_session.cc
+++ b/content/browser/devtools/devtools_session.cc
@@ -101,6 +101,7 @@
     io_session_.reset();
     return;
   }
+
   // TODO(https://crbug.com/978694): Consider a reset flow since new mojo types
   // checks is_bound strictly.
   if (receiver_.is_bound()) {
@@ -108,11 +109,12 @@
     session_.reset();
     io_session_.reset();
   }
-  agent->AttachDevToolsSession(
-      receiver_.BindNewEndpointAndPassRemote(),
-      session_.BindNewEndpointAndPassReceiver(),
-      io_session_.BindNewPipeAndPassReceiver(), session_state_cookie_.Clone(),
-      client_->UsesBinaryProtocol(), child_session_id_);
+
+  agent->AttachDevToolsSession(receiver_.BindNewEndpointAndPassRemote(),
+                               session_.BindNewEndpointAndPassReceiver(),
+                               io_session_.BindNewPipeAndPassReceiver(),
+                               session_state_cookie_.Clone(),
+                               client_->UsesBinaryProtocol());
   session_.set_disconnect_handler(base::BindOnce(
       &DevToolsSession::MojoConnectionDestroyed, base::Unretained(this)));
 
@@ -279,18 +281,10 @@
 // the browser to the client.
 static void SendProtocolResponseOrNotification(
     DevToolsAgentHostClient* client,
-    const std::string& child_session_id,
     DevToolsAgentHostImpl* agent_host,
     std::unique_ptr<protocol::Serializable> message) {
   std::string cbor = message->serialize(/*binary=*/true);
   DCHECK(IsCBORMessage(SpanFrom(cbor)));
-  if (!child_session_id.empty()) {
-    IPEStatus status = AppendString8EntryToCBORMap(
-        SpanFrom(kSessionId), SpanFrom(child_session_id), &cbor);
-    DCHECK(status.ok()) << status.ToASCIIString();
-    if (!status.ok())
-      return;
-  }
   if (client->UsesBinaryProtocol()) {
     client->DispatchProtocolMessage(agent_host, cbor);
     return;
@@ -304,15 +298,13 @@
 void DevToolsSession::sendProtocolResponse(
     int call_id,
     std::unique_ptr<protocol::Serializable> message) {
-  SendProtocolResponseOrNotification(client_, child_session_id_, agent_host_,
-                                     std::move(message));
+  SendProtocolResponseOrNotification(client_, agent_host_, std::move(message));
   // |this| may be deleted at this point.
 }
 
 void DevToolsSession::sendProtocolNotification(
     std::unique_ptr<protocol::Serializable> message) {
-  SendProtocolResponseOrNotification(client_, child_session_id_, agent_host_,
-                                     std::move(message));
+  SendProtocolResponseOrNotification(client_, agent_host_, std::move(message));
   // |this| may be deleted at this point.
 }
 
@@ -400,7 +392,6 @@
   DCHECK(!root_session_);
   auto session = std::make_unique<DevToolsSession>(client);
   session->root_session_ = this;
-  session->child_session_id_ = session_id;
   DevToolsSession* session_ptr = session.get();
   // If attach did not succeed, |session| is already destroyed.
   if (!agent_host->AttachInternal(std::move(session)))
@@ -418,12 +409,18 @@
   if (child_sessions_.find(session_id) == child_sessions_.end())
     return;
   DCHECK(IsCBORMessage(SpanFrom(message)));
+  std::string patched(message);
+  IPEStatus status = AppendString8EntryToCBORMap(
+      SpanFrom(kSessionId), SpanFrom(session_id), &patched);
+  LOG_IF(ERROR, !status.ok()) << status.ToASCIIString();
+  if (!status.ok())
+    return;
   if (client_->UsesBinaryProtocol()) {
-    client_->DispatchProtocolMessage(agent_host_, message);
+    client_->DispatchProtocolMessage(agent_host_, patched);
     return;
   }
   std::string json;
-  IPEStatus status = ConvertCBORToJSON(SpanFrom(message), &json);
+  status = ConvertCBORToJSON(SpanFrom(patched), &json);
   LOG_IF(ERROR, !status.ok()) << status.ToASCIIString();
   client_->DispatchProtocolMessage(agent_host_, json);
   // |this| may be deleted at this point.
diff --git a/content/browser/devtools/devtools_session.h b/content/browser/devtools/devtools_session.h
index b0a6f225..0288b21 100644
--- a/content/browser/devtools/devtools_session.h
+++ b/content/browser/devtools/devtools_session.h
@@ -137,10 +137,6 @@
   blink::mojom::DevToolsSessionStatePtr session_state_cookie_;
 
   DevToolsSession* root_session_ = nullptr;
-  // If this session is a child session (root_session_ != nullptr), and we're in
-  // flatten_protocol mode (see protocol/target_handler.cc), then and only then,
-  // |child_session_id_| will contain a non-empty session id.
-  std::string child_session_id_;
   base::flat_map<std::string, DevToolsSession*> child_sessions_;
   base::OnceClosure runtime_resume_;
   DevToolsExternalAgentProxyDelegate* proxy_delegate_ = nullptr;
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index f528d88a..5890acc 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -4259,9 +4259,15 @@
       },
       base::Unretained(permission_service_context_.get())));
 
-  registry_->AddInterface(
-      base::Bind(&RenderFrameHostImpl::BindPresentationServiceRequest,
-                 base::Unretained(this)));
+  registry_->AddInterface(base::BindRepeating(
+      [](RenderFrameHostImpl* frame,
+         blink::mojom::PresentationServiceRequest request) {
+        if (!frame->presentation_service_)
+          frame->presentation_service_ = PresentationServiceImpl::Create(frame);
+
+        frame->presentation_service_->Bind(std::move(request));
+      },
+      base::Unretained(this)));
 
   registry_->AddInterface(
       base::Bind(&MediaSessionServiceImpl::Create, base::Unretained(this)));
@@ -6261,14 +6267,6 @@
 }
 #endif
 
-void RenderFrameHostImpl::BindPresentationServiceRequest(
-    blink::mojom::PresentationServiceRequest request) {
-  if (!presentation_service_)
-    presentation_service_ = PresentationServiceImpl::Create(this);
-
-  presentation_service_->Bind(std::move(request));
-}
-
 void RenderFrameHostImpl::GetIdleManager(
     mojo::PendingReceiver<blink::mojom::IdleManager> receiver) {
   if (!IsFeatureEnabled(blink::mojom::FeaturePolicyFeature::kIdleDetection)) {
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 44c320e..2de97c9 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -58,7 +58,7 @@
 #include "content/public/common/transferrable_url_loader.mojom.h"
 #include "media/mojo/mojom/interface_factory.mojom.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "net/base/network_isolation_key.h"
@@ -1513,9 +1513,6 @@
       mojo::PendingReceiver<blink::mojom::Authenticator> receiver);
 #endif
 
-  void BindPresentationServiceRequest(
-      blink::mojom::PresentationServiceRequest request);
-
   void BindSmsReceiverRequest(blink::mojom::SmsReceiverRequest request);
 
   // service_manager::mojom::InterfaceProvider:
diff --git a/content/browser/native_file_system/native_file_system_file_writer_impl.cc b/content/browser/native_file_system/native_file_system_file_writer_impl.cc
index a4bd3944..0cf815bd 100644
--- a/content/browser/native_file_system/native_file_system_file_writer_impl.cc
+++ b/content/browser/native_file_system/native_file_system_file_writer_impl.cc
@@ -11,6 +11,7 @@
 #include "content/browser/native_file_system/native_file_system_manager_impl.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/common/content_client.h"
+#include "crypto/secure_hash.h"
 #include "storage/browser/blob/blob_storage_context.h"
 #include "storage/browser/fileapi/file_system_operation_runner.h"
 #include "third_party/blink/public/mojom/blob/blob.mojom.h"
@@ -33,6 +34,33 @@
   return result;
 }
 
+std::pair<base::File::Error, std::string> ReadAndComputeSHA256Checksum(
+    const base::FilePath& path) {
+  base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+
+  if (!file.IsValid())
+    return std::make_pair(file.error_details(), std::string());
+
+  std::unique_ptr<crypto::SecureHash> hash =
+      crypto::SecureHash::Create(crypto::SecureHash::SHA256);
+  std::vector<char> buffer(8 * 1024);
+  int bytes_read = file.ReadAtCurrentPos(buffer.data(), buffer.size());
+
+  while (bytes_read > 0) {
+    hash->Update(buffer.data(), bytes_read);
+    bytes_read = file.ReadAtCurrentPos(buffer.data(), buffer.size());
+  }
+
+  // If bytes_read is -ve, it means there were issues reading from disk.
+  if (bytes_read < 0)
+    return std::make_pair(file.error_details(), std::string());
+
+  std::string hash_str(hash->GetHashLength(), 0);
+  hash->Finish(base::data(hash_str), hash_str.size());
+
+  return std::make_pair(file.error_details(), hash_str);
+}
+
 }  // namespace
 
 namespace content {
@@ -314,6 +342,20 @@
   std::move(callback).Run(native_file_system_error::Ok());
 }
 
+void NativeFileSystemFileWriterImpl::ComputeSecureHashForFile(
+    const base::FilePath& path,
+    HashCallback callback) {
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::ThreadPool(), base::MayBlock()},
+      base::BindOnce(&ReadAndComputeSHA256Checksum, path),
+      base::BindOnce(
+          [](HashCallback callback,
+             const std::pair<base::File::Error, std::string>& result) {
+            std::move(callback).Run(result.first, result.second);
+          },
+          std::move(callback)));
+}
+
 base::WeakPtr<NativeFileSystemHandleBase>
 NativeFileSystemFileWriterImpl::AsWeakPtr() {
   return weak_factory_.GetWeakPtr();
diff --git a/content/browser/native_file_system/native_file_system_file_writer_impl.h b/content/browser/native_file_system/native_file_system_file_writer_impl.h
index 8ea7562..8f817d9 100644
--- a/content/browser/native_file_system/native_file_system_file_writer_impl.h
+++ b/content/browser/native_file_system/native_file_system_file_writer_impl.h
@@ -59,10 +59,21 @@
     skip_quarantine_service_for_testing_ = true;
   }
 
+  // TODO(https://crbug.com/968556): Use swap_url_ path, instead of requiring a
+  // FilePath.
+  void compute_file_hash_for_testing(
+      const base::FilePath& path,
+      base::OnceCallback<void(base::File::Error, const std::string&)>
+          callback) {
+    ComputeSecureHashForFile(path, std::move(callback));
+  }
+
  private:
   // State that is kept for the duration of a write operation, to keep track of
   // progress until the write completes.
   struct WriteState;
+  using HashCallback = base::OnceCallback<void(base::File::Error error,
+                                               const std::string& hash)>;
 
   void WriteImpl(uint64_t offset,
                  blink::mojom::BlobPtr data,
@@ -89,6 +100,11 @@
            url().type() != storage::kFileSystemTypeNativeLocal;
   }
 
+  // TODO(https://crbug.com/968556): Integrate with Close writer flow.
+  // The path given needs to be a native local path.
+  void ComputeSecureHashForFile(const base::FilePath& path,
+                                HashCallback callback);
+
   enum class State {
     // The writer accepts write operations.
     kOpen,
diff --git a/content/browser/native_file_system/native_file_system_file_writer_impl_unittest.cc b/content/browser/native_file_system/native_file_system_file_writer_impl_unittest.cc
index e2c227c..058e6b1 100644
--- a/content/browser/native_file_system/native_file_system_file_writer_impl_unittest.cc
+++ b/content/browser/native_file_system/native_file_system_file_writer_impl_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/bind_helpers.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/guid.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
@@ -33,6 +34,10 @@
 
 namespace content {
 
+std::string GetHexEncodedString(const std::string& input) {
+  return base::HexEncode(base::as_bytes(base::make_span(input)));
+}
+
 class NativeFileSystemFileWriterImplTest : public testing::Test {
  public:
   NativeFileSystemFileWriterImplTest()
@@ -256,6 +261,76 @@
   EXPECT_EQ("", ReadFile(test_file_url_));
 }
 
+TEST_F(NativeFileSystemFileWriterImplTest, HashSimpleOK) {
+  uint64_t bytes_written;
+  NativeFileSystemStatus result = WriteSync(0, "abc", &bytes_written);
+  EXPECT_EQ(result, NativeFileSystemStatus::kOk);
+  EXPECT_EQ(bytes_written, 3u);
+
+  base::FilePath real_path = dir_.GetPath().Append(test_swap_url_.path());
+
+  base::RunLoop loop;
+  handle_->compute_file_hash_for_testing(
+      real_path, base::BindLambdaForTesting([&](base::File::Error result,
+                                                const std::string& hash_value) {
+        EXPECT_EQ(base::File::FILE_OK, result);
+        EXPECT_EQ(
+            "BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD",
+            GetHexEncodedString(hash_value));
+        loop.Quit();
+      }));
+  loop.Run();
+}
+
+TEST_F(NativeFileSystemFileWriterImplTest, HashEmptyOK) {
+  base::FilePath real_path = dir_.GetPath().Append(test_swap_url_.path());
+  base::RunLoop loop;
+  handle_->compute_file_hash_for_testing(
+      real_path, base::BindLambdaForTesting([&](base::File::Error result,
+                                                const std::string& hash_value) {
+        EXPECT_EQ(base::File::FILE_OK, result);
+        EXPECT_EQ(
+            "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
+            GetHexEncodedString(hash_value));
+        loop.Quit();
+      }));
+  loop.Run();
+}
+
+TEST_F(NativeFileSystemFileWriterImplTest, HashNonExistingFileFails) {
+  base::FilePath real_path = dir_.GetPath().AppendASCII("i_do_not_exist.txt");
+  base::RunLoop loop;
+  handle_->compute_file_hash_for_testing(
+      real_path, base::BindLambdaForTesting([&](base::File::Error result,
+                                                const std::string& hash_value) {
+        EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, result);
+        loop.Quit();
+      }));
+  loop.Run();
+}
+
+TEST_F(NativeFileSystemFileWriterImplTest, HashLargerFileOK) {
+  size_t target_size = 9 * 1024u;
+  std::string file_data(target_size, '0');
+  uint64_t bytes_written;
+  NativeFileSystemStatus result = WriteSync(0, file_data, &bytes_written);
+  EXPECT_EQ(result, NativeFileSystemStatus::kOk);
+  EXPECT_EQ(bytes_written, target_size);
+
+  base::FilePath real_path = dir_.GetPath().Append(test_swap_url_.path());
+  base::RunLoop loop;
+  handle_->compute_file_hash_for_testing(
+      real_path, base::BindLambdaForTesting([&](base::File::Error result,
+                                                const std::string& hash_value) {
+        EXPECT_EQ(base::File::FILE_OK, result);
+        EXPECT_EQ(
+            "34A82D28CB1E0BA92CADC4BE8497DC9EEA9AC4F63B9C445A9E52D298990AC491",
+            GetHexEncodedString(hash_value));
+        loop.Quit();
+      }));
+  loop.Run();
+}
+
 TEST_P(NativeFileSystemFileWriterImplWriteTest, WriteValidEmptyString) {
   uint64_t bytes_written;
   NativeFileSystemStatus result = WriteSync(0, "", &bytes_written);
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index 8adab18..ed42728 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -151,47 +151,6 @@
   std::unique_ptr<base::RunLoop> loop_;
 };
 
-// Record every WebContentsObserver's event related to navigation. The goal is
-// to check these events happen and happen in the expected right order.
-class NavigationRecorder : public WebContentsObserver {
- public:
-  explicit NavigationRecorder(WebContents* web_contents)
-      : WebContentsObserver(web_contents) {}
-
-  // WebContentsObserver implementation.
-  void DidStartNavigation(NavigationHandle* navigation_handle) override {
-    records_.push_back("start " + navigation_handle->GetURL().path());
-    WakeUp();
-  }
-  void ReadyToCommitNavigation(NavigationHandle* navigation_handle) override {
-    records_.push_back("ready-to-commit " + navigation_handle->GetURL().path());
-    WakeUp();
-  }
-  void DidFinishNavigation(NavigationHandle* navigation_handle) override {
-    records_.push_back("did-commit " + navigation_handle->GetURL().path());
-    WakeUp();
-  }
-
-  void WaitForEvents(size_t numbers_of_events) {
-    while (records_.size() < numbers_of_events) {
-      loop_.reset(new base::RunLoop);
-      loop_->Run();
-      loop_.reset();
-    }
-  }
-
-  const std::vector<std::string> records() { return records_; }
-
- private:
-  void WakeUp() {
-    if (loop_)
-      loop_->Quit();
-  }
-
-  std::unique_ptr<base::RunLoop> loop_;
-  std::vector<std::string> records_;
-};
-
 // Used to wait for an observed IPC to be received.
 class BrowserMessageObserver : public content::BrowserMessageFilter {
  public:
diff --git a/content/browser/presentation/presentation_service_impl.cc b/content/browser/presentation/presentation_service_impl.cc
index 7785c39..bb1200c3 100644
--- a/content/browser/presentation/presentation_service_impl.cc
+++ b/content/browser/presentation/presentation_service_impl.cc
@@ -65,7 +65,6 @@
       controller_delegate_(controller_delegate),
       receiver_delegate_(receiver_delegate),
       start_presentation_request_id_(kInvalidRequestId),
-      binding_(this),
       // TODO(imcheng): Consider using RenderFrameHost* directly instead of IDs.
       render_process_id_(render_frame_host->GetProcess()->GetID()),
       render_frame_id_(render_frame_host->GetRoutingID()),
@@ -117,26 +116,30 @@
 }
 
 void PresentationServiceImpl::Bind(
-    blink::mojom::PresentationServiceRequest request) {
-  binding_.Bind(std::move(request));
-  binding_.set_connection_error_handler(base::BindOnce(
+    mojo::PendingReceiver<blink::mojom::PresentationService> receiver) {
+  presentation_service_receiver_.Bind(std::move(receiver));
+  presentation_service_receiver_.set_disconnect_handler(base::BindOnce(
       &PresentationServiceImpl::OnConnectionError, base::Unretained(this)));
 }
 
 void PresentationServiceImpl::SetController(
-    blink::mojom::PresentationControllerPtr controller) {
-  if (controller_) {
+    mojo::PendingRemote<blink::mojom::PresentationController>
+        presentation_controller_remote) {
+  if (presentation_controller_remote_) {
     mojo::ReportBadMessage(
         "There can only be one PresentationController at any given time.");
     return;
   }
-  controller_ = std::move(controller);
-  controller_.set_connection_error_handler(base::BindOnce(
+
+  presentation_controller_remote_.Bind(
+      std::move(presentation_controller_remote));
+  presentation_controller_remote_.set_disconnect_handler(base::BindOnce(
       &PresentationServiceImpl::OnConnectionError, base::Unretained(this)));
 }
 
 void PresentationServiceImpl::SetReceiver(
-    blink::mojom::PresentationReceiverPtr receiver) {
+    mojo::PendingRemote<blink::mojom::PresentationReceiver>
+        presentation_receiver_remote) {
   // Presentation receiver virtual web tests (which have the flag set) has no
   // ReceiverPresentationServiceDelegate implementation.
   // TODO(imcheng): Refactor content_browser_client to return a no-op
@@ -153,13 +156,13 @@
     return;
   }
 
-  if (receiver_) {
+  if (presentation_receiver_remote_) {
     mojo::ReportBadMessage("SetReceiver can only be called once.");
     return;
   }
 
-  receiver_ = std::move(receiver);
-  receiver_.set_connection_error_handler(base::BindOnce(
+  presentation_receiver_remote_.Bind(std::move(presentation_receiver_remote));
+  presentation_receiver_remote_.set_disconnect_handler(base::BindOnce(
       &PresentationServiceImpl::OnConnectionError, base::Unretained(this)));
   receiver_delegate_->RegisterReceiverConnectionAvailableCallback(
       base::Bind(&PresentationServiceImpl::OnReceiverConnectionAvailable,
@@ -169,9 +172,9 @@
 void PresentationServiceImpl::ListenForScreenAvailability(const GURL& url) {
   DVLOG(2) << "ListenForScreenAvailability " << url.spec();
   if (!controller_delegate_ || !url.is_valid()) {
-    if (controller_) {
-      controller_->OnScreenAvailabilityUpdated(url,
-                                               ScreenAvailability::UNAVAILABLE);
+    if (presentation_controller_remote_) {
+      presentation_controller_remote_->OnScreenAvailabilityUpdated(
+          url, ScreenAvailability::UNAVAILABLE);
     }
     return;
   }
@@ -398,15 +401,15 @@
   DVLOG(2) << "PresentationServiceImpl::OnConnectionStateChanged "
            << "[presentation_id]: " << connection.id
            << " [state]: " << info.state;
-  if (!controller_)
+  if (!presentation_controller_remote_)
     return;
 
   if (info.state == PresentationConnectionState::CLOSED) {
-    controller_->OnConnectionClosed(PresentationInfo::New(connection),
-                                    info.close_reason, info.message);
+    presentation_controller_remote_->OnConnectionClosed(
+        PresentationInfo::New(connection), info.close_reason, info.message);
   } else {
-    controller_->OnConnectionStateChanged(PresentationInfo::New(connection),
-                                          info.state);
+    presentation_controller_remote_->OnConnectionStateChanged(
+        PresentationInfo::New(connection), info.state);
   }
 }
 
@@ -433,13 +436,15 @@
 // TODO(btolsch): Convert to PresentationConnectionResultPtr.
 void PresentationServiceImpl::OnReceiverConnectionAvailable(
     PresentationInfoPtr presentation_info,
-    PresentationConnectionPtr controller_connection_ptr,
-    PresentationConnectionRequest receiver_connection_request) {
+    mojo::PendingRemote<blink::mojom::PresentationConnection>
+        controller_connection_remote,
+    mojo::PendingReceiver<blink::mojom::PresentationConnection>
+        receiver_connection_receiver) {
   DVLOG(2) << "PresentationServiceImpl::OnReceiverConnectionAvailable";
 
-  receiver_->OnReceiverConnectionAvailable(
-      std::move(presentation_info), std::move(controller_connection_ptr),
-      std::move(receiver_connection_request));
+  presentation_receiver_remote_->OnReceiverConnectionAvailable(
+      std::move(presentation_info), std::move(controller_connection_remote),
+      std::move(receiver_connection_receiver));
 }
 
 void PresentationServiceImpl::DidFinishNavigation(
@@ -478,9 +483,9 @@
 
   pending_reconnect_presentation_cbs_.clear();
 
-  binding_.Close();
-  controller_.reset();
-  receiver_.reset();
+  presentation_service_receiver_.reset();
+  presentation_controller_remote_.reset();
+  presentation_receiver_remote_.reset();
 }
 
 void PresentationServiceImpl::OnDelegateDestroyed() {
@@ -493,8 +498,10 @@
 void PresentationServiceImpl::OnDefaultPresentationStarted(
     blink::mojom::PresentationConnectionResultPtr result) {
   auto presentation_info = *result->presentation_info;
-  if (controller_)
-    controller_->OnDefaultPresentationStarted(std::move(result));
+  if (presentation_controller_remote_) {
+    presentation_controller_remote_->OnDefaultPresentationStarted(
+        std::move(result));
+  }
 
   // TODO(btolsch): Remove the state-change API in favor of direct
   // PresentationConnection state use.
@@ -519,9 +526,9 @@
 
 void PresentationServiceImpl::ScreenAvailabilityListenerImpl::
     OnScreenAvailabilityChanged(blink::mojom::ScreenAvailability availability) {
-  if (service_->controller_) {
-    service_->controller_->OnScreenAvailabilityUpdated(availability_url_,
-                                                       availability);
+  if (service_->presentation_controller_remote_) {
+    service_->presentation_controller_remote_->OnScreenAvailabilityUpdated(
+        availability_url_, availability);
   }
 }
 
diff --git a/content/browser/presentation/presentation_service_impl.h b/content/browser/presentation/presentation_service_impl.h
index d4a44d1..cc9a312 100644
--- a/content/browser/presentation/presentation_service_impl.h
+++ b/content/browser/presentation/presentation_service_impl.h
@@ -22,7 +22,10 @@
 #include "content/public/browser/presentation_service_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/frame_navigate_params.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom.h"
 #include "url/gurl.h"
 
@@ -61,16 +64,17 @@
 
   ~PresentationServiceImpl() override;
 
-  // Creates a binding between this object and |request|. Note that a
-  // PresentationServiceImpl instance can be bound to multiple requests.
-  void Bind(blink::mojom::PresentationServiceRequest request);
+  // Creates a binding between this object and |receiver|. Note that a
+  // PresentationServiceImpl instance can be bound to multiple receivers.
+  void Bind(mojo::PendingReceiver<blink::mojom::PresentationService> receiver);
 
   // PresentationService implementation.
   void SetDefaultPresentationUrls(
       const std::vector<GURL>& presentation_urls) override;
-  void SetController(
-      blink::mojom::PresentationControllerPtr controller) override;
-  void SetReceiver(blink::mojom::PresentationReceiverPtr receiver) override;
+  void SetController(mojo::PendingRemote<blink::mojom::PresentationController>
+                         presentation_controller_remote) override;
+  void SetReceiver(mojo::PendingRemote<blink::mojom::PresentationReceiver>
+                       presentation_receiver_remote) override;
   void ListenForScreenAvailability(const GURL& url) override;
   void StopListeningForScreenAvailability(const GURL& url) override;
   void StartPresentation(const std::vector<GURL>& presentation_urls,
@@ -208,8 +212,10 @@
   // Calls |receiver_| to create a new PresentationConnection on receiver page.
   void OnReceiverConnectionAvailable(
       blink::mojom::PresentationInfoPtr presentation_info,
-      PresentationConnectionPtr controller_connection_ptr,
-      PresentationConnectionRequest receiver_connection_request);
+      mojo::PendingRemote<blink::mojom::PresentationConnection>
+          controller_connection_remote,
+      mojo::PendingReceiver<blink::mojom::PresentationConnection>
+          receiver_connection_receiver);
 
   // Associates a ReconnectPresentation |callback| with a unique request ID and
   // stores it in a map. Moves out |callback| object if |callback| is registered
@@ -248,10 +254,12 @@
   ReceiverPresentationServiceDelegate* receiver_delegate_;
 
   // Pointer to the PresentationController implementation in the renderer.
-  blink::mojom::PresentationControllerPtr controller_;
+  mojo::Remote<blink::mojom::PresentationController>
+      presentation_controller_remote_;
 
   // Pointer to the PresentationReceiver implementation in the renderer.
-  blink::mojom::PresentationReceiverPtr receiver_;
+  mojo::Remote<blink::mojom::PresentationReceiver>
+      presentation_receiver_remote_;
 
   std::vector<GURL> default_presentation_urls_;
 
@@ -270,8 +278,9 @@
   std::unordered_map<int, std::unique_ptr<NewPresentationCallbackWrapper>>
       pending_reconnect_presentation_cbs_;
 
-  // RAII binding of |this| to PresentationService request.
-  mojo::Binding<blink::mojom::PresentationService> binding_;
+  // RAII receiver of |this| to PresentationService request.
+  mojo::Receiver<blink::mojom::PresentationService>
+      presentation_service_receiver_{this};
 
   // ID of the RenderFrameHost this object is associated with.
   int render_process_id_;
diff --git a/content/browser/presentation/presentation_service_impl_unittest.cc b/content/browser/presentation/presentation_service_impl_unittest.cc
index 863e15e..75ee108a 100644
--- a/content/browser/presentation/presentation_service_impl_unittest.cc
+++ b/content/browser/presentation/presentation_service_impl_unittest.cc
@@ -15,6 +15,8 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/optional.h"
 #include "base/run_loop.h"
 #include "content/public/browser/presentation_request.h"
 #include "content/public/browser/presentation_service_delegate.h"
@@ -23,18 +25,19 @@
 #include "content/test/test_render_view_host.h"
 #include "content/test/test_web_contents.h"
 #include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 using blink::mojom::PresentationConnection;
 using blink::mojom::PresentationConnectionCloseReason;
 using blink::mojom::PresentationConnectionMessagePtr;
-using blink::mojom::PresentationConnectionPtr;
-using blink::mojom::PresentationConnectionPtrInfo;
 using blink::mojom::PresentationConnectionResult;
 using blink::mojom::PresentationConnectionResultPtr;
 using blink::mojom::PresentationConnectionState;
 using blink::mojom::PresentationController;
-using blink::mojom::PresentationControllerPtr;
 using blink::mojom::PresentationError;
 using blink::mojom::PresentationErrorPtr;
 using blink::mojom::PresentationErrorType;
@@ -155,10 +158,12 @@
 
 class MockPresentationReceiver : public blink::mojom::PresentationReceiver {
  public:
-  MOCK_METHOD3(OnReceiverConnectionAvailable,
-               void(PresentationInfoPtr info,
-                    PresentationConnectionPtr controller_connection,
-                    PresentationConnectionRequest receiver_connection_request));
+  MOCK_METHOD3(
+      OnReceiverConnectionAvailable,
+      void(PresentationInfoPtr info,
+           mojo::PendingRemote<PresentationConnection> controller_connection,
+           mojo::PendingReceiver<PresentationConnection>
+               presentation_receiver_receiver));
 };
 
 class MockReceiverPresentationServiceDelegate
@@ -220,10 +225,11 @@
     service_impl_.reset(new PresentationServiceImpl(
         render_frame_host, contents(), &mock_delegate_, nullptr));
 
-    PresentationControllerPtr controller_ptr;
-    controller_binding_.reset(new mojo::Binding<PresentationController>(
-        &mock_controller_, mojo::MakeRequest(&controller_ptr)));
-    service_impl_->SetController(std::move(controller_ptr));
+    mojo::PendingRemote<PresentationController> presentation_controller_remote;
+    controller_receiver_.emplace(
+        &mock_controller_,
+        presentation_controller_remote.InitWithNewPipeAndPassReceiver());
+    service_impl_->SetController(std::move(presentation_controller_remote));
 
     presentation_urls_.push_back(presentation_url1_);
     presentation_urls_.push_back(presentation_url2_);
@@ -312,7 +318,7 @@
   std::unique_ptr<PresentationServiceImpl> service_impl_;
 
   MockPresentationController mock_controller_;
-  std::unique_ptr<mojo::Binding<PresentationController>> controller_binding_;
+  base::Optional<mojo::Receiver<PresentationController>> controller_receiver_;
 
   GURL presentation_url1_;
   GURL presentation_url2_;
@@ -406,18 +412,14 @@
       });
   EXPECT_CALL(mock_delegate_, ListenForConnectionStateChange(_, _, _, _));
 
-  // Mojo requires we not send nullptr for the InterfacePtrInfo and
-  // InterfaceRequest in PresentationConnectionResult, but there's no reason to
-  // actually have them properly bound in the test.  To get around this, we
-  // create mojo pipes but bind to a nullptr for the implementation.
-  PresentationConnectionPtrInfo receiver_ptr;
-  PresentationConnectionPtr controller_ptr;
-  auto request = mojo::MakeRequest(&controller_ptr);
-  mojo::Binding<PresentationConnection> binding(
-      /** impl */ nullptr, mojo::MakeRequest(&receiver_ptr));
+  mojo::PendingRemote<PresentationConnection> presentation_connection_remote;
+  mojo::Remote<PresentationConnection> controller_remote;
+  ignore_result(
+      presentation_connection_remote.InitWithNewPipeAndPassReceiver());
   std::move(callback).Run(PresentationConnectionResult::New(
       blink::mojom::PresentationInfo::New(presentation_url2_, kPresentationId),
-      std::move(receiver_ptr), std::move(request)));
+      std::move(presentation_connection_remote),
+      controller_remote.BindNewPipeAndPassReceiver()));
   base::RunLoop().RunUntilIdle();
 }
 
@@ -504,7 +506,7 @@
       .Run(PresentationConnectionResult::New(
           blink::mojom::PresentationInfo::New(presentation_url1_,
                                               kPresentationId),
-          nullptr, nullptr));
+          mojo::NullRemote(), mojo::NullReceiver()));
   ExpectPresentationCallbackWasRun();
 }
 
@@ -552,7 +554,7 @@
       .Run(PresentationConnectionResult::New(
           blink::mojom::PresentationInfo::New(presentation_url1_,
                                               kPresentationId),
-          nullptr, nullptr));
+          mojo::NullRemote(), mojo::NullReceiver()));
   ExpectPresentationCallbackWasRun();
 }
 
@@ -615,28 +617,28 @@
       .WillOnce(SaveArg<0>(&callback));
 
   MockPresentationReceiver mock_receiver;
-  blink::mojom::PresentationReceiverPtr receiver_ptr;
-  mojo::Binding<blink::mojom::PresentationReceiver> receiver_binding(
-      &mock_receiver, mojo::MakeRequest(&receiver_ptr));
-  service_impl.controller_delegate_ = nullptr;
-  service_impl.SetReceiver(std::move(receiver_ptr));
+  mojo::Receiver<blink::mojom::PresentationReceiver>
+      presentation_receiver_receiver(&mock_receiver);
+  service_impl.SetReceiver(
+      presentation_receiver_receiver.BindNewPipeAndPassRemote());
   EXPECT_FALSE(callback.is_null());
 
   PresentationInfo expected(presentation_url1_, kPresentationId);
 
   // Client gets notified of receiver connections.
-  PresentationConnectionPtr controller_connection;
+  mojo::PendingRemote<PresentationConnection> controller_connection;
   MockPresentationConnection mock_presentation_connection;
-  mojo::Binding<PresentationConnection> connection_binding(
-      &mock_presentation_connection, mojo::MakeRequest(&controller_connection));
-  PresentationConnectionPtr receiver_connection;
+  mojo::Receiver<PresentationConnection> connection_binding(
+      &mock_presentation_connection,
+      controller_connection.InitWithNewPipeAndPassReceiver());
+  mojo::Remote<PresentationConnection> receiver_connection;
 
   EXPECT_CALL(mock_receiver,
               OnReceiverConnectionAvailable(InfoPtrEquals(expected), _, _))
       .Times(1);
   callback.Run(PresentationInfo::New(expected),
                std::move(controller_connection),
-               mojo::MakeRequest(&receiver_connection));
+               receiver_connection.BindNewPipeAndPassReceiver());
   base::RunLoop().RunUntilIdle();
 
   EXPECT_CALL(mock_receiver_delegate_, RemoveObserver(_, _)).Times(1);
@@ -654,11 +656,12 @@
               RegisterReceiverConnectionAvailableCallback(_))
       .Times(0);
 
-  PresentationControllerPtr controller_ptr;
-  controller_binding_.reset(new mojo::Binding<PresentationController>(
-      &mock_controller_, mojo::MakeRequest(&controller_ptr)));
+  mojo::PendingRemote<PresentationController> presentation_controller_remote;
+  controller_receiver_.emplace(
+      &mock_controller_,
+      presentation_controller_remote.InitWithNewPipeAndPassReceiver());
   service_impl.controller_delegate_ = nullptr;
-  service_impl.SetController(std::move(controller_ptr));
+  service_impl.SetController(std::move(presentation_controller_remote));
 
   EXPECT_CALL(mock_receiver_delegate_, Reset(_, _)).Times(0);
   service_impl.Reset();
diff --git a/content/browser/renderer_host/direct_manipulation_event_handler_win.cc b/content/browser/renderer_host/direct_manipulation_event_handler_win.cc
index bae8794..5e5d832 100644
--- a/content/browser/renderer_host/direct_manipulation_event_handler_win.cc
+++ b/content/browser/renderer_host/direct_manipulation_event_handler_win.cc
@@ -73,7 +73,8 @@
       // kScroll -> kNone, kPinch, ScrollEnd.
       // kScroll -> kFling, we don't want to end the current scroll sequence.
       if (new_gesture_state != GestureState::kFling)
-        event_target_->ApplyPanGestureScrollEnd();
+        event_target_->ApplyPanGestureScrollEnd(new_gesture_state ==
+                                                GestureState::kPinch);
       break;
     }
     case GestureState::kFling: {
diff --git a/content/browser/renderer_host/direct_manipulation_win_browsertest.cc b/content/browser/renderer_host/direct_manipulation_win_browsertest.cc
index 10cf453..1f10e20 100644
--- a/content/browser/renderer_host/direct_manipulation_win_browsertest.cc
+++ b/content/browser/renderer_host/direct_manipulation_win_browsertest.cc
@@ -220,7 +220,7 @@
   }
 
   {
-    target->ApplyPanGestureScrollEnd();
+    target->ApplyPanGestureScrollEnd(true);
     std::unique_ptr<ui::Event> event = event_logger.ReleaseLastEvent();
 
     if (GetParam()) {
@@ -229,7 +229,8 @@
       ui::ScrollEvent* scroll_event = event->AsScrollEvent();
       EXPECT_EQ(0, scroll_event->x_offset());
       EXPECT_EQ(0, scroll_event->y_offset());
-      EXPECT_EQ(ui::EventMomentumPhase::NONE, scroll_event->momentum_phase());
+      EXPECT_EQ(ui::EventMomentumPhase::BLOCKED,
+                scroll_event->momentum_phase());
       EXPECT_EQ(ui::ScrollEventPhase::kEnd, scroll_event->scroll_event_phase());
     } else {
       ASSERT_FALSE(event);
diff --git a/content/browser/renderer_host/direct_manipulation_win_unittest.cc b/content/browser/renderer_host/direct_manipulation_win_unittest.cc
index ccfa4d9..b117d14 100644
--- a/content/browser/renderer_host/direct_manipulation_win_unittest.cc
+++ b/content/browser/renderer_host/direct_manipulation_win_unittest.cc
@@ -311,7 +311,7 @@
     events_.push_back(Event(EventGesture::kFlingEnd));
   }
 
-  void ApplyPanGestureScrollEnd() override {
+  void ApplyPanGestureScrollEnd(bool tranisitioning_to_pinch) override {
     events_.push_back(Event(EventGesture::kScrollEnd));
   }
 
diff --git a/content/browser/renderer_host/embedded_frame_sink_impl.cc b/content/browser/renderer_host/embedded_frame_sink_impl.cc
index 85a822a..edaf6d1c 100644
--- a/content/browser/renderer_host/embedded_frame_sink_impl.cc
+++ b/content/browser/renderer_host/embedded_frame_sink_impl.cc
@@ -17,13 +17,13 @@
     viz::HostFrameSinkManager* host_frame_sink_manager,
     const viz::FrameSinkId& parent_frame_sink_id,
     const viz::FrameSinkId& frame_sink_id,
-    blink::mojom::EmbeddedFrameSinkClientPtr client,
+    mojo::PendingRemote<blink::mojom::EmbeddedFrameSinkClient> client,
     DestroyCallback destroy_callback)
     : host_frame_sink_manager_(host_frame_sink_manager),
-      client_(std::move(client)),
       parent_frame_sink_id_(parent_frame_sink_id),
       frame_sink_id_(frame_sink_id) {
-  client_.set_connection_error_handler(std::move(destroy_callback));
+  client_.Bind(std::move(client));
+  client_.set_disconnect_handler(std::move(destroy_callback));
   host_frame_sink_manager_->RegisterFrameSinkId(
       frame_sink_id_, this, viz::ReportFirstSurfaceActivation::kNo);
   host_frame_sink_manager_->SetFrameSinkDebugLabel(frame_sink_id_,
@@ -39,8 +39,8 @@
 }
 
 void EmbeddedFrameSinkImpl::CreateCompositorFrameSink(
-    viz::mojom::CompositorFrameSinkClientPtr client,
-    viz::mojom::CompositorFrameSinkRequest request) {
+    mojo::PendingRemote<viz::mojom::CompositorFrameSinkClient> client,
+    mojo::PendingReceiver<viz::mojom::CompositorFrameSink> receiver) {
   // We might recreate the CompositorFrameSink on context loss or GPU crash.
   // Only register frame sink hierarchy the first time.
   if (!has_created_compositor_frame_sink_) {
@@ -55,14 +55,16 @@
   }
 
   host_frame_sink_manager_->CreateCompositorFrameSink(
-      frame_sink_id_, std::move(request), std::move(client));
+      frame_sink_id_, std::move(receiver),
+      viz::mojom::CompositorFrameSinkClientPtr(std::move(client)));
 
   has_created_compositor_frame_sink_ = true;
 }
 
 void EmbeddedFrameSinkImpl::ConnectToEmbedder(
-    blink::mojom::SurfaceEmbedderRequest surface_embedder_request) {
-  client_->BindSurfaceEmbedder(std::move(surface_embedder_request));
+    mojo::PendingReceiver<blink::mojom::SurfaceEmbedder>
+        surface_embedder_receiver) {
+  client_->BindSurfaceEmbedder(std::move(surface_embedder_receiver));
 }
 
 void EmbeddedFrameSinkImpl::OnFirstSurfaceActivation(
diff --git a/content/browser/renderer_host/embedded_frame_sink_impl.h b/content/browser/renderer_host/embedded_frame_sink_impl.h
index 8935c04..51833339b 100644
--- a/content/browser/renderer_host/embedded_frame_sink_impl.h
+++ b/content/browser/renderer_host/embedded_frame_sink_impl.h
@@ -11,7 +11,9 @@
 #include "components/viz/common/surfaces/surface_info.h"
 #include "components/viz/host/host_frame_sink_client.h"
 #include "content/common/content_export.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom.h"
 
 namespace viz {
@@ -28,11 +30,12 @@
  public:
   using DestroyCallback = base::OnceCallback<void()>;
 
-  EmbeddedFrameSinkImpl(viz::HostFrameSinkManager* host_frame_sink_manager,
-                        const viz::FrameSinkId& parent_frame_sink_id,
-                        const viz::FrameSinkId& frame_sink_id,
-                        blink::mojom::EmbeddedFrameSinkClientPtr client,
-                        DestroyCallback destroy_callback);
+  EmbeddedFrameSinkImpl(
+      viz::HostFrameSinkManager* host_frame_sink_manager,
+      const viz::FrameSinkId& parent_frame_sink_id,
+      const viz::FrameSinkId& frame_sink_id,
+      mojo::PendingRemote<blink::mojom::EmbeddedFrameSinkClient> client,
+      DestroyCallback destroy_callback);
   ~EmbeddedFrameSinkImpl() override;
 
   const viz::FrameSinkId& frame_sink_id() const { return frame_sink_id_; }
@@ -42,13 +45,13 @@
 
   // Creates a CompositorFrameSink connection to FrameSinkManagerImpl.
   void CreateCompositorFrameSink(
-      viz::mojom::CompositorFrameSinkClientPtr client,
-      viz::mojom::CompositorFrameSinkRequest request);
+      mojo::PendingRemote<viz::mojom::CompositorFrameSinkClient> client,
+      mojo::PendingReceiver<viz::mojom::CompositorFrameSink> receiver);
 
-  // Establishes a connection to the emedder of this FrameSink. Allows the child
-  // to notify its embedder of its LocalSurfaceId changes.
-  void ConnectToEmbedder(
-      blink::mojom::SurfaceEmbedderRequest surface_embedder_request);
+  // Establishes a connection to the embedder of this FrameSink. Allows the
+  // child to notify its embedder of its LocalSurfaceId changes.
+  void ConnectToEmbedder(mojo::PendingReceiver<blink::mojom::SurfaceEmbedder>
+                             surface_embedder_receiver);
 
   // viz::HostFrameSinkClient implementation.
   void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override;
@@ -57,7 +60,7 @@
  private:
   viz::HostFrameSinkManager* const host_frame_sink_manager_;
 
-  blink::mojom::EmbeddedFrameSinkClientPtr client_;
+  mojo::Remote<blink::mojom::EmbeddedFrameSinkClient> client_;
 
   // Surface-related state
   const viz::FrameSinkId parent_frame_sink_id_;
diff --git a/content/browser/renderer_host/embedded_frame_sink_provider_impl.cc b/content/browser/renderer_host/embedded_frame_sink_provider_impl.cc
index 3fdb87da..ba4451ae 100644
--- a/content/browser/renderer_host/embedded_frame_sink_provider_impl.cc
+++ b/content/browser/renderer_host/embedded_frame_sink_provider_impl.cc
@@ -4,6 +4,8 @@
 
 #include "content/browser/renderer_host/embedded_frame_sink_provider_impl.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "content/browser/renderer_host/embedded_frame_sink_impl.h"
@@ -21,14 +23,14 @@
 EmbeddedFrameSinkProviderImpl::~EmbeddedFrameSinkProviderImpl() = default;
 
 void EmbeddedFrameSinkProviderImpl::Add(
-    blink::mojom::EmbeddedFrameSinkProviderRequest request) {
-  bindings_.AddBinding(this, std::move(request));
+    mojo::PendingReceiver<blink::mojom::EmbeddedFrameSinkProvider> receiver) {
+  receivers_.Add(this, std::move(receiver));
 }
 
 void EmbeddedFrameSinkProviderImpl::RegisterEmbeddedFrameSink(
     const viz::FrameSinkId& parent_frame_sink_id,
     const viz::FrameSinkId& frame_sink_id,
-    blink::mojom::EmbeddedFrameSinkClientPtr client) {
+    mojo::PendingRemote<blink::mojom::EmbeddedFrameSinkClient> client) {
   // TODO(kylechar): Kill the renderer too.
   if (parent_frame_sink_id.client_id() != renderer_client_id_) {
     DLOG(ERROR) << "Invalid parent client id " << parent_frame_sink_id;
@@ -50,8 +52,8 @@
 
 void EmbeddedFrameSinkProviderImpl::CreateCompositorFrameSink(
     const viz::FrameSinkId& frame_sink_id,
-    viz::mojom::CompositorFrameSinkClientPtr client,
-    viz::mojom::CompositorFrameSinkRequest request) {
+    mojo::PendingRemote<viz::mojom::CompositorFrameSinkClient> client,
+    mojo::PendingReceiver<viz::mojom::CompositorFrameSink> receiver) {
   // TODO(kylechar): Kill the renderer too.
   if (frame_sink_id.client_id() != renderer_client_id_) {
     DLOG(ERROR) << "Invalid client id " << frame_sink_id;
@@ -65,25 +67,29 @@
   }
 
   iter->second->CreateCompositorFrameSink(std::move(client),
-                                          std::move(request));
+                                          std::move(receiver));
 }
 
 void EmbeddedFrameSinkProviderImpl::CreateSimpleCompositorFrameSink(
     const viz::FrameSinkId& parent_frame_sink_id,
     const viz::FrameSinkId& frame_sink_id,
-    blink::mojom::EmbeddedFrameSinkClientPtr embedded_frame_sink_client,
-    viz::mojom::CompositorFrameSinkClientPtr compositor_frame_sink_client,
-    viz::mojom::CompositorFrameSinkRequest compositor_frame_sink_request) {
+    mojo::PendingRemote<blink::mojom::EmbeddedFrameSinkClient>
+        embedded_frame_sink_client,
+    mojo::PendingRemote<viz::mojom::CompositorFrameSinkClient>
+        compositor_frame_sink_client,
+    mojo::PendingReceiver<viz::mojom::CompositorFrameSink>
+        compositor_frame_sink_receiver) {
   RegisterEmbeddedFrameSink(parent_frame_sink_id, frame_sink_id,
                             std::move(embedded_frame_sink_client));
   CreateCompositorFrameSink(frame_sink_id,
                             std::move(compositor_frame_sink_client),
-                            std::move(compositor_frame_sink_request));
+                            std::move(compositor_frame_sink_receiver));
 }
 
 void EmbeddedFrameSinkProviderImpl::ConnectToEmbedder(
     const viz::FrameSinkId& child_frame_sink_id,
-    blink::mojom::SurfaceEmbedderRequest surface_embedder_request) {
+    mojo::PendingReceiver<blink::mojom::SurfaceEmbedder>
+        surface_embedder_receiver) {
   // TODO(kylechar): Kill the renderer too.
   if (child_frame_sink_id.client_id() != renderer_client_id_) {
     DLOG(ERROR) << "Invalid client id " << child_frame_sink_id;
@@ -96,7 +102,7 @@
     return;
   }
 
-  iter->second->ConnectToEmbedder(std::move(surface_embedder_request));
+  iter->second->ConnectToEmbedder(std::move(surface_embedder_receiver));
 }
 
 void EmbeddedFrameSinkProviderImpl::DestroyEmbeddedFrameSink(
diff --git a/content/browser/renderer_host/embedded_frame_sink_provider_impl.h b/content/browser/renderer_host/embedded_frame_sink_provider_impl.h
index 92628f2c..fbc93b6a 100644
--- a/content/browser/renderer_host/embedded_frame_sink_provider_impl.h
+++ b/content/browser/renderer_host/embedded_frame_sink_provider_impl.h
@@ -10,7 +10,9 @@
 #include "base/containers/flat_map.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "content/common/content_export.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom.h"
 
 namespace viz {
@@ -30,27 +32,32 @@
       uint32_t renderer_client_id);
   ~EmbeddedFrameSinkProviderImpl() override;
 
-  void Add(blink::mojom::EmbeddedFrameSinkProviderRequest request);
+  void Add(
+      mojo::PendingReceiver<blink::mojom::EmbeddedFrameSinkProvider> receiver);
 
   // blink::mojom::EmbeddedFrameSinkProvider implementation.
   void RegisterEmbeddedFrameSink(
       const viz::FrameSinkId& parent_frame_sink_id,
       const viz::FrameSinkId& frame_sink_id,
-      blink::mojom::EmbeddedFrameSinkClientPtr client) override;
+      mojo::PendingRemote<blink::mojom::EmbeddedFrameSinkClient> client)
+      override;
   void CreateCompositorFrameSink(
       const viz::FrameSinkId& frame_sink_id,
-      viz::mojom::CompositorFrameSinkClientPtr sink_client,
-      viz::mojom::CompositorFrameSinkRequest sink_request) override;
+      mojo::PendingRemote<viz::mojom::CompositorFrameSinkClient> sink_client,
+      mojo::PendingReceiver<viz::mojom::CompositorFrameSink> sink_receiver)
+      override;
   void CreateSimpleCompositorFrameSink(
       const viz::FrameSinkId& parent_frame_sink_id,
       const viz::FrameSinkId& frame_sink_id,
-      blink::mojom::EmbeddedFrameSinkClientPtr embedded_frame_sink_client,
-      viz::mojom::CompositorFrameSinkClientPtr compositor_frame_sink_client,
-      viz::mojom::CompositorFrameSinkRequest compositor_frame_sink_request)
-      override;
-  void ConnectToEmbedder(
-      const viz::FrameSinkId& child_frame_sink_id,
-      blink::mojom::SurfaceEmbedderRequest surface_embedder_request) override;
+      mojo::PendingRemote<blink::mojom::EmbeddedFrameSinkClient>
+          embedded_frame_sink_client,
+      mojo::PendingRemote<viz::mojom::CompositorFrameSinkClient>
+          compositor_frame_sink_client,
+      mojo::PendingReceiver<viz::mojom::CompositorFrameSink>
+          compositor_frame_sink_receiver) override;
+  void ConnectToEmbedder(const viz::FrameSinkId& child_frame_sink_id,
+                         mojo::PendingReceiver<blink::mojom::SurfaceEmbedder>
+                             surface_embedder_receiver) override;
 
  private:
   friend class EmbeddedFrameSinkProviderImplTest;
@@ -64,7 +71,7 @@
   // FrameSinkIds for embedded frame sinks must use the renderer client id.
   const uint32_t renderer_client_id_;
 
-  mojo::BindingSet<blink::mojom::EmbeddedFrameSinkProvider> bindings_;
+  mojo::ReceiverSet<blink::mojom::EmbeddedFrameSinkProvider> receivers_;
 
   base::flat_map<viz::FrameSinkId, std::unique_ptr<EmbeddedFrameSinkImpl>>
       frame_sink_map_;
diff --git a/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc b/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc
index d49a13d2..df76a38 100644
--- a/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc
+++ b/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc
@@ -5,6 +5,7 @@
 #include "content/browser/renderer_host/embedded_frame_sink_provider_impl.h"
 
 #include <algorithm>
+#include <memory>
 #include <utility>
 #include <vector>
 
@@ -55,20 +56,20 @@
     : public blink::mojom::EmbeddedFrameSinkClient,
       public blink::mojom::SurfaceEmbedder {
  public:
-  StubEmbeddedFrameSinkClient()
-      : surface_embedder_binding_(this), binding_(this) {}
-  ~StubEmbeddedFrameSinkClient() override {}
+  StubEmbeddedFrameSinkClient() = default;
+  ~StubEmbeddedFrameSinkClient() override = default;
 
-  blink::mojom::EmbeddedFrameSinkClientPtr GetInterfacePtr() {
-    blink::mojom::EmbeddedFrameSinkClientPtr client;
-    binding_.Bind(mojo::MakeRequest(&client));
-    binding_.set_connection_error_handler(
+  mojo::PendingRemote<blink::mojom::EmbeddedFrameSinkClient>
+  GetInterfaceRemote() {
+    mojo::PendingRemote<blink::mojom::EmbeddedFrameSinkClient> client;
+    receiver_.Bind(client.InitWithNewPipeAndPassReceiver());
+    receiver_.set_disconnect_handler(
         base::BindOnce([](bool* error_variable) { *error_variable = true; },
                        &connection_error_));
     return client;
   }
 
-  void Close() { binding_.Close(); }
+  void Close() { receiver_.reset(); }
 
   const viz::LocalSurfaceId& last_received_local_surface_id() const {
     return last_received_local_surface_id_;
@@ -79,8 +80,8 @@
  private:
   // blink::mojom::EmbeddedFrameSinkClient:
   void BindSurfaceEmbedder(
-      blink::mojom::SurfaceEmbedderRequest request) override {
-    surface_embedder_binding_.Bind(std::move(request));
+      mojo::PendingReceiver<blink::mojom::SurfaceEmbedder> receiver) override {
+    surface_embedder_receiver_.Bind(std::move(receiver));
   }
 
   // blink::mojom::SurfaceEmbedder implementation:
@@ -88,8 +89,9 @@
     last_received_local_surface_id_ = local_surface_id;
   }
 
-  mojo::Binding<blink::mojom::SurfaceEmbedder> surface_embedder_binding_;
-  mojo::Binding<blink::mojom::EmbeddedFrameSinkClient> binding_;
+  mojo::Receiver<blink::mojom::SurfaceEmbedder> surface_embedder_receiver_{
+      this};
+  mojo::Receiver<blink::mojom::EmbeddedFrameSinkClient> receiver_{this};
   viz::LocalSurfaceId last_received_local_surface_id_;
   bool connection_error_ = false;
 
@@ -166,7 +168,7 @@
   // Mimic connection from the renderer main thread to browser.
   StubEmbeddedFrameSinkClient efs_client;
   provider()->RegisterEmbeddedFrameSink(kFrameSinkParent, kFrameSinkA,
-                                        efs_client.GetInterfacePtr());
+                                        efs_client.GetInterfaceRemote());
 
   EmbeddedFrameSinkImpl* efs_impl = GetEmbeddedFrameSink(kFrameSinkA);
 
@@ -176,14 +178,15 @@
   EXPECT_THAT(GetAllCanvases(), ElementsAre(kFrameSinkA));
 
   // Mimic connection from the renderer main or worker thread to browser.
-  viz::mojom::CompositorFrameSinkPtr compositor_frame_sink;
+  mojo::Remote<viz::mojom::CompositorFrameSink> compositor_frame_sink;
   viz::MockCompositorFrameSinkClient compositor_frame_sink_client;
-  blink::mojom::SurfaceEmbedderPtr surface_embedder;
+  mojo::Remote<blink::mojom::SurfaceEmbedder> surface_embedder;
   provider()->CreateCompositorFrameSink(
-      kFrameSinkA, compositor_frame_sink_client.BindInterfacePtr(),
-      mojo::MakeRequest(&compositor_frame_sink));
+      kFrameSinkA,
+      compositor_frame_sink_client.BindInterfacePtr().PassInterface(),
+      compositor_frame_sink.BindNewPipeAndPassReceiver());
   provider()->ConnectToEmbedder(kFrameSinkA,
-                                mojo::MakeRequest(&surface_embedder));
+                                surface_embedder.BindNewPipeAndPassReceiver());
 
   // Renderer submits a CompositorFrame with |local_id|.
   const viz::LocalSurfaceId local_id(1, base::UnguessableToken::Create());
@@ -210,7 +213,7 @@
 TEST_F(EmbeddedFrameSinkProviderImplTest, ClientClosesConnection) {
   StubEmbeddedFrameSinkClient efs_client;
   provider()->RegisterEmbeddedFrameSink(kFrameSinkParent, kFrameSinkA,
-                                        efs_client.GetInterfacePtr());
+                                        efs_client.GetInterfaceRemote());
 
   RunUntilIdle();
 
@@ -231,7 +234,7 @@
 TEST_F(EmbeddedFrameSinkProviderImplTest, ProviderClosesConnections) {
   StubEmbeddedFrameSinkClient efs_client;
   provider()->RegisterEmbeddedFrameSink(kFrameSinkParent, kFrameSinkA,
-                                        efs_client.GetInterfacePtr());
+                                        efs_client.GetInterfaceRemote());
 
   RunUntilIdle();
 
@@ -259,7 +262,8 @@
   // Try to connect CompositorFrameSink without first making
   // EmbeddedFrameSink connection. This should fail.
   provider()->CreateCompositorFrameSink(
-      kFrameSinkA, compositor_frame_sink_client.BindInterfacePtr(),
+      kFrameSinkA,
+      compositor_frame_sink_client.BindInterfacePtr().PassInterface(),
       mojo::MakeRequest(&compositor_frame_sink));
 
   // The request will fail and trigger a connection error.
@@ -271,14 +275,15 @@
 TEST_F(EmbeddedFrameSinkProviderImplTest, ParentNotRegistered) {
   StubEmbeddedFrameSinkClient efs_client;
   provider()->RegisterEmbeddedFrameSink(kFrameSinkA, kFrameSinkB,
-                                        efs_client.GetInterfacePtr());
+                                        efs_client.GetInterfaceRemote());
 
   viz::mojom::CompositorFrameSinkPtr compositor_frame_sink;
   viz::MockCompositorFrameSinkClient compositor_frame_sink_client;
   // The embedder, kFrameSinkA, has already been invalidated and isn't
   // registered at this point. This request should fail.
   provider()->CreateCompositorFrameSink(
-      kFrameSinkB, compositor_frame_sink_client.BindInterfacePtr(),
+      kFrameSinkB,
+      compositor_frame_sink_client.BindInterfacePtr().PassInterface(),
       mojo::MakeRequest(&compositor_frame_sink));
 
   // The request will fail and trigger a connection error.
@@ -293,7 +298,7 @@
 
   StubEmbeddedFrameSinkClient efs_client;
   provider()->RegisterEmbeddedFrameSink(kFrameSinkParent, invalid_frame_sink_id,
-                                        efs_client.GetInterfacePtr());
+                                        efs_client.GetInterfaceRemote());
 
   RunUntilIdle();
 
@@ -310,11 +315,11 @@
        MultiHTMLCanvasElementTransferToOffscreen) {
   StubEmbeddedFrameSinkClient efs_client_a;
   provider()->RegisterEmbeddedFrameSink(kFrameSinkParent, kFrameSinkA,
-                                        efs_client_a.GetInterfacePtr());
+                                        efs_client_a.GetInterfaceRemote());
 
   StubEmbeddedFrameSinkClient efs_client_b;
   provider()->RegisterEmbeddedFrameSink(kFrameSinkParent, kFrameSinkB,
-                                        efs_client_b.GetInterfacePtr());
+                                        efs_client_b.GetInterfaceRemote());
 
   RunUntilIdle();
 
diff --git a/content/browser/renderer_host/input/mouse_wheel_phase_handler.cc b/content/browser/renderer_host/input/mouse_wheel_phase_handler.cc
index 12fe3c12..08c29b7 100644
--- a/content/browser/renderer_host/input/mouse_wheel_phase_handler.cc
+++ b/content/browser/renderer_host/input/mouse_wheel_phase_handler.cc
@@ -32,11 +32,19 @@
 
   if (has_phase) {
     if (mouse_wheel_event.phase == blink::WebMouseWheelEvent::kPhaseEnded) {
-      // Don't send the wheel end event immediately, start a timer instead to
-      // see whether momentum phase of the scrolling starts or not.
-      ScheduleMouseWheelEndDispatching(
-          should_route_event,
-          kMaximumTimeBetweenPhaseEndedAndMomentumPhaseBegan);
+      // If the momentum_phase is anything other than blocked, don't send the
+      // wheel end event immediately, start a timer instead to see whether
+      // momentum phase of the scrolling starts or not. If momentum_phase is
+      // blocked, that means that momentum scrolling events are not coming
+      // next (i.e. preparing for a pinch, maybe), so end immediately.
+      if (mouse_wheel_event.momentum_phase ==
+          blink::WebMouseWheelEvent::kPhaseBlocked) {
+        SendSyntheticWheelEventWithPhaseEnded(should_route_event);
+      } else {
+        ScheduleMouseWheelEndDispatching(
+            should_route_event,
+            max_time_between_phase_ended_and_momentum_phase_began());
+      }
     } else if (mouse_wheel_event.phase ==
                blink::WebMouseWheelEvent::kPhaseBegan) {
       // A new scrolling sequence has started, send the pending wheel end
diff --git a/content/browser/renderer_host/input/mouse_wheel_phase_handler.h b/content/browser/renderer_host/input/mouse_wheel_phase_handler.h
index b0dfd84..c7b187f 100644
--- a/content/browser/renderer_host/input/mouse_wheel_phase_handler.h
+++ b/content/browser/renderer_host/input/mouse_wheel_phase_handler.h
@@ -19,12 +19,6 @@
 constexpr base::TimeDelta kDefaultMouseWheelLatchingTransaction =
     base::TimeDelta::FromMilliseconds(500);
 
-// Maximum time that the phase handler waits for arrival of a wheel event with
-// momentum_phase = kPhaseBegan before sending its previous wheel event with
-// phase = kPhaseEnded.
-constexpr base::TimeDelta kMaximumTimeBetweenPhaseEndedAndMomentumPhaseBegan =
-    base::TimeDelta::FromMilliseconds(100);
-
 // Maximum allowed difference between coordinates of two mouse wheel events in
 // the same scroll sequence.
 const double kWheelLatchingSlopRegion = 10.0;
@@ -87,9 +81,21 @@
     return touchpad_scroll_phase_state_;
   }
 
+  // Used in testing for setting the max time to wait for momentum phase began
+  // after a scroll phase end.
+  void set_max_time_between_phase_ended_and_momentum_phase_began(
+      base::TimeDelta timeout) {
+    max_time_between_phase_ended_and_momentum_phase_began_ = timeout;
+  }
+
+  // Used get the max time to wait for a momentum scroll to begin.
+  const base::TimeDelta
+  max_time_between_phase_ended_and_momentum_phase_began() {
+    return max_time_between_phase_ended_and_momentum_phase_began_;
+  }
+
  private:
-  void SendSyntheticWheelEventWithPhaseEnded(
-      bool should_route_event);
+  void SendSyntheticWheelEventWithPhaseEnded(bool should_route_event);
   void ScheduleMouseWheelEndDispatching(bool should_route_event,
                                         const base::TimeDelta timeout);
   bool IsWithinSlopRegion(const blink::WebMouseWheelEvent& wheel_event) const;
@@ -117,6 +123,12 @@
   FirstScrollUpdateAckState first_scroll_update_ack_state_ =
       FirstScrollUpdateAckState::kNotArrived;
 
+  // Maximum time that the phase handler waits for arrival of a wheel event with
+  // momentum_phase = kPhaseBegan before sending its previous wheel event with
+  // phase = kPhaseEnded.
+  base::TimeDelta max_time_between_phase_ended_and_momentum_phase_began_ =
+      base::TimeDelta::FromMilliseconds(100);
+
   DISALLOW_COPY_AND_ASSIGN(MouseWheelPhaseHandler);
 };
 
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index a27f38c..c03a741 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1967,7 +1967,11 @@
   AddUIThreadInterface(
       registry.get(),
       base::BindRepeating(
-          &RenderProcessHostImpl::CreateEmbeddedFrameSinkProvider,
+          [](RenderProcessHostImpl* impl,
+             blink::mojom::EmbeddedFrameSinkProviderRequest request) {
+            // An implicit conversion to PendinReceiver<T> will be used below.
+            impl->CreateEmbeddedFrameSinkProvider(std::move(request));
+          },
           base::Unretained(this)));
 
   AddUIThreadInterface(
@@ -2215,7 +2219,7 @@
 }
 
 void RenderProcessHostImpl::CreateEmbeddedFrameSinkProvider(
-    blink::mojom::EmbeddedFrameSinkProviderRequest request) {
+    mojo::PendingReceiver<blink::mojom::EmbeddedFrameSinkProvider> receiver) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!embedded_frame_sink_provider_) {
     // The client id gets converted to a uint32_t in FrameSinkId.
@@ -2224,7 +2228,7 @@
         std::make_unique<EmbeddedFrameSinkProviderImpl>(
             GetHostFrameSinkManager(), renderer_client_id);
   }
-  embedded_frame_sink_provider_->Add(std::move(request));
+  embedded_frame_sink_provider_->Add(std::move(receiver));
 }
 
 void RenderProcessHostImpl::BindFrameSinkProvider(
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 99fa162..03b61a5 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -573,7 +573,7 @@
   void BindRouteProvider(mojom::RouteProviderAssociatedRequest request);
 
   void CreateEmbeddedFrameSinkProvider(
-      blink::mojom::EmbeddedFrameSinkProviderRequest request);
+      mojo::PendingReceiver<blink::mojom::EmbeddedFrameSinkProvider> receiver);
   void BindFrameSinkProvider(mojom::FrameSinkProviderRequest request);
   void BindCompositingModeReporter(
       viz::mojom::CompositingModeReporterRequest request);
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 0d73103..cc057b57 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -5563,6 +5563,81 @@
   EXPECT_EQ("MouseWheel", GetMessageNames(events));
 }
 
+TEST_F(RenderWidgetHostViewAuraTest,
+       TouchpadScrollThenPinchFiresImmediateScrollEnd) {
+  // Set the max_time_between_phase_ended_and_momentum_phase_began timer
+  // timeout to a large value to make sure that the timer is still running
+  // when the wheel event with phase == end is sent.
+  view_->event_handler()
+      ->set_max_time_between_phase_ended_and_momentum_phase_began(
+          TestTimeouts::action_max_timeout());
+
+  view_->InitAsChild(nullptr);
+  view_->Show();
+  sink_->ClearMessages();
+
+  ui::ScrollEvent begin_scroll(
+      ui::ET_SCROLL, gfx::Point(2, 2), ui::EventTimeForNow(), 0, 2, 2, 2, 2, 2,
+      ui::EventMomentumPhase::NONE, ui::ScrollEventPhase::kBegan);
+  view_->OnScrollEvent(&begin_scroll);
+  base::RunLoop().RunUntilIdle();
+
+  // If a pinch is coming next, then a ScrollEvent is created with
+  // momentum_phase == BLOCKED so that the end phase event can be dispatched
+  // immediately, rather than scheduling for later dispatch.
+  ui::ScrollEvent end_scroll_with_pinch_next(
+      ui::ET_SCROLL, gfx::Point(2, 2), ui::EventTimeForNow(), 0, 0, 0, 0, 0, 2,
+      ui::EventMomentumPhase::BLOCKED, ui::ScrollEventPhase::kEnd);
+  view_->OnScrollEvent(&end_scroll_with_pinch_next);
+  base::RunLoop().RunUntilIdle();
+
+  MockWidgetInputHandler::MessageVector events =
+      GetAndResetDispatchedMessages();
+  EXPECT_EQ("MouseWheel", GetMessageNames(events));
+  events[0]->ToEvent()->CallCallback(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+
+  events = GetAndResetDispatchedMessages();
+  EXPECT_EQ(5U, events.size());
+  EXPECT_EQ(
+      "GestureScrollBegin GestureScrollUpdate MouseWheel GestureScrollEnd "
+      "MouseWheel",
+      GetMessageNames(events));
+  EXPECT_FALSE(GetMouseWheelPhaseHandler()->HasPendingWheelEndEvent());
+
+  const WebMouseWheelEvent* wheel_event =
+      static_cast<const WebMouseWheelEvent*>(
+          events[4]->ToEvent()->Event()->web_event.get());
+  EXPECT_EQ(blink::WebMouseWheelEvent::kPhaseBlocked,
+            wheel_event->momentum_phase);
+
+  // Now, try the same thing as above, but without knowing if pinch is next.
+  ui::ScrollEvent begin_scroll2(
+      ui::ET_SCROLL, gfx::Point(2, 2), ui::EventTimeForNow(), 0, 2, 2, 2, 2, 2,
+      ui::EventMomentumPhase::NONE, ui::ScrollEventPhase::kBegan);
+  view_->OnScrollEvent(&begin_scroll2);
+  base::RunLoop().RunUntilIdle();
+
+  // If its unknown what is coming next, set the event momentum_phase to NONE.
+  // This results in the phase end event being scheduled for dispatch, but not
+  // ultimately dispatched in this test.
+  ui::ScrollEvent end_scroll_with_momentum_next_maybe(
+      ui::ET_SCROLL, gfx::Point(2, 2), ui::EventTimeForNow(), 0, 0, 0, 0, 0, 2,
+      ui::EventMomentumPhase::NONE, ui::ScrollEventPhase::kEnd);
+  view_->OnScrollEvent(&end_scroll_with_momentum_next_maybe);
+  base::RunLoop().RunUntilIdle();
+
+  events = GetAndResetDispatchedMessages();
+  EXPECT_EQ("MouseWheel", GetMessageNames(events));
+  events[0]->ToEvent()->CallCallback(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+
+  events = GetAndResetDispatchedMessages();
+  EXPECT_EQ(4U, events.size());
+  EXPECT_EQ(
+      "GestureScrollBegin GestureScrollUpdate MouseWheel GestureScrollEnd",
+      GetMessageNames(events));
+  EXPECT_TRUE(GetMouseWheelPhaseHandler()->HasPendingWheelEndEvent());
+}
+
 TEST_F(RenderWidgetHostViewAuraTest, GestureTapFromStylusHasPointerType) {
   view_->InitAsFullscreen(parent_view_);
   view_->Show();
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.h b/content/browser/renderer_host/render_widget_host_view_event_handler.h
index a12e6af..32eed089 100644
--- a/content/browser/renderer_host/render_widget_host_view_event_handler.h
+++ b/content/browser/renderer_host/render_widget_host_view_event_handler.h
@@ -162,6 +162,14 @@
     mouse_wheel_phase_handler_.set_mouse_wheel_end_dispatch_timeout(timeout);
   }
 
+  // Used in testing for setting the max time to wait for momentum phase began
+  // after a scroll phase end.
+  void set_max_time_between_phase_ended_and_momentum_phase_began(
+      base::TimeDelta timeout) {
+    mouse_wheel_phase_handler_
+        .set_max_time_between_phase_ended_and_momentum_phase_began(timeout);
+  }
+
  private:
   FRIEND_TEST_ALL_PREFIXES(InputMethodResultAuraTest,
                            FinishImeCompositionSession);
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h
index 78df5af..d583548 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.h
+++ b/content/browser/renderer_host/render_widget_host_view_mac.h
@@ -295,6 +295,14 @@
     mouse_wheel_phase_handler_.set_mouse_wheel_end_dispatch_timeout(timeout);
   }
 
+  // Used to get the max amount of time to wait after a phase end event for a
+  // momentum phase began event.
+  const base::TimeDelta
+  max_time_between_phase_ended_and_momentum_phase_began_for_test() {
+    return mouse_wheel_phase_handler_
+        .max_time_between_phase_ended_and_momentum_phase_began();
+  }
+
   // Update the size, scale factor, color profile, vsync parameters, and any
   // other properties of the NSView or its NSScreen. Propagate these to the
   // RenderWidgetHostImpl as well.
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
index ad912e4..0c97c59 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -1359,6 +1359,11 @@
   ASSERT_EQ(0U, events.size());
   DCHECK(view->HasPendingWheelEndEventForTesting());
 
+  // Get the max time here before |view| is destroyed in the
+  // ShutdownAndDestroyWidget call below.
+  const base::TimeDelta max_time_between_phase_ended_and_momentum_phase_began =
+      view->max_time_between_phase_ended_and_momentum_phase_began_for_test();
+
   host->ShutdownAndDestroyWidget(true);
 
   // Wait for the mouse_wheel_end_dispatch_timer_ to expire after host is
@@ -1369,7 +1374,7 @@
   base::RunLoop run_loop;
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE, run_loop.QuitClosure(),
-      kMaximumTimeBetweenPhaseEndedAndMomentumPhaseBegan);
+      max_time_between_phase_ended_and_momentum_phase_began);
   run_loop.Run();
 }
 
diff --git a/content/browser/sms/sms_browsertest.cc b/content/browser/sms/sms_browsertest.cc
index 2ed8736..f5b1a4d 100644
--- a/content/browser/sms/sms_browsertest.cc
+++ b/content/browser/sms/sms_browsertest.cc
@@ -95,7 +95,8 @@
 
 }  // namespace
 
-IN_PROC_BROWSER_TEST_F(SmsBrowserTest, Receive) {
+// Flaky. crbug.com/997549
+IN_PROC_BROWSER_TEST_F(SmsBrowserTest, DISABLED_Receive) {
   GURL url = GetTestUrl(nullptr, "simple_page.html");
   NavigateToURL(shell(), url);
 
@@ -130,7 +131,8 @@
   ExpectOutcomeUKM(url, blink::SMSReceiverOutcome::kSuccess);
 }
 
-IN_PROC_BROWSER_TEST_F(SmsBrowserTest, AtMostOnePendingSmsRequest) {
+// Flaky. crbug.com/997549
+IN_PROC_BROWSER_TEST_F(SmsBrowserTest, DISABLED_AtMostOnePendingSmsRequest) {
   GURL url = GetTestUrl(nullptr, "simple_page.html");
   NavigateToURL(shell(), url);
 
@@ -172,7 +174,8 @@
   ExpectOutcomeUKM(url, blink::SMSReceiverOutcome::kSuccess);
 }
 
-IN_PROC_BROWSER_TEST_F(SmsBrowserTest, Reload) {
+// Flaky. crbug.com/997549
+IN_PROC_BROWSER_TEST_F(SmsBrowserTest, DISABLED_Reload) {
   GURL url = GetTestUrl(nullptr, "simple_page.html");
   NavigateToURL(shell(), url);
 
@@ -249,7 +252,8 @@
   ExpectNoOutcomeUKM();
 }
 
-IN_PROC_BROWSER_TEST_F(SmsBrowserTest, TwoTabsSameOrigin) {
+// Flaky. crbug.com/997549
+IN_PROC_BROWSER_TEST_F(SmsBrowserTest, DISABLED_TwoTabsSameOrigin) {
   auto* provider = new NiceMock<MockSmsProvider>();
   BrowserMainLoop::GetInstance()->SetSmsProviderForTesting(
       base::WrapUnique(provider));
@@ -347,7 +351,8 @@
   ExpectOutcomeUKM(url, blink::SMSReceiverOutcome::kSuccess);
 }
 
-IN_PROC_BROWSER_TEST_F(SmsBrowserTest, TwoTabsDifferentOrigin) {
+// Flaky. crbug.com/997549
+IN_PROC_BROWSER_TEST_F(SmsBrowserTest, DISABLED_TwoTabsDifferentOrigin) {
   auto* provider = new NiceMock<MockSmsProvider>();
   BrowserMainLoop::GetInstance()->SetSmsProviderForTesting(
       base::WrapUnique(provider));
@@ -458,7 +463,8 @@
   ExpectNoOutcomeUKM();
 }
 
-IN_PROC_BROWSER_TEST_F(SmsBrowserTest, Cancels) {
+// Flaky. crbug.com/997549
+IN_PROC_BROWSER_TEST_F(SmsBrowserTest, DISABLED_Cancels) {
   GURL url = GetTestUrl(nullptr, "simple_page.html");
   NavigateToURL(shell(), url);
 
diff --git a/content/common/input/synthetic_web_input_event_builders.cc b/content/common/input/synthetic_web_input_event_builders.cc
index dafd7e1..425c65ea 100644
--- a/content/common/input/synthetic_web_input_event_builders.cc
+++ b/content/common/input/synthetic_web_input_event_builders.cc
@@ -39,7 +39,7 @@
   result.SetPositionInScreen(window_x, window_y);
   result.SetModifiers(modifiers);
   result.pointer_type = pointer_type;
-  result.id = ui::MouseEvent::kMousePointerId;
+  result.id = ui::kPointerIdMouse;
   return result;
 }
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContactsDialogHost.java b/content/public/android/java/src/org/chromium/content/browser/ContactsDialogHost.java
index 51e79e9..62f2ec1c 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContactsDialogHost.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContactsDialogHost.java
@@ -82,11 +82,12 @@
     }
 
     @Override
-    public void onContactsPickerUserAction(
-            @ContactsPickerAction int action, List<Contact> contacts, int percentageShared) {
+    public void onContactsPickerUserAction(@ContactsPickerAction int action, List<Contact> contacts,
+            int percentageShared, int propertiesRequested) {
         switch (action) {
             case ContactsPickerAction.CANCEL:
-                ContactsDialogHostJni.get().endContactsList(mNativeContactsProviderAndroid, 0);
+                ContactsDialogHostJni.get().endContactsList(
+                        mNativeContactsProviderAndroid, 0, propertiesRequested);
                 break;
 
             case ContactsPickerAction.CONTACTS_SELECTED:
@@ -104,7 +105,7 @@
                                     : null);
                 }
                 ContactsDialogHostJni.get().endContactsList(
-                        mNativeContactsProviderAndroid, percentageShared);
+                        mNativeContactsProviderAndroid, percentageShared, propertiesRequested);
                 break;
 
             case ContactsPickerAction.SELECT_ALL:
@@ -118,7 +119,8 @@
         void addContact(long nativeContactsProviderAndroid, boolean includeNames,
                 boolean includeEmails, boolean includeTel, String[] names, String[] emails,
                 String[] tel);
-        void endContactsList(long nativeContactsProviderAndroid, int percentageShared);
+        void endContactsList(
+                long nativeContactsProviderAndroid, int percentageShared, int propertiesRequested);
         void endWithPermissionDenied(long nativeContactsProviderAndroid);
     }
 }
diff --git a/content/public/browser/presentation_service_delegate.h b/content/public/browser/presentation_service_delegate.h
index c1621465..a7b337b 100644
--- a/content/public/browser/presentation_service_delegate.h
+++ b/content/public/browser/presentation_service_delegate.h
@@ -13,6 +13,8 @@
 #include "base/callback.h"
 #include "content/common/content_export.h"
 #include "media/base/flinging_controller.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom.h"
 
 namespace content {
@@ -46,14 +48,10 @@
 using PresentationConnectionStateChangedCallback =
     base::RepeatingCallback<void(const PresentationConnectionStateChangeInfo&)>;
 
-using PresentationConnectionPtr = blink::mojom::PresentationConnectionPtr;
-using PresentationConnectionRequest =
-    blink::mojom::PresentationConnectionRequest;
-
-using ReceiverConnectionAvailableCallback =
-    base::RepeatingCallback<void(blink::mojom::PresentationInfoPtr,
-                                 PresentationConnectionPtr,
-                                 PresentationConnectionRequest)>;
+using ReceiverConnectionAvailableCallback = base::RepeatingCallback<void(
+    blink::mojom::PresentationInfoPtr,
+    mojo::PendingRemote<blink::mojom::PresentationConnection>,
+    mojo::PendingReceiver<blink::mojom::PresentationConnection>)>;
 
 // Base class for ControllerPresentationServiceDelegate and
 // ReceiverPresentationServiceDelegate.
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 1cc3e1d..562f1cb 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -3145,6 +3145,9 @@
       network::mojom::URLLoaderFactoryParams::New();
   url_loader_factory_params->process_id = process_id;
   url_loader_factory_params->is_corb_enabled = false;
+  url::Origin origin = url::Origin::Create(url);
+  url_loader_factory_params->network_isolation_key =
+      net::NetworkIsolationKey(origin, origin);
   network_context->CreateURLLoaderFactory(MakeRequest(&url_loader_factory),
                                           std::move(url_loader_factory_params));
   // |url_loader_factory| will receive error notification asynchronously if
diff --git a/content/renderer/media/webrtc/mock_peer_connection_impl.cc b/content/renderer/media/webrtc/mock_peer_connection_impl.cc
index d2882c8..0e7060c 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_impl.cc
+++ b/content/renderer/media/webrtc/mock_peer_connection_impl.cc
@@ -512,14 +512,9 @@
   remote_desc_.reset(desc);
 }
 
-bool MockPeerConnectionImpl::SetConfiguration(
-    const RTCConfiguration& configuration,
-    webrtc::RTCError* error) {
-  if (setconfiguration_error_type_ == webrtc::RTCErrorType::NONE) {
-    return true;
-  }
-  error->set_type(setconfiguration_error_type_);
-  return false;
+webrtc::RTCError MockPeerConnectionImpl::SetConfiguration(
+    const RTCConfiguration& configuration) {
+  return webrtc::RTCError(setconfiguration_error_type_);
 }
 
 bool MockPeerConnectionImpl::AddIceCandidate(
diff --git a/content/renderer/media/webrtc/mock_peer_connection_impl.h b/content/renderer/media/webrtc/mock_peer_connection_impl.h
index 00690293..dff3766a 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_impl.h
+++ b/content/renderer/media/webrtc/mock_peer_connection_impl.h
@@ -326,8 +326,9 @@
     NOTIMPLEMENTED();
     return webrtc::PeerConnectionInterface::RTCConfiguration();
   }
-  bool SetConfiguration(const RTCConfiguration& configuration,
-                        webrtc::RTCError* error) override;
+  webrtc::RTCError SetConfiguration(
+      const RTCConfiguration& configuration) override;
+
   bool AddIceCandidate(const webrtc::IceCandidateInterface* candidate) override;
   bool RemoveIceCandidates(
       const std::vector<cricket::Candidate>& candidates) override {
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index 4bba903..497d0cbd 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -1450,13 +1450,8 @@
   if (peer_connection_tracker_)
     peer_connection_tracker_->TrackSetConfiguration(this, new_configuration);
 
-  webrtc::RTCError webrtc_error;
-  bool ret = native_peer_connection_->SetConfiguration(new_configuration,
-                                                       &webrtc_error);
-  // The boolean return value is made redundant by the error output param; just
-  // DCHECK that they're consistent.
-  DCHECK_EQ(ret, webrtc_error.type() == webrtc::RTCErrorType::NONE);
-
+  webrtc::RTCError webrtc_error =
+      native_peer_connection_->SetConfiguration(new_configuration);
   if (webrtc_error.ok()) {
     configuration_ = new_configuration;
   }
diff --git a/content/test/data/accessibility/html/figcaption-expected-uia-win.txt b/content/test/data/accessibility/html/figcaption-expected-uia-win.txt
index aeb5424..2ad5236 100644
--- a/content/test/data/accessibility/html/figcaption-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/figcaption-expected-uia-win.txt
@@ -1,5 +1,5 @@
 document
 ++group Name='Fig.1 - A green Box'
 ++++img Name='This is a green box.'
-++++group
-++++++description Name='Fig.1 - A green Box'
+++++description ControlType='UIA_TextControlTypeId'
+++++++description ControlType='UIA_TextControlTypeId' Name='Fig.1 - A green Box'
diff --git a/content/test/data/accessibility/html/figcaption.html b/content/test/data/accessibility/html/figcaption.html
index f53674b..32d3e19 100644
--- a/content/test/data/accessibility/html/figcaption.html
+++ b/content/test/data/accessibility/html/figcaption.html
@@ -1,3 +1,6 @@
+<!--
+@UIA-WIN-ALLOW:ControlType='UIA_TextControlTypeId'
+-->
 <!DOCTYPE html>
 <html>
 <body>
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 5de0983..61f4399 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -605,86 +605,26 @@
 
 # Linux AMD only.
 # It looks like AMD shader compiler rejects many valid ES3 semantics.
-crbug.com/844311 [ linux amd ] conformance/glsl/misc/fragcolor-fragdata-invariant.html [ Failure ]
 crbug.com/766776 [ linux amd ] conformance2/attribs/gl-vertex-attrib-normalized-int.html [ Failure ]
 crbug.com/981070 [ linux amd ] conformance2/glsl3/matrix-row-major.html [ Failure ]
 crbug.com/709351 [ linux amd ] conformance2/glsl3/vector-dynamic-indexing-swizzled-lvalue.html [ Failure ]
-crbug.com/617290 [ linux amd ] deqp/functional/gles3/multisample.html [ Failure ]
 crbug.com/483282 [ linux amd ] deqp/data/gles3/shaders/conversions.html [ Failure ]
 crbug.com/483282 [ linux amd ] deqp/data/gles3/shaders/arrays.html [ Skip ]
 crbug.com/483282 [ linux amd ] deqp/data/gles3/shaders/qualification_order.html [ Skip ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/internalformatquery.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturestatequery.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/buffercopy.html [ Failure ]
 crbug.com/483282 [ linux amd ] deqp/functional/gles3/samplerobject.html [ Failure ]
 crbug.com/483282 [ linux amd ] deqp/functional/gles3/shaderprecision_int.html [ Failure ]
-crbug.com/606114 [ linux amd ] deqp/functional/gles3/texturefiltering/3d* [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/shadertexturefunction/texture.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/shadertexturefunction/texturegrad.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/shadertexturefunction/texelfetchoffset.html [ Failure ]
 crbug.com/694877 [ linux amd ] deqp/functional/gles3/vertexarrays/single_attribute.first.html [ Failure ]
 crbug.com/483282 [ linux amd ] deqp/functional/gles3/negativetextureapi.html [ Failure ]
 crbug.com/483282 [ linux amd ] deqp/functional/gles3/transformfeedback/array_separate* [ Failure ]
-crbug.com/483282 [ linux no-passthrough amd ] conformance2/misc/uninitialized-test-2.html [ Failure ]
-crbug.com/483282 [ linux amd ] conformance2/reading/read-pixels-from-fbo-test.html [ Failure ]
 crbug.com/634525 [ linux amd ] conformance2/rendering/blitframebuffer-filter-srgb.html [ Failure ]
 crbug.com/662644 [ linux amd ] conformance2/rendering/blitframebuffer-outside-readbuffer.html [ Failure ]
-crbug.com/295792 [ linux amd ] conformance2/renderbuffers/framebuffer-texture-layer.html [ Failure ]
 crbug.com/483282 [ linux amd ] conformance2/textures/misc/tex-mipmap-levels.html [ Failure ]
-crbug.com/483282 [ linux amd ] conformance2/textures/misc/copy-texture-image-luma-format.html [ Failure ]
 crbug.com/899754 [ linux amd ] conformance2/vertex_arrays/vertex-array-object-and-disabled-attributes.html [ Failure ]
 crbug.com/911216 [ linux no-passthrough amd ] conformance2/textures/misc/copy-texture-image-same-texture.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/teximage2d_pbo_cube_00.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/teximage2d_pbo_cube_01.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/teximage2d_pbo_cube_02.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/teximage2d_pbo_cube_03.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/teximage2d_pbo_cube_04.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/teximage2d_pbo_params.html [ Failure ]
 crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/teximage2d_depth_pbo.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/basic_copyteximage2d.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/basic_teximage3d_3d_00.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/basic_teximage3d_3d_01.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/basic_teximage3d_3d_02.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/basic_teximage3d_3d_03.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/basic_teximage3d_3d_04.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/texstorage2d_format_depth_stencil.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/texstorage3d_format_2d_array_00.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/texstorage3d_format_2d_array_01.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/texstorage3d_format_2d_array_02.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/texstorage3d_format_3d_00.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/texstorage3d_format_3d_01.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/texstorage3d_format_3d_02.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/texstorage3d_format_3d_03.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/texstorage3d_format_depth_stencil.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/texstorage3d_format_size.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/vertexarrays/single_attribute.output_type.unsigned_int.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/draw/* [ Failure ]
+crbug.com/483282 [ linux amd ] deqp/functional/gles3/draw/random.html [ Failure ]
 crbug.com/483282 [ linux amd ] deqp/functional/gles3/fbomultisample* [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/fbocompleteness.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/textureshadow/* [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/shadermatrix/mul_dynamic_highp.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/shadermatrix/mul_dynamic_lowp.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/shadermatrix/mul_dynamic_mediump.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/shadermatrix/pre_decrement.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_04.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_07.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_08.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_10.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_11.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_12.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_13.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_18.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_25.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_28.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_29.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_30.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_31.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_32.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_33.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_34.html [ Failure ]
 crbug.com/658832 [ linux amd ] deqp/functional/gles3/framebufferblit/default_framebuffer_00.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/shaderoperator/unary_operator_01.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/shaderoperator/unary_operator_02.html [ Failure ]
 crbug.com/483282 [ linux amd ] conformance2/glsl3/vector-dynamic-indexing.html [ Failure ]
 crbug.com/483282 [ no-angle linux amd ] conformance2/reading/read-pixels-pack-parameters.html [ Failure ]
 crbug.com/483282 [ no-angle linux amd ] conformance2/textures/misc/tex-unpack-params.html [ Failure ]
@@ -696,38 +636,12 @@
 crbug.com/705865 [ linux amd ] conformance2/textures/image/tex-2d-r11f_g11f_b10f-rgb-float.html [ Failure ]
 
 # Uniform buffer related failures
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/uniformbuffers/single_struct_array.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/uniformbuffers/single_nested_struct.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/uniformbuffers/single_nested_struct_array.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/uniformbuffers/multi_basic_types.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/uniformbuffers/multi_nested_struct.html [ Failure ]
 crbug.com/483282 [ linux amd ] deqp/functional/gles3/uniformbuffers/random.html [ Failure ]
-crbug.com/658842 [ linux amd ] conformance2/buffers/uniform-buffers.html [ Failure ]
-crbug.com/658844 [ linux amd ] conformance2/rendering/uniform-block-buffer-size.html [ Failure ]
 crbug.com/angleproject/2103 [ linux amd ] conformance2/uniforms/uniform-blocks-with-arrays.html [ Failure ]
 crbug.com/809595 [ no-angle linux amd ] conformance2/uniforms/simple-buffer-change.html [ Failure ]
 
 # Linux AMD R7 240
-crbug.com/710392 [ linux amd-0x6613 ] conformance2/textures/canvas/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ Failure ]
-crbug.com/710392 [ linux amd-0x6613 ] conformance2/textures/canvas/tex-2d-rgb8ui-rgb_integer-unsigned_byte.html [ Failure ]
-crbug.com/710392 [ linux amd-0x6613 ] conformance2/textures/canvas/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
-crbug.com/710392 [ linux amd-0x6613 ] conformance2/textures/webgl_canvas/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ Failure ]
-crbug.com/710392 [ linux amd-0x6613 ] conformance2/textures/webgl_canvas/tex-2d-rgb8ui-rgb_integer-unsigned_byte.html [ Failure ]
-crbug.com/710392 [ linux amd-0x6613 ] conformance2/textures/webgl_canvas/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
-crbug.com/701138 [ linux amd-0x6613 ] conformance2/textures/image_bitmap_from_video/tex-2d-rgba16f-rgba-float.html [ Failure ]
-crbug.com/701138 [ linux amd-0x6613 ] conformance2/textures/image_bitmap_from_video/tex-2d-rgba16f-rgba-half_float.html [ Failure ]
-crbug.com/701138 [ linux amd-0x6613 ] conformance2/textures/image_bitmap_from_video/tex-2d-rgba32f-rgba-float.html [ Failure ]
-crbug.com/701138 [ linux amd-0x6613 ] conformance2/textures/image_bitmap_from_video/tex-2d-rgba4-rgba-unsigned_byte.html [ Failure ]
-crbug.com/701138 [ linux amd-0x6613 ] conformance2/textures/image_bitmap_from_video/tex-2d-rgba4-rgba-unsigned_short_4_4_4_4.html [ Failure ]
-crbug.com/847217 [ linux amd-0x6613 ] conformance2/textures/image_bitmap_from_video/tex-3d-rgb10_a2-rgba-unsigned_int_2_10_10_10_rev.html [ Failure ]
-crbug.com/847217 [ linux amd-0x6613 ] conformance2/textures/video/tex-2d-rg32f-rg-float.html [ Failure ]
-crbug.com/701138 [ linux amd-0x6613 ] conformance2/textures/image_data/tex-3d-rgb32f-rgb-float.html [ Failure ]
-crbug.com/701138 [ linux amd-0x6613 ] conformance2/textures/image_data/tex-3d-rgb565-rgb-unsigned_byte.html [ Failure ]
-crbug.com/701138 [ linux amd-0x6613 ] conformance2/textures/image_data/tex-3d-rgb565-rgb-unsigned_short_5_6_5.html [ Failure ]
-crbug.com/701138 [ linux amd-0x6613 ] conformance2/textures/image_data/tex-3d-rgb5_a1-rgba-unsigned_byte.html [ Failure ]
-crbug.com/832864 [ no-angle linux amd-0x6613 ] conformance2/textures/misc/tex-image-with-bad-args-from-dom-elements.html [ Failure ]
 crbug.com/696345 [ no-angle linux amd-0x6613 ] conformance2/transform_feedback/switching-objects.html [ Failure ]
-crbug.com/851159 [ linux amd-0x6613 ] conformance2/buffers/get-buffer-sub-data-validity.html [ Failure ]
 crbug.com/913301 [ linux amd-0x6613 ] conformance2/textures/misc/generate-mipmap-with-large-base-level.html [ Failure ]
 crbug.com/809237 [ linux amd-0x6613 ] conformance2/uniforms/incompatible-texture-type-for-sampler.html [ Skip ]
 
diff --git a/device/base/features.cc b/device/base/features.cc
index bf3708f..93d6545 100644
--- a/device/base/features.cc
+++ b/device/base/features.cc
@@ -19,11 +19,6 @@
 // Enables or disables the use of newblue Bluetooth daemon on Chrome OS.
 const base::Feature kNewblueDaemon{"Newblue",
                                    base::FEATURE_DISABLED_BY_DEFAULT};
-// Shows all Bluetooth devices in UI (System Tray/Settings Page).
-// Needed for working on the early integration with NewBlue.
-// TODO(crbug.com/862492): Remove this feature once NewBlue gets stable.
-const base::Feature kUnfilteredBluetoothDevices{
-    "UnfilteredBluetoothDevices", base::FEATURE_DISABLED_BY_DEFAULT};
 #endif  // defined(OS_CHROMEOS)
 
 #if BUILDFLAG(ENABLE_VR)
diff --git a/device/base/features.h b/device/base/features.h
index e4e4525..86385a4 100644
--- a/device/base/features.h
+++ b/device/base/features.h
@@ -19,7 +19,6 @@
 
 #if defined(OS_CHROMEOS)
 DEVICE_BASE_EXPORT extern const base::Feature kNewblueDaemon;
-DEVICE_BASE_EXPORT extern const base::Feature kUnfilteredBluetoothDevices;
 #endif  // defined(OS_CHROMEOS)
 
 #if BUILDFLAG(ENABLE_VR)
diff --git a/device/bluetooth/chromeos/bluetooth_utils.cc b/device/bluetooth/chromeos/bluetooth_utils.cc
index d2f55eab..3a5a84d 100644
--- a/device/bluetooth/chromeos/bluetooth_utils.cc
+++ b/device/bluetooth/chromeos/bluetooth_utils.cc
@@ -15,6 +15,7 @@
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
 #include "chromeos/constants/chromeos_features.h"
+#include "chromeos/constants/chromeos_switches.h"
 #include "device/base/features.h"
 
 namespace device {
@@ -64,7 +65,7 @@
 // Filter out unknown devices from the list.
 BluetoothAdapter::DeviceList FilterUnknownDevices(
     const BluetoothAdapter::DeviceList& devices) {
-  if (base::FeatureList::IsEnabled(device::kUnfilteredBluetoothDevices))
+  if (chromeos::switches::IsUnfilteredBluetoothDevicesEnabled())
     return devices;
 
   BluetoothAdapter::DeviceList result;
diff --git a/gpu/vulkan/generate_bindings.py b/gpu/vulkan/generate_bindings.py
index 5d91c6da..679e6c9 100755
--- a/gpu/vulkan/generate_bindings.py
+++ b/gpu/vulkan/generate_bindings.py
@@ -64,6 +64,13 @@
     ]
   },
   {
+    'ifdef': 'defined(OS_FUCHSIA)',
+    'extension': 'VK_FUCHSIA_IMAGEPIPE_SURFACE_EXTENSION_NAME',
+    'functions': [
+      'vkCreateImagePipeSurfaceFUCHSIA',
+    ]
+  },
+  {
     # vkGetPhysicalDeviceFeatures2() is defined in Vulkan 1.1 or suffixed in the
     # VK_KHR_get_physical_device_properties2 extension.
     'min_api_version': 'VK_VERSION_1_1',
@@ -278,6 +285,10 @@
 #endif
 
 #if defined(OS_FUCHSIA)
+#include <zircon/types.h>
+// <vulkan/vulkan_fuchsia.h> must be included after <zircon/types.h>
+#include <vulkan/vulkan_fuchsia.h>
+
 #include "gpu/vulkan/fuchsia/vulkan_fuchsia_ext.h"
 #endif
 
diff --git a/gpu/vulkan/vulkan_function_pointers.cc b/gpu/vulkan/vulkan_function_pointers.cc
index 7c22f7e..f772a8f 100644
--- a/gpu/vulkan/vulkan_function_pointers.cc
+++ b/gpu/vulkan/vulkan_function_pointers.cc
@@ -231,6 +231,21 @@
   }
 #endif  // defined(OS_ANDROID)
 
+#if defined(OS_FUCHSIA)
+  if (gfx::HasExtension(enabled_extensions,
+                        VK_FUCHSIA_IMAGEPIPE_SURFACE_EXTENSION_NAME)) {
+    vkCreateImagePipeSurfaceFUCHSIAFn =
+        reinterpret_cast<PFN_vkCreateImagePipeSurfaceFUCHSIA>(
+            vkGetInstanceProcAddrFn(vk_instance,
+                                    "vkCreateImagePipeSurfaceFUCHSIA"));
+    if (!vkCreateImagePipeSurfaceFUCHSIAFn) {
+      DLOG(WARNING) << "Failed to bind vulkan entrypoint: "
+                    << "vkCreateImagePipeSurfaceFUCHSIA";
+      return false;
+    }
+  }
+#endif  // defined(OS_FUCHSIA)
+
   if (api_version >= VK_VERSION_1_1) {
     vkGetPhysicalDeviceFeatures2Fn =
         reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2>(
diff --git a/gpu/vulkan/vulkan_function_pointers.h b/gpu/vulkan/vulkan_function_pointers.h
index 33e579c4..f27e00d 100644
--- a/gpu/vulkan/vulkan_function_pointers.h
+++ b/gpu/vulkan/vulkan_function_pointers.h
@@ -23,6 +23,10 @@
 #endif
 
 #if defined(OS_FUCHSIA)
+#include <zircon/types.h>
+// <vulkan/vulkan_fuchsia.h> must be included after <zircon/types.h>
+#include <vulkan/vulkan_fuchsia.h>
+
 #include "gpu/vulkan/fuchsia/vulkan_fuchsia_ext.h"
 #endif
 
@@ -98,6 +102,11 @@
   PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHRFn = nullptr;
 #endif  // defined(OS_ANDROID)
 
+#if defined(OS_FUCHSIA)
+  PFN_vkCreateImagePipeSurfaceFUCHSIA vkCreateImagePipeSurfaceFUCHSIAFn =
+      nullptr;
+#endif  // defined(OS_FUCHSIA)
+
   PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2Fn = nullptr;
 
   // Device functions
@@ -247,6 +256,11 @@
   gpu::GetVulkanFunctionPointers()->vkCreateAndroidSurfaceKHRFn
 #endif  // defined(OS_ANDROID)
 
+#if defined(OS_FUCHSIA)
+#define vkCreateImagePipeSurfaceFUCHSIA \
+  gpu::GetVulkanFunctionPointers()->vkCreateImagePipeSurfaceFUCHSIAFn
+#endif  // defined(OS_FUCHSIA)
+
 #define vkGetPhysicalDeviceFeatures2 \
   gpu::GetVulkanFunctionPointers()->vkGetPhysicalDeviceFeatures2Fn
 
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_manager_client.mm b/ios/chrome/browser/passwords/ios_chrome_password_manager_client.mm
index 9627ee73..a29038c 100644
--- a/ios/chrome/browser/passwords/ios_chrome_password_manager_client.mm
+++ b/ios/chrome/browser/passwords/ios_chrome_password_manager_client.mm
@@ -66,8 +66,8 @@
   saving_passwords_enabled_.Init(
       password_manager::prefs::kCredentialsEnableService, GetPrefs());
   static base::NoDestructor<password_manager::StoreMetricsReporter> reporter(
-      *saving_passwords_enabled_, this, GetSyncService(delegate_.browserState),
-      GetIdentityManager(), GetPrefs());
+      this, GetSyncService(delegate_.browserState), GetIdentityManager(),
+      GetPrefs());
   log_manager_ = autofill::LogManager::Create(
       ios::PasswordManagerLogRouterFactory::GetForBrowserState(
           delegate_.browserState),
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.mm
index 6730895..7a5ad32 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.mm
@@ -6,6 +6,7 @@
 
 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_cell.h"
 #import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestion_identifier.h"
+#import "ios/chrome/common/colors/semantic_color_names.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -57,7 +58,7 @@
 // Configures the |textLabel|.
 - (void)configureTextLabel:(UILabel*)textLabel {
   textLabel.text = self.text;
-  textLabel.textColor = [UIColor colorWithWhite:0.13 alpha:1];
+  textLabel.textColor = [UIColor colorNamed:kTextPrimaryColor];
   textLabel.font =
       [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
   textLabel.adjustsFontForContentSizeCategory = YES;
@@ -67,7 +68,7 @@
 // Configures the |detailTextLabel|.
 - (void)configureDetailTextLabel:(UILabel*)detailTextLabel {
   detailTextLabel.text = self.detailText;
-  detailTextLabel.textColor = [UIColor colorWithWhite:0.62 alpha:1];
+  detailTextLabel.textColor = [UIColor colorNamed:kTextSecondaryColor];
   detailTextLabel.font =
       [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
   detailTextLabel.adjustsFontForContentSizeCategory = YES;
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
index fcc2775..68d6891 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
@@ -11,6 +11,7 @@
 
 #include "base/bind.h"
 #include "base/ios/ios_util.h"
+#include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #import "base/test/ios/wait_util.h"
@@ -117,14 +118,21 @@
 
 // Scroll to the top of the Reading List.
 void ScrollToTop() {
-  NSError* error = nil;
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID([
-                                          [ReadingListTableViewController class]
-                                          accessibilityIdentifier])]
-      performAction:grey_scrollToContentEdgeWithStartPoint(kGREYContentEdgeTop,
-                                                           0.5, 0.5)
-              error:&error];
-  [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
+  // On iOS 13 the settings menu appears as a card that can be dismissed with a
+  // downward swipe, for this reason we need to swipe up programatically to
+  // avoid dismissing the VC.
+  GREYPerformBlock scrollToTopBlock =
+      ^BOOL(id element, __strong NSError** error) {
+        UIScrollView* view = base::mac::ObjCCastStrict<UIScrollView>(element);
+        view.contentOffset = CGPointZero;
+        return YES;
+      };
+
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
+                                          [[ReadingListTableViewController
+                                              class] accessibilityIdentifier])]
+      performAction:[GREYActionBlock actionWithName:@"Scroll to top"
+                                       performBlock:scrollToTopBlock]];
 }
 
 // Asserts that the "mark" toolbar button is visible and has the a11y label of
@@ -976,6 +984,8 @@
     EARL_GREY_TEST_SKIPPED(@"Test disabled on when feature flag is off.");
   }
 
+  GetReadingListModel()->AddEntry(GURL(kUnreadURL), std::string(kUnreadTitle),
+                                  reading_list::ADDED_VIA_CURRENT_APP);
   OpenReadingList();
 
   // Check that the TableView is presented.
diff --git a/ios/chrome/browser/ui/table_view/feature_flags.mm b/ios/chrome/browser/ui/table_view/feature_flags.mm
index 9f18f73b..ca3913a 100644
--- a/ios/chrome/browser/ui/table_view/feature_flags.mm
+++ b/ios/chrome/browser/ui/table_view/feature_flags.mm
@@ -9,7 +9,7 @@
 #endif
 
 const base::Feature kCollectionsCardPresentationStyle{
-    "CollectionsCardPresentationStyle", base::FEATURE_DISABLED_BY_DEFAULT};
+    "CollectionsCardPresentationStyle", base::FEATURE_ENABLED_BY_DEFAULT};
 
 bool IsCollectionsCardPresentationStyleEnabled() {
   return base::FeatureList::IsEnabled(kCollectionsCardPresentationStyle);
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 3de23b6..2e331a09 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -431,7 +431,7 @@
 
 // Enable picture in picture web api for android.
 const base::Feature kPictureInPictureAPI{"PictureInPictureAPI",
-                                         base::FEATURE_DISABLED_BY_DEFAULT};
+                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables CanPlayType() (and other queries) for HLS MIME types. Note that
 // disabling this also causes navigation to .m3u8 files to trigger downloading
diff --git a/mojo/public/cpp/bindings/unique_associated_receiver_set.h b/mojo/public/cpp/bindings/unique_associated_receiver_set.h
index cabffa4..8fe1007a 100644
--- a/mojo/public/cpp/bindings/unique_associated_receiver_set.h
+++ b/mojo/public/cpp/bindings/unique_associated_receiver_set.h
@@ -6,18 +6,12 @@
 #define MOJO_PUBLIC_CPP_BINDINGS_UNIQUE_ASSOCIATED_RECEIVER_SET_H_
 
 #include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/associated_receiver_set.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/unique_ptr_impl_ref_traits.h"
 
 namespace mojo {
 
-template <typename Interface, typename ImplRefTraits>
-struct ReceiverSetTraits<AssociatedReceiver<Interface, ImplRefTraits>> {
-  using InterfaceType = Interface;
-  using PendingType = PendingAssociatedReceiver<Interface>;
-  using ImplPointerType = typename ImplRefTraits::PointerType;
-};
-
 // This class manages a set of associated receiving endpoints where each
 // endpoint is bound to a unique implementation of the interface owned by this
 // object. That is to say, for every bound AssociatedReceiver<T> in the set,
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py b/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
index 948ba3d..de7d3b2 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
@@ -4,9 +4,14 @@
 
 # Based on third_party/WebKit/Source/build/scripts/template_expander.py.
 
+import codecs
 import os.path
 import sys
 
+# Cache the ascii codec to ensure pickle can find it at jinja2 import time.
+# See https://crbug.com/997598#c5 for context.
+codecs.lookup('ascii')
+
 _current_dir = os.path.dirname(os.path.realpath(__file__))
 # jinja2 is in chromium's third_party directory
 # Insert at front to override system libraries, and after path[0] == script dir
diff --git a/net/data/proxy_resolver_v8_unittest/pac_library_unittest.js b/net/data/proxy_resolver_v8_unittest/pac_library_unittest.js
index a904a72..a8f27f69 100644
--- a/net/data/proxy_resolver_v8_unittest/pac_library_unittest.js
+++ b/net/data/proxy_resolver_v8_unittest/pac_library_unittest.js
@@ -189,10 +189,25 @@
   t.expectTrue(isInNetEx("3ffe:8311:ffff:abcd:1234:dead:beef:101",
                          "3ffe:8311:ffff::/48"));
 
-  // IPv4 and IPv6 mix.
-  t.expectFalse(isInNetEx("127.0.0.1", "0:0:0:0:0:0:7f00:1/16"));
+  // Test an IPv4 literal against an IPv6 range. This passes since 127.0.0.1 is
+  // ::ffff:127.0.0.1 in IPv4 mapped notation.
+  t.expectTrue(isInNetEx("127.0.0.1", "0:0:0:0:0:0:7f00:1/16"));
+
+  // Test an IPv4 literal against an IPv6 range. Doesn't match when
+  // 192.168.24.3 is mapped to IPv6.
   t.expectFalse(isInNetEx("192.168.24.3", "fe80:0:0:0:0:0:c0a8:1803/32"));
 
+  // Test that IPv4 ranges work interchangeably with IPv4 mapped IPv6 literals
+  // - both for the range prefix and the test address.
+  t.expectTrue(isInNetEx("::ffff:192.168.100.5", "192.168.1.1/16"));
+  t.expectFalse(isInNetEx("::ffff:10.168.100.5", "192.168.1.1/16"));
+  t.expectFalse(isInNetEx("::fffe:192.168.100.5", "192.168.1.1/16"));
+  t.expectTrue(isInNetEx("::ffff:192.168.100.5", "::ffff:192.168.1.1/112"));
+  t.expectFalse(isInNetEx("::ffff:10.168.100.5", "::ffff:192.168.1.1/112"));
+  t.expectTrue(isInNetEx("192.168.1.1", "::ffff:192.168.1.1/112"));
+  t.expectFalse(isInNetEx("192.168.1.1", "::fffe:192.168.1.1/112"));
+  t.expectFalse(isInNetEx("10.168.1.1", "::ffff:192.168.1.1/112"));
+
   t.expectFalse(isInNetEx("198.95.249.78", "198.95.249.79/32"));
   t.expectFalse(isInNetEx("198.96.115.10", "198.95.0.0/16"));
   t.expectFalse(isInNetEx("3fff:8311:ffff:abcd:1234:dead:beef:101",
diff --git a/net/proxy_resolution/proxy_resolver_v8.cc b/net/proxy_resolution/proxy_resolver_v8.cc
index 2ac6072..f32d0fa 100644
--- a/net/proxy_resolution/proxy_resolver_v8.cc
+++ b/net/proxy_resolution/proxy_resolver_v8.cc
@@ -322,8 +322,9 @@
 // slash-delimited IP prefix with the top 'n' bits specified in the bit
 // field. This returns 'true' if the address is in the same subnet, and
 // 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
-// format, or if an address and prefix of different types are used (e.g. IPv6
-// address and IPv4 prefix).
+// format. If the address types of |ip_address| and |ip_prefix| don't match,
+// will promote the IPv4 literal to an IPv4 mapped IPv6 literal and
+// proceed with the comparison.
 bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
   IPAddress address;
   if (!address.AssignFromIPLiteral(ip_address))
@@ -334,13 +335,6 @@
   if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
     return false;
 
-  // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
-  if (address.size() != prefix.size())
-    return false;
-
-  DCHECK((address.IsIPv4() && prefix.IsIPv4()) ||
-         (address.IsIPv6() && prefix.IsIPv6()));
-
   return IPAddressMatchesPrefix(address, prefix, prefix_length_in_bits);
 }
 
diff --git a/net/quic/platform/impl/quic_flags_test.cc b/net/quic/platform/impl/quic_flags_test.cc
index f6bd2ccb8..0a77e8d 100644
--- a/net/quic/platform/impl/quic_flags_test.cc
+++ b/net/quic/platform/impl/quic_flags_test.cc
@@ -182,7 +182,9 @@
   EXPECT_THAT(captured_stdout, testing::ContainsRegex("--baz +Silence again."));
 }
 
-TEST(QuicFlagsTest, SetQuicFlagByName_bool) {
+class QuicFlagsTest : public QuicTest {};
+
+TEST_F(QuicFlagsTest, SetQuicFlagByName_bool) {
   FLAGS_quic_enforce_single_packet_chlo = true;
   SetQuicFlagByName("FLAGS_quic_enforce_single_packet_chlo", "false");
   EXPECT_FALSE(FLAGS_quic_enforce_single_packet_chlo);
@@ -194,61 +196,61 @@
   EXPECT_TRUE(FLAGS_quic_enforce_single_packet_chlo);
 }
 
-TEST(QuicFlagsTest, SetQuicFlagByName_double) {
+TEST_F(QuicFlagsTest, SetQuicFlagByName_double) {
   FLAGS_quic_bbr_cwnd_gain = 3.0;
   SetQuicFlagByName("FLAGS_quic_bbr_cwnd_gain", "1.5");
   EXPECT_EQ(1.5, FLAGS_quic_bbr_cwnd_gain);
 }
 
-TEST(QuicFlagsTest, SetQuicFlagByName_double_invalid) {
+TEST_F(QuicFlagsTest, SetQuicFlagByName_double_invalid) {
   FLAGS_quic_bbr_cwnd_gain = 3.0;
   SetQuicFlagByName("FLAGS_quic_bbr_cwnd_gain", "true");
   EXPECT_EQ(3.0, FLAGS_quic_bbr_cwnd_gain);
 }
 
-TEST(QuicFlagsTest, SetQuicFlagByName_int64_t) {
+TEST_F(QuicFlagsTest, SetQuicFlagByName_int64_t) {
   FLAGS_quic_max_tracked_packet_count = 100;
   SetQuicFlagByName("FLAGS_quic_max_tracked_packet_count", "5");
   EXPECT_EQ(5, FLAGS_quic_max_tracked_packet_count);
 }
 
-TEST(QuicFlagsTest, SetQuicFlagByName_int64_t_invalid) {
+TEST_F(QuicFlagsTest, SetQuicFlagByName_int64_t_invalid) {
   FLAGS_quic_max_tracked_packet_count = 100;
   SetQuicFlagByName("FLAGS_quic_max_tracked_packet_count", "false");
   EXPECT_EQ(100, FLAGS_quic_max_tracked_packet_count);
 }
 
-TEST(QuicFlagsTest, SetQuicFlagByName_uint32_t) {
+TEST_F(QuicFlagsTest, SetQuicFlagByName_uint32_t) {
   FLAGS_quic_send_buffer_max_data_slice_size = 4096;
   SetQuicFlagByName("FLAGS_quic_send_buffer_max_data_slice_size", "1024");
   EXPECT_EQ(1024u, FLAGS_quic_send_buffer_max_data_slice_size);
 }
 
-TEST(QuicFlagsTest, SetQuicFlagByName_uint32_t_invalid) {
+TEST_F(QuicFlagsTest, SetQuicFlagByName_uint32_t_invalid) {
   FLAGS_quic_send_buffer_max_data_slice_size = 4096;
   SetQuicFlagByName("FLAGS_quic_send_buffer_max_data_slice_size", "false");
   EXPECT_EQ(4096u, FLAGS_quic_send_buffer_max_data_slice_size);
 }
 
-TEST(QuicFlagsTest, SetQuicFlagByName_uint32_t_negative) {
+TEST_F(QuicFlagsTest, SetQuicFlagByName_uint32_t_negative) {
   FLAGS_quic_send_buffer_max_data_slice_size = 4096;
   SetQuicFlagByName("FLAGS_quic_send_buffer_max_data_slice_size", "-1");
   EXPECT_EQ(4096u, FLAGS_quic_send_buffer_max_data_slice_size);
 }
 
-TEST(QuicFlagsTest, SetQuicFlagByName_uint32_t_too_large) {
+TEST_F(QuicFlagsTest, SetQuicFlagByName_uint32_t_too_large) {
   FLAGS_quic_send_buffer_max_data_slice_size = 4096;
   SetQuicFlagByName("FLAGS_quic_send_buffer_max_data_slice_size", "4294967297");
   EXPECT_EQ(4096u, FLAGS_quic_send_buffer_max_data_slice_size);
 }
 
-TEST(QuicFlagsTest, SetQuicFlagByName_int32_t) {
+TEST_F(QuicFlagsTest, SetQuicFlagByName_int32_t) {
   FLAGS_quic_lumpy_pacing_size = 1;
   SetQuicFlagByName("FLAGS_quic_lumpy_pacing_size", "10");
   EXPECT_EQ(10, FLAGS_quic_lumpy_pacing_size);
 }
 
-TEST(QuicFlagsTest, SetQuicFlagByName_int32_t_invalid) {
+TEST_F(QuicFlagsTest, SetQuicFlagByName_int32_t_invalid) {
   FLAGS_quic_lumpy_pacing_size = 1;
   SetQuicFlagByName("FLAGS_quic_lumpy_pacing_size", "false");
   EXPECT_EQ(1, FLAGS_quic_lumpy_pacing_size);
diff --git a/net/quic/quic_chromium_client_session_test.cc b/net/quic/quic_chromium_client_session_test.cc
index 2bb4990..925b45ab 100644
--- a/net/quic/quic_chromium_client_session_test.cc
+++ b/net/quic/quic_chromium_client_session_test.cc
@@ -1751,6 +1751,11 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, RetransmittableOnWireTimeout) {
+  // TODO(crbug/997684): Re-enable this test
+  if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 &&
+      version_.transport_version == quic::QUIC_VERSION_99)
+    return;
+
   migrate_session_early_v2_ = true;
 
   MockQuicData quic_data(version_);
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 0bb628a5..2e2bf7c 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -72,6 +72,10 @@
 // Max size of data slice in bytes for QUIC stream send buffer.
 QUIC_FLAG(uint32_t, FLAGS_quic_send_buffer_max_data_slice_size, 4096u)
 
+// Anti-amplification factor. Before address validation, server will
+// send no more than factor times bytes received.
+QUIC_FLAG(int32_t, FLAGS_quic_anti_amplification_factor, 3)
+
 // If true, QUIC supports both QUIC Crypto and TLS 1.3 for the handshake
 // protocol.
 QUIC_FLAG(bool, FLAGS_quic_supports_tls_handshake, false)
@@ -174,11 +178,6 @@
           FLAGS_quic_reloadable_flag_quic_bbr_startup_rate_reduction,
           false)
 
-// If true, log leaf cert subject name into warning log.
-QUIC_FLAG(bool,
-          FLAGS_quic_reloadable_flag_quic_log_cert_name_for_empty_sct,
-          true)
-
 // If true, enable QUIC version 47.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_version_47, false)
 
@@ -219,28 +218,17 @@
           FLAGS_quic_reloadable_flag_quic_conservative_cwnd_and_pacing_gains,
           false)
 
-// When true, QuicConnectionId will allocate long connection IDs on the heap
-// instead of inline in the object.
-QUIC_FLAG(bool, FLAGS_quic_restart_flag_quic_use_allocated_connection_ids, true)
-
 // If enabled, do not call OnStreamFrame() with empty frame after receiving
 // empty or too large headers with FIN.
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_avoid_empty_frame_after_empty_headers,
           true)
 
-// If true, disable QUIC version 44.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_44, true)
-
 // If true, ignore TLPR if there is no pending stream data.
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_ignore_tlpr_if_no_pending_stream_data,
           true)
 
-// If true, when detecting losses, use packets_acked of corresponding packet
-// number space.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_packets_acked, true)
-
 // When true, QuicDispatcher will drop packets that have an initial destination
 // connection ID that is too short, instead of responding with a Version
 // Negotiation packet to reject it.
@@ -295,7 +283,7 @@
 QUIC_FLAG(
     bool,
     FLAGS_quic_reloadable_flag_quic_reject_unprocessable_packets_statelessly,
-    true)
+    false)
 
 // When true, QuicConnectionId::Hash uses SipHash instead of XOR.
 QUIC_FLAG(bool, FLAGS_quic_restart_flag_quic_connection_id_use_siphash, true)
diff --git a/pdf/accessibility.cc b/pdf/accessibility.cc
index 7f1cd5b..20b0bd87 100644
--- a/pdf/accessibility.cc
+++ b/pdf/accessibility.cc
@@ -40,33 +40,51 @@
   int char_index = 0;
   while (char_index < char_count) {
     PP_PrivateAccessibilityTextRunInfo text_run_info;
-    pp::FloatRect bounds;
-    engine->GetTextRunInfo(page_index, char_index, &text_run_info.len,
-                           &text_run_info.font_size, &bounds);
+    engine->GetTextRunInfo(page_index, char_index, &text_run_info);
     DCHECK_LE(char_index + text_run_info.len,
               static_cast<uint32_t>(char_count));
-    text_run_info.direction = PP_PRIVATEDIRECTION_LTR;
-    text_run_info.bounds = bounds;
     text_runs->push_back(text_run_info);
 
     // We need to provide enough information to draw a bounding box
     // around any arbitrary text range, but the bounding boxes of characters
-    // we get from PDFium don't necessarily "line up". Walk through the
+    // we get from PDFium don't necessarily "line up".
+    // Example for LTR text direction: walk through the
     // characters in each text run and let the width of each character be
     // the difference between the x coordinate of one character and the
     // x coordinate of the next. The rest of the bounds of each character
     // can be computed from the bounds of the text run.
+    // The same idea is used for RTL, TTB and BTT text direction.
     pp::FloatRect char_bounds = engine->GetCharBounds(page_index, char_index);
     for (uint32_t i = 0; i < text_run_info.len - 1; i++) {
       DCHECK_LT(char_index + i + 1, static_cast<uint32_t>(char_count));
       pp::FloatRect next_char_bounds =
           engine->GetCharBounds(page_index, char_index + i + 1);
-      (*chars)[char_index + i].char_width =
-          next_char_bounds.x() - char_bounds.x();
+      double& char_width = (*chars)[char_index + i].char_width;
+      switch (text_run_info.direction) {
+        case PP_PRIVATEDIRECTION_NONE:
+        case PP_PRIVATEDIRECTION_LTR:
+          char_width = next_char_bounds.x() - char_bounds.x();
+          break;
+        case PP_PRIVATEDIRECTION_TTB:
+          char_width = next_char_bounds.y() - char_bounds.y();
+          break;
+        case PP_PRIVATEDIRECTION_RTL:
+          char_width = char_bounds.right() - next_char_bounds.right();
+          break;
+        case PP_PRIVATEDIRECTION_BTT:
+          char_width = char_bounds.bottom() - next_char_bounds.bottom();
+          break;
+      }
       char_bounds = next_char_bounds;
     }
-    (*chars)[char_index + text_run_info.len - 1].char_width =
-        char_bounds.width();
+    double& char_width =
+        (*chars)[char_index + text_run_info.len - 1].char_width;
+    if (text_run_info.direction == PP_PRIVATEDIRECTION_BTT ||
+        text_run_info.direction == PP_PRIVATEDIRECTION_TTB) {
+      char_width = char_bounds.height();
+    } else {
+      char_width = char_bounds.width();
+    }
 
     char_index += text_run_info.len;
   }
diff --git a/pdf/pdf_engine.h b/pdf/pdf_engine.h
index 6f28b4f..ab3f8cd 100644
--- a/pdf/pdf_engine.h
+++ b/pdf/pdf_engine.h
@@ -41,6 +41,7 @@
 
 struct PP_PdfAccessibilityActionData;
 struct PP_PdfPrintSettings_Dev;
+struct PP_PrivateAccessibilityTextRunInfo;
 
 namespace gfx {
 class Rect;
@@ -358,13 +359,13 @@
   // Get a given unicode character on a given page.
   virtual uint32_t GetCharUnicode(int page_index, int char_index) = 0;
   // Given a start char index, find the longest continuous run of text that's
-  // in a single direction and with the same style and font size. Return the
-  // length of that sequence and its font size and bounding box.
-  virtual void GetTextRunInfo(int page_index,
-                              int start_char_index,
-                              uint32_t* out_len,
-                              double* out_font_size,
-                              pp::FloatRect* out_bounds) = 0;
+  // in a single direction and with the same style and font size. Fill the
+  // |text_run_info| with the length of that sequence, text direction, bounding
+  // box and font size.
+  virtual void GetTextRunInfo(
+      int page_index,
+      int start_char_index,
+      PP_PrivateAccessibilityTextRunInfo* text_run_info) = 0;
   // Gets the PDF document's print scaling preference. True if the document can
   // be scaled to fit.
   virtual bool GetPrintScaling() = 0;
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index 23b03be..51fccde 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -2189,14 +2189,12 @@
   return pages_[page_index]->GetCharUnicode(char_index);
 }
 
-void PDFiumEngine::GetTextRunInfo(int page_index,
-                                  int start_char_index,
-                                  uint32_t* out_len,
-                                  double* out_font_size,
-                                  pp::FloatRect* out_bounds) {
+void PDFiumEngine::GetTextRunInfo(
+    int page_index,
+    int start_char_index,
+    PP_PrivateAccessibilityTextRunInfo* text_run_info) {
   DCHECK(PageIndexInBounds(page_index));
-  return pages_[page_index]->GetTextRunInfo(start_char_index, out_len,
-                                            out_font_size, out_bounds);
+  return pages_[page_index]->GetTextRunInfo(start_char_index, text_run_info);
 }
 
 bool PDFiumEngine::GetPrintScaling() {
diff --git a/pdf/pdfium/pdfium_engine.h b/pdf/pdfium/pdfium_engine.h
index e22c9c1..0b5a1ad 100644
--- a/pdf/pdfium/pdfium_engine.h
+++ b/pdf/pdfium/pdfium_engine.h
@@ -113,11 +113,10 @@
   int GetCharCount(int page_index) override;
   pp::FloatRect GetCharBounds(int page_index, int char_index) override;
   uint32_t GetCharUnicode(int page_index, int char_index) override;
-  void GetTextRunInfo(int page_index,
-                      int start_char_index,
-                      uint32_t* out_len,
-                      double* out_font_size,
-                      pp::FloatRect* out_bounds) override;
+  void GetTextRunInfo(
+      int page_index,
+      int start_char_index,
+      PP_PrivateAccessibilityTextRunInfo* text_run_info) override;
   bool GetPrintScaling() override;
   int GetCopiesToPrint() override;
   int GetDuplexType() override;
diff --git a/pdf/pdfium/pdfium_page.cc b/pdf/pdfium/pdfium_page.cc
index 4216164..2a8527a 100644
--- a/pdf/pdfium/pdfium_page.cc
+++ b/pdf/pdfium/pdfium_page.cc
@@ -251,29 +251,30 @@
   return text_page();
 }
 
-void PDFiumPage::GetTextRunInfo(int start_char_index,
-                                uint32_t* out_len,
-                                double* out_font_size,
-                                pp::FloatRect* out_bounds) {
+void PDFiumPage::GetTextRunInfo(
+    int start_char_index,
+    PP_PrivateAccessibilityTextRunInfo* text_run_info) {
   if (start_char_index < 0) {
-    *out_len = 0;
-    *out_font_size = 0;
-    *out_bounds = pp::FloatRect();
+    text_run_info->len = 0;
+    text_run_info->font_size = 0;
+    text_run_info->bounds = pp::FloatRect();
+    text_run_info->direction = PP_PRIVATEDIRECTION_NONE;
     return;
   }
-
   FPDF_PAGE page = GetPage();
   FPDF_TEXTPAGE text_page = GetTextPage();
   int chars_count = FPDFText_CountChars(text_page);
 
-  int char_index = GetFirstNonUnicodeWhiteSpaceCharIndex(
+  int actual_start_char_index = GetFirstNonUnicodeWhiteSpaceCharIndex(
       text_page, start_char_index, chars_count);
-  if (char_index >= chars_count) {
-    *out_len = 0;
-    *out_font_size = 0;
-    *out_bounds = pp::FloatRect();
+  if (actual_start_char_index >= chars_count) {
+    text_run_info->len = 0;
+    text_run_info->font_size = 0;
+    text_run_info->bounds = pp::FloatRect();
+    text_run_info->direction = PP_PRIVATEDIRECTION_NONE;
     return;
   }
+  int char_index = actual_start_char_index;
 
   pp::FloatRect start_char_rect =
       GetFloatCharRectInPixels(page, text_page, char_index);
@@ -376,9 +377,16 @@
     text_run_font_size = estimated_font_size;
   }
 
-  *out_len = char_index - start_char_index;
-  *out_font_size = text_run_font_size;
-  *out_bounds = text_run_bounds;
+  // Infer text direction from first and last character of the text run. We
+  // can't base our decision on the character direction, since a character of a
+  // RTL language will have an angle of 0 when not rotated, just like a
+  // character in a LTR language.
+  text_run_info->direction = char_index - actual_start_char_index > 1
+                                 ? GetDirectionFromAngle(text_run_angle)
+                                 : PP_PRIVATEDIRECTION_NONE;
+  text_run_info->len = char_index - start_char_index;
+  text_run_info->font_size = text_run_font_size;
+  text_run_info->bounds = text_run_bounds;
 }
 
 uint32_t PDFiumPage::GetCharUnicode(int char_index) {
diff --git a/pdf/pdfium/pdfium_page.h b/pdf/pdfium/pdfium_page.h
index 4059d597..81315f2 100644
--- a/pdf/pdfium/pdfium_page.h
+++ b/pdf/pdfium/pdfium_page.h
@@ -20,6 +20,8 @@
 #include "third_party/pdfium/public/fpdf_text.h"
 #include "ui/gfx/geometry/point_f.h"
 
+struct PP_PrivateAccessibilityTextRunInfo;
+
 namespace chrome_pdf {
 
 class PDFiumEngine;
@@ -43,12 +45,11 @@
   FPDF_TEXTPAGE GetTextPage();
 
   // Given a start char index, find the longest continuous run of text that's
-  // in a single direction and with the same style and font size. Return the
-  // length of that sequence and its font size and bounding box.
+  // in a single direction and with the same style and font size. Fill the
+  // |text_run_info| with the length of that sequence, text direction, bounding
+  // box and font size.
   void GetTextRunInfo(int start_char_index,
-                      uint32_t* out_len,
-                      double* out_font_size,
-                      pp::FloatRect* out_bounds);
+                      PP_PrivateAccessibilityTextRunInfo* text_run_info);
   // Get a unicode character from the page.
   uint32_t GetCharUnicode(int char_index);
   // Get the bounds of a character in page pixels.
diff --git a/services/network/cors/preflight_controller.cc b/services/network/cors/preflight_controller.cc
index 6c62da8..cc1a175c 100644
--- a/services/network/cors/preflight_controller.cc
+++ b/services/network/cors/preflight_controller.cc
@@ -110,10 +110,6 @@
       net::HttpRequestHeaders::kOrigin,
       (tainted ? url::Origin() : *request.request_initiator).Serialize());
 
-  // TODO(toyoshim): Should not matter, but at this moment, it hits a sanity
-  // check in ResourceDispatcherHostImpl if |resource_type| isn't set.
-  preflight_request->resource_type = request.resource_type;
-
   return preflight_request;
 }
 
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 2ce3c69..b40efa3 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -56,6 +56,54 @@
       },
       {
         "args": [
+          "--disable-field-trial-config",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "system_webview_shell_layout_test_apk_no_field_trial"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "system_webview_shell_layout_test_apk_no_field_trial",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "LMY48I",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "expiration": 10800,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "system_webview_shell_layout_test_apk"
+      },
+      {
+        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -108,6 +156,60 @@
       },
       {
         "args": [
+          "--disable-field-trial-config",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webview_cts_tests_no_field_trial"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webview_cts_tests_no_field_trial",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/android_webview/tools/cts_archive",
+              "location": "android_webview/tools/cts_archive",
+              "revision": "version:1.7"
+            },
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "LMY48I",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "expiration": 10800,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 3
+        },
+        "test": "webview_cts_tests"
+      },
+      {
+        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -151,6 +253,54 @@
           ]
         },
         "test": "webview_ui_test_app_test_apk"
+      },
+      {
+        "args": [
+          "--disable-field-trial-config",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webview_ui_test_app_test_apk_no_field_trial"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webview_ui_test_app_test_apk_no_field_trial",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "LMY48I",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "expiration": 10800,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "webview_ui_test_app_test_apk"
       }
     ]
   },
@@ -203,6 +353,53 @@
       },
       {
         "args": [
+          "--disable-field-trial-config",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "system_webview_shell_layout_test_apk_no_field_trial"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "system_webview_shell_layout_test_apk_no_field_trial",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "system_webview_shell_layout_test_apk"
+      },
+      {
+        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -254,6 +451,59 @@
       },
       {
         "args": [
+          "--disable-field-trial-config",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webview_cts_tests_no_field_trial"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webview_cts_tests_no_field_trial",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/android_webview/tools/cts_archive",
+              "location": "android_webview/tools/cts_archive",
+              "revision": "version:1.7"
+            },
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 3
+        },
+        "test": "webview_cts_tests"
+      },
+      {
+        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -296,6 +546,53 @@
           ]
         },
         "test": "webview_ui_test_app_test_apk"
+      },
+      {
+        "args": [
+          "--disable-field-trial-config",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webview_ui_test_app_test_apk_no_field_trial"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webview_ui_test_app_test_apk_no_field_trial",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "webview_ui_test_app_test_apk"
       }
     ]
   },
@@ -348,6 +645,53 @@
       },
       {
         "args": [
+          "--disable-field-trial-config",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "system_webview_shell_layout_test_apk_no_field_trial"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "system_webview_shell_layout_test_apk_no_field_trial",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "NRD90M",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "system_webview_shell_layout_test_apk"
+      },
+      {
+        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -399,6 +743,59 @@
       },
       {
         "args": [
+          "--disable-field-trial-config",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webview_cts_tests_no_field_trial"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webview_cts_tests_no_field_trial",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/android_webview/tools/cts_archive",
+              "location": "android_webview/tools/cts_archive",
+              "revision": "version:1.7"
+            },
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "NRD90M",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 3
+        },
+        "test": "webview_cts_tests"
+      },
+      {
+        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -441,6 +838,53 @@
           ]
         },
         "test": "webview_ui_test_app_test_apk"
+      },
+      {
+        "args": [
+          "--disable-field-trial-config",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webview_ui_test_app_test_apk_no_field_trial"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webview_ui_test_app_test_apk_no_field_trial",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "NRD90M",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "webview_ui_test_app_test_apk"
       }
     ]
   },
@@ -493,6 +937,53 @@
       },
       {
         "args": [
+          "--disable-field-trial-config",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "system_webview_shell_layout_test_apk_no_field_trial"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "system_webview_shell_layout_test_apk_no_field_trial",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "OPM4.171019.021.P2",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "walleye",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "system_webview_shell_layout_test_apk"
+      },
+      {
+        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -544,6 +1035,59 @@
       },
       {
         "args": [
+          "--disable-field-trial-config",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webview_cts_tests_no_field_trial"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webview_cts_tests_no_field_trial",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/android_webview/tools/cts_archive",
+              "location": "android_webview/tools/cts_archive",
+              "revision": "version:1.7"
+            },
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "OPM4.171019.021.P2",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "walleye",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 3
+        },
+        "test": "webview_cts_tests"
+      },
+      {
+        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -586,6 +1130,53 @@
           ]
         },
         "test": "webview_ui_test_app_test_apk"
+      },
+      {
+        "args": [
+          "--disable-field-trial-config",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webview_ui_test_app_test_apk_no_field_trial"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webview_ui_test_app_test_apk_no_field_trial",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "OPM4.171019.021.P2",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "walleye",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "webview_ui_test_app_test_apk"
       }
     ]
   },
@@ -26570,6 +27161,53 @@
       },
       {
         "args": [
+          "--disable-field-trial-config",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "system_webview_shell_layout_test_apk_no_field_trial"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "system_webview_shell_layout_test_apk_no_field_trial",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "OPM4.171019.021.P2",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "walleye",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "system_webview_shell_layout_test_apk"
+      },
+      {
+        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -26621,6 +27259,59 @@
       },
       {
         "args": [
+          "--disable-field-trial-config",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webview_cts_tests_no_field_trial"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webview_cts_tests_no_field_trial",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/android_webview/tools/cts_archive",
+              "location": "android_webview/tools/cts_archive",
+              "revision": "version:1.7"
+            },
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "OPM4.171019.021.P2",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "walleye",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 3
+        },
+        "test": "webview_cts_tests"
+      },
+      {
+        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -26663,6 +27354,53 @@
           ]
         },
         "test": "webview_ui_test_app_test_apk"
+      },
+      {
+        "args": [
+          "--disable-field-trial-config",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webview_ui_test_app_test_apk_no_field_trial"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webview_ui_test_app_test_apk_no_field_trial",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "OPM4.171019.021.P2",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "walleye",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "webview_ui_test_app_test_apk"
       }
     ]
   },
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 7434adb1..81d6fb4 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -2547,6 +2547,7 @@
       "apks/SystemWebView.apk",
       "--replace-system-package",
       "org.chromium.webview_shell,apks/SystemWebViewShell.apk",
+      "--use-apk-under-test-flags-file",
     ],
     "label": "//android_webview/tools/system_webview_shell:system_webview_shell_layout_test_apk",
     "type": "console_test_launcher",
@@ -2967,6 +2968,7 @@
     "args": [
       "--use-webview-provider",
       "apks/SystemWebView.apk",
+      "--use-apk-under-test-flags-file",
     ],
     "label": "//android_webview/tools/automated_ui_tests:webview_ui_test_app_test_apk",
     "type": "console_test_launcher",
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 6b14aeae..231238cc 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -4621,6 +4621,15 @@
       'system_webview_shell_layout_test_apk': {},
     },
 
+    'system_webview_shell_instrumentation_tests_no_field_trial': {
+      'system_webview_shell_layout_test_apk_no_field_trial': {
+        'args': [
+          '--disable-field-trial-config',
+        ],
+        'test': 'system_webview_shell_layout_test_apk',
+      },
+    },
+
     'telemetry_perf_unittests_isolated_scripts': {
       'telemetry_perf_unittests': {
         'swarming': {
@@ -4768,10 +4777,38 @@
       },
     },
 
+    'webview_cts_tests_gtest_no_field_trial': {
+      'webview_cts_tests_no_field_trial': {
+        'args': [
+          '--disable-field-trial-config',
+        ],
+        'swarming': {
+          'shards': 3,
+          'cipd_packages': [
+            {
+              "cipd_package": 'chromium/android_webview/tools/cts_archive',
+              'location': 'android_webview/tools/cts_archive',
+              'revision': 'version:1.7',
+            }
+          ]
+        },
+        'test': 'webview_cts_tests',
+      },
+    },
+
     'webview_ui_instrumentation_tests': {
       'webview_ui_test_app_test_apk': {},
     },
 
+    'webview_ui_instrumentation_tests_no_field_trial': {
+      'webview_ui_test_app_test_apk_no_field_trial': {
+        'args': [
+          '--disable-field-trial-config',
+        ],
+        'test': 'webview_ui_test_app_test_apk',
+      },
+    },
+
     'win7_32_bit_gtests': {
       'base_unittests': {},
       'browser_tests': {
@@ -5587,8 +5624,11 @@
 
     'webview_bot_gtests': [
       'system_webview_shell_instrumentation_tests',
+      'system_webview_shell_instrumentation_tests_no_field_trial',
       'webview_ui_instrumentation_tests',
+      'webview_ui_instrumentation_tests_no_field_trial',
       'webview_cts_tests_gtest',
+      'webview_cts_tests_gtest_no_field_trial',
     ],
   }
 }
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 6d5ee55..9448d8c7 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -386,7 +386,6 @@
     "web/modules/webrtc/webrtc_audio_device_not_impl.h",
     "web/modules/webrtc/webrtc_audio_renderer.h",
     "web/web_active_fling_parameters.h",
-    "web/web_apply_constraints_request.h",
     "web/web_array_buffer.h",
     "web/web_array_buffer_converter.h",
     "web/web_associated_url_loader.h",
diff --git a/third_party/blink/public/mojom/devtools/devtools_agent.mojom b/third_party/blink/public/mojom/devtools/devtools_agent.mojom
index bd979c0..89a1dbc5 100644
--- a/third_party/blink/public/mojom/devtools/devtools_agent.mojom
+++ b/third_party/blink/public/mojom/devtools/devtools_agent.mojom
@@ -72,17 +72,11 @@
   // |client_expects_binary_response| indicates that responses (and
   // notifications) sent from this session should use binary (CBOR)
   // encoding as opposed to JSON encoding.
-  //
-  // |session_id| is a string which, if provided, uniquely identifies the
-  // session. The renderer must send this back with each response. The current
-  // implementation uses the string serialization of an UnguessableToken, but
-  // that is subject to change.
   AttachDevToolsSession(pending_associated_remote<DevToolsSessionHost> host,
                         pending_associated_receiver<DevToolsSession> session,
                         pending_receiver<DevToolsSession> io_session,
                         DevToolsSessionState? reattach_session_state,
-                        bool client_expects_binary_responses,
-                        string sesson_id);
+                        bool client_expects_binary_responses);
 
   // Requests an element at specific position to be inspected in every
   // attached session (or the next attached one if none yet).
diff --git a/third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom b/third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom
index 586d1fe..a0ccec0 100644
--- a/third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom
+++ b/third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom
@@ -20,7 +20,7 @@
 interface EmbeddedFrameSinkClient {
   // Called when a CompositorFrameSink is created for this frame sink.
   // Establishes a direct connection from the embedee to the embedder.
-  BindSurfaceEmbedder(SurfaceEmbedder& embedder);
+  BindSurfaceEmbedder(pending_receiver<SurfaceEmbedder> embedder);
 };
 
 // Provides embedded CompositorFrameSinks for the renderer. The renderer should
@@ -33,25 +33,25 @@
   // the parent must be specified at this time.
   RegisterEmbeddedFrameSink(viz.mojom.FrameSinkId parent_frame_sink_id,
                             viz.mojom.FrameSinkId frame_sink_id,
-                            EmbeddedFrameSinkClient client);
+                            pending_remote<EmbeddedFrameSinkClient> client);
 
   // Create a CompositorFrameSink for |frame_sink_id|. This must happen after
   // calling RegisterEmbeddedFrameSink() for |frame_sink_id|.
   CreateCompositorFrameSink(viz.mojom.FrameSinkId frame_sink_id,
-                            viz.mojom.CompositorFrameSinkClient client,
-                            viz.mojom.CompositorFrameSink& sink);
+                            pending_remote<viz.mojom.CompositorFrameSinkClient> client,
+                            pending_receiver<viz.mojom.CompositorFrameSink> sink);
 
   // Create CompositorFrameSink directly in a single call (instead of going
   // through both function above).
   CreateSimpleCompositorFrameSink(viz.mojom.FrameSinkId parent_frame_sink_id,
                                   viz.mojom.FrameSinkId frame_sink_id,
-                                  EmbeddedFrameSinkClient surface_client,
-                                  viz.mojom.CompositorFrameSinkClient client,
-                                  viz.mojom.CompositorFrameSink& sink);
+                                  pending_remote<EmbeddedFrameSinkClient> surface_client,
+                                  pending_remote<viz.mojom.CompositorFrameSinkClient> client,
+                                  pending_receiver<viz.mojom.CompositorFrameSink> sink);
 
   // Establishes a direct connection to the embedder of |frame_sink_id|. This
   // will allow the client to let the embedder know what LocalSurfaceId it is
   // using.
   ConnectToEmbedder(viz.mojom.FrameSinkId frame_sink_id,
-                    SurfaceEmbedder& embedder);
+                    pending_receiver<SurfaceEmbedder> embedder);
 };
diff --git a/third_party/blink/public/mojom/presentation/presentation.mojom b/third_party/blink/public/mojom/presentation/presentation.mojom
index f84022b..8d0e8093 100644
--- a/third_party/blink/public/mojom/presentation/presentation.mojom
+++ b/third_party/blink/public/mojom/presentation/presentation.mojom
@@ -82,13 +82,13 @@
 // Holds result from successfully starting or reconnecting to a presentation
 // with a pair of Mojo pipes to communicate with the presentation page.
 // |presentation_info| holds the URL and ID of the presentation.
-// |connection_ptr| holds the connection from the presentation to the
-// controlling frame.  |connection_request| is fulfilled by the controlling
+// |connection_remote| holds the connection from the presentation to the
+// controlling frame.  |connection_receiver| is fulfilled by the controlling
 // frame with a new connection to return to the presentation.
 struct PresentationConnectionResult {
   PresentationInfo presentation_info;
-  PresentationConnection connection_ptr;
-  PresentationConnection& connection_request;
+  pending_remote<PresentationConnection> connection_remote;
+  pending_receiver<PresentationConnection> connection_receiver;
 };
 
 // Service provided by the browser process to implement parts of the
@@ -106,14 +106,14 @@
   // Sets the PresentationController of the frame. This is called by the
   // controller page of a presentation to listen for screen availability and
   // initiate presentations.
-  SetController(PresentationController controller);
+  SetController(pending_remote<PresentationController> controller);
 
   // Sets the PresentationReceiver of the page. This is called by the
   // presentation page of a 1-UA presentation in order to receive updates on new
   // connections (initiated by a controller) becoming available.
   // TODO(crbug.com/749327): Move this method out of PresentationService and
   // define a new interface that implements only Presentation Receiver APIs.
-  SetReceiver(PresentationReceiver receiver);
+  SetReceiver(pending_remote<PresentationReceiver> receiver);
 
   ///////////// Functions here are for the controller part of the API. /////////
 
@@ -194,12 +194,12 @@
   // Notifies the PresentationReceiver that a receiver connection has become
   // available.
   // |info|: URL and ID of the PresentationConnection.
-  // |controller_connection|: Ptr to the corresponding controller connection.
-  // |receiver_connection_request|: Interface request to be bound to a receiver
-  // connection implementation.
+  // |controller_connection|: Remote to the corresponding controller connection.
+  // |receiver_connection_receiver|: PendingReceiver to be bound at the other
+  // endpoint of the controller connection.
   // TODO(btolsch): Convert this to take a PresentationConnectionResult.
   OnReceiverConnectionAvailable(
       PresentationInfo info,
-      PresentationConnection controller_connection,
-      PresentationConnection& receiver_connection_request);
+      pending_remote<PresentationConnection> controller_connection,
+      pending_receiver<PresentationConnection> receiver_connection_receiver);
 };
diff --git a/third_party/blink/public/platform/web_mouse_wheel_event.h b/third_party/blink/public/platform/web_mouse_wheel_event.h
index a9f9523..087aef42 100644
--- a/third_party/blink/public/platform/web_mouse_wheel_event.h
+++ b/third_party/blink/public/platform/web_mouse_wheel_event.h
@@ -35,6 +35,10 @@
     // A wheel event with phase may begin shows that a scrolling sequence may
     // start.
     kPhaseMayBegin = 1 << 5,
+    // A wheel event with momentum phase blocked shows that a scrolling sequence
+    // will not be followed by a momentum fling. This should only ever be set on
+    // the momentum phase of an event.
+    kPhaseBlocked = 1 << 6,
   };
 
   // A hint at the outcome of a wheel event should it not get canceled.
diff --git a/third_party/blink/public/web/web_apply_constraints_request.h b/third_party/blink/public/web/web_apply_constraints_request.h
deleted file mode 100644
index 97ec47b..0000000
--- a/third_party/blink/public/web/web_apply_constraints_request.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_APPLY_CONSTRAINTS_REQUEST_H_
-#define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_APPLY_CONSTRAINTS_REQUEST_H_
-
-#include "third_party/blink/public/platform/web_common.h"
-#include "third_party/blink/public/platform/web_private_ptr.h"
-
-namespace blink {
-
-class ApplyConstraintsRequest;
-class WebMediaConstraints;
-class WebMediaStreamTrack;
-class WebString;
-
-class BLINK_EXPORT WebApplyConstraintsRequest {
- public:
-  WebApplyConstraintsRequest() = default;
-  WebApplyConstraintsRequest(const WebApplyConstraintsRequest& other) {
-    Assign(other);
-  }
-  WebApplyConstraintsRequest& operator=(
-      const WebApplyConstraintsRequest& other) {
-    Assign(other);
-    return *this;
-  }
-  ~WebApplyConstraintsRequest();
-
-  bool operator==(const WebApplyConstraintsRequest& other) const;
-
-  void Reset();
-  bool IsNull() const { return private_.IsNull(); }
-
-  WebMediaStreamTrack Track() const;
-  WebMediaConstraints Constraints() const;
-
-  void RequestSucceeded();
-  void RequestFailed(const WebString& constraint, const WebString& message);
-
-  // For testing in content/
-  static WebApplyConstraintsRequest CreateForTesting(
-      const WebMediaStreamTrack&,
-      const WebMediaConstraints&);
-
-#if INSIDE_BLINK
-  explicit WebApplyConstraintsRequest(ApplyConstraintsRequest*);
-#endif
-
- private:
-  void Assign(const WebApplyConstraintsRequest&);
-
-  WebPrivatePtr<ApplyConstraintsRequest> private_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_APPLY_CONSTRAINTS_REQUEST_H_
diff --git a/third_party/blink/renderer/core/animation/css_color_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_color_interpolation_type.cc
index 3e8a8e89..ad90d4a 100644
--- a/third_party/blink/renderer/core/animation/css_color_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_color_interpolation_type.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
 #include "third_party/blink/renderer/core/layout/layout_theme.h"
+#include "third_party/blink/renderer/core/style/computed_style.h"
 
 namespace blink {
 
@@ -72,7 +73,9 @@
       return CreateInterpolableColor(LayoutTheme::GetTheme().FocusRingColor());
     default:
       DCHECK(StyleColor::IsColorKeyword(keyword));
-      return CreateInterpolableColor(StyleColor::ColorFromKeyword(keyword));
+      // TODO(crbug.com/929098) Need to pass an appropriate color scheme here.
+      return CreateInterpolableColor(StyleColor::ColorFromKeyword(
+          keyword, ComputedStyle::InitialStyle().UsedColorScheme()));
   }
 }
 
diff --git a/third_party/blink/renderer/core/css/parser/css_parser.cc b/third_party/blink/renderer/core/css/parser/css_parser.cc
index 5c25803..57553b8 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser.cc
@@ -253,7 +253,9 @@
   return true;
 }
 
-bool CSSParser::ParseSystemColor(Color& color, const String& color_string) {
+bool CSSParser::ParseSystemColor(Color& color,
+                                 const String& color_string,
+                                 WebColorScheme color_scheme) {
   CSSValueID id = CssValueKeywordID(color_string);
   if (!StyleColor::IsSystemColor(id))
     return false;
@@ -262,7 +264,7 @@
       (id == CSSValueID::kLinktext || id == CSSValueID::kVisitedtext)) {
     return false;
   }
-  color = LayoutTheme::GetTheme().SystemColor(id);
+  color = LayoutTheme::GetTheme().SystemColor(id, color_scheme);
   return true;
 }
 
diff --git a/third_party/blink/renderer/core/css/parser/css_parser.h b/third_party/blink/renderer/core/css/parser/css_parser.h
index d53b6f0a..6de5b9b 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PARSER_CSS_PARSER_H_
 
 #include <memory>
+#include "third_party/blink/public/platform/web_color_scheme.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
@@ -99,7 +100,9 @@
   // The color will only be changed when string contains a valid CSS color, so
   // callers can set it to a default color and ignore the boolean result.
   static bool ParseColor(Color&, const String&, bool strict = false);
-  static bool ParseSystemColor(Color&, const String&);
+  static bool ParseSystemColor(Color&,
+                               const String&,
+                               WebColorScheme color_scheme);
 
   static void ParseSheetForInspector(const CSSParserContext*,
                                      StyleSheetContents*,
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
index 62eea52..babd44d 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
@@ -1319,7 +1319,8 @@
           case CSSValueID::kCurrentcolor:
             break;
           default:
-            color = StyleColor::ColorFromKeyword(value_id);
+            color = StyleColor::ColorFromKeyword(
+                value_id, ComputedStyle::InitialStyle().UsedColorScheme());
         }
       }
     }
diff --git a/third_party/blink/renderer/core/css/style_color.cc b/third_party/blink/renderer/core/css/style_color.cc
index 8bf8203e..b114f1e 100644
--- a/third_party/blink/renderer/core/css/style_color.cc
+++ b/third_party/blink/renderer/core/css/style_color.cc
@@ -8,13 +8,14 @@
 
 namespace blink {
 
-Color StyleColor::ColorFromKeyword(CSSValueID keyword) {
+Color StyleColor::ColorFromKeyword(CSSValueID keyword,
+                                   WebColorScheme color_scheme) {
   if (const char* value_name = getValueName(keyword)) {
     if (const NamedColor* named_color =
             FindColor(value_name, static_cast<wtf_size_t>(strlen(value_name))))
       return Color(named_color->argb_value);
   }
-  return LayoutTheme::GetTheme().SystemColor(keyword);
+  return LayoutTheme::GetTheme().SystemColor(keyword, color_scheme);
 }
 
 bool StyleColor::IsColorKeyword(CSSValueID id) {
diff --git a/third_party/blink/renderer/core/css/style_color.h b/third_party/blink/renderer/core/css/style_color.h
index 8e96f863d..16144c82 100644
--- a/third_party/blink/renderer/core/css/style_color.h
+++ b/third_party/blink/renderer/core/css/style_color.h
@@ -31,6 +31,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_STYLE_COLOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_STYLE_COLOR_H_
 
+#include "third_party/blink/public/platform/web_color_scheme.h"
 #include "third_party/blink/renderer/core/css_value_keywords.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -57,7 +58,7 @@
 
   bool HasAlpha() const { return !current_color_ && color_.HasAlpha(); }
 
-  static Color ColorFromKeyword(CSSValueID);
+  static Color ColorFromKeyword(CSSValueID, WebColorScheme color_scheme);
   static bool IsColorKeyword(CSSValueID);
   static bool IsSystemColor(CSSValueID);
 
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 4c78c12..5edaa47 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -38,6 +38,7 @@
 #include "base/time/time.h"
 #include "cc/input/overscroll_behavior.h"
 #include "cc/input/scroll_snap_data.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/metrics/public/cpp/mojo_ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
@@ -5628,11 +5629,11 @@
   if (!GetFrame())
     return;
 
-  mojom::blink::InsecureInputServicePtr insecure_input_service_ptr;
+  mojo::Remote<mojom::blink::InsecureInputService> insecure_input_service;
   GetFrame()->GetInterfaceProvider().GetInterface(
-      mojo::MakeRequest(&insecure_input_service_ptr));
+      insecure_input_service.BindNewPipeAndPassReceiver());
 
-  insecure_input_service_ptr->DidEditFieldInInsecureContext();
+  insecure_input_service->DidEditFieldInInsecureContext();
 }
 
 void Document::RegisterEventFactory(
diff --git a/third_party/blink/renderer/core/dom/events/event_target.cc b/third_party/blink/renderer/core/dom/events/event_target.cc
index ca6d560..171b1b8 100644
--- a/third_party/blink/renderer/core/dom/events/event_target.cc
+++ b/third_party/blink/renderer/core/dom/events/event_target.cc
@@ -388,8 +388,7 @@
   // For mousewheel event listeners that have the target as the window and
   // a bound function name of "ssc_wheel" treat and no passive value default
   // passive to true. See crbug.com/501568.
-  if (RuntimeEnabledFeatures::SmoothScrollJSInterventionEnabled() &&
-      event_type == event_type_names::kMousewheel && ToLocalDOMWindow() &&
+  if (event_type == event_type_names::kMousewheel && ToLocalDOMWindow() &&
       event_listener && !options->hasPassive()) {
     JSBasedEventListener* v8_listener =
         DynamicTo<JSBasedEventListener>(event_listener);
diff --git a/third_party/blink/renderer/core/dom/text_link_colors.cc b/third_party/blink/renderer/core/dom/text_link_colors.cc
index fa04a05..b243ecc5 100644
--- a/third_party/blink/renderer/core/dom/text_link_colors.cc
+++ b/third_party/blink/renderer/core/dom/text_link_colors.cc
@@ -82,7 +82,7 @@
     case CSSValueID::kInternalRootColor:
       return LayoutTheme::GetTheme().RootElementColor(color_scheme);
     default:
-      return StyleColor::ColorFromKeyword(value_id);
+      return StyleColor::ColorFromKeyword(value_id, color_scheme);
   }
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc b/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
index dd2d14b..3cbc9678 100644
--- a/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
+++ b/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
@@ -126,10 +126,11 @@
     // <select> background color. On Linux, that makes the <option>
     // background color very dark, so by default, try to use a lighter
     // background color for <option>s.
-    if (LayoutTheme::GetTheme().SystemColor(CSSValueID::kButtonface) ==
+    if (LayoutTheme::GetTheme().SystemColor(CSSValueID::kButtonface,
+                                            style.UsedColorScheme()) ==
         background_color_) {
-      background_color_ =
-          LayoutTheme::GetTheme().SystemColor(CSSValueID::kMenu);
+      background_color_ = LayoutTheme::GetTheme().SystemColor(
+          CSSValueID::kMenu, style.UsedColorScheme());
     }
 #endif
   }
diff --git a/third_party/blink/renderer/core/html/forms/password_input_type_test.cc b/third_party/blink/renderer/core/html/forms/password_input_type_test.cc
index f9bb4466..9b76583 100644
--- a/third_party/blink/renderer/core/html/forms/password_input_type_test.cc
+++ b/third_party/blink/renderer/core/html/forms/password_input_type_test.cc
@@ -1,12 +1,15 @@
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+
 #include <memory>
 #include <utility>
 
 #include "third_party/blink/renderer/core/html/forms/password_input_type.h"
 
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "mojo/public/cpp/system/message_pipe.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/insecure_input/insecure_input_service.mojom-blink.h"
@@ -23,15 +26,16 @@
         &frame.GetInterfaceProvider());
     test_api.SetBinderForName(
         mojom::blink::InsecureInputService::Name_,
-        WTF::BindRepeating(&MockInsecureInputService::BindRequest,
+        WTF::BindRepeating(&MockInsecureInputService::BindReceiver,
                            WTF::Unretained(this)));
   }
 
   ~MockInsecureInputService() override = default;
 
-  void BindRequest(mojo::ScopedMessagePipeHandle handle) {
-    binding_set_.AddBinding(
-        this, mojom::blink::InsecureInputServiceRequest(std::move(handle)));
+  void BindReceiver(mojo::ScopedMessagePipeHandle handle) {
+    receiver_set_.Add(this,
+                      mojo::PendingReceiver<mojom::blink::InsecureInputService>(
+                          std::move(handle)));
   }
 
   unsigned DidEditFieldCalls() const { return num_did_edit_field_calls_; }
@@ -39,7 +43,7 @@
  private:
   void DidEditFieldInInsecureContext() override { ++num_did_edit_field_calls_; }
 
-  mojo::BindingSet<InsecureInputService> binding_set_;
+  mojo::ReceiverSet<InsecureInputService> receiver_set_;
 
   unsigned num_did_edit_field_calls_ = 0;
 };
diff --git a/third_party/blink/renderer/core/html/forms/resources/color_picker.js b/third_party/blink/renderer/core/html/forms/resources/color_picker.js
index 0eb00142..903d43d1 100644
--- a/third_party/blink/renderer/core/html/forms/resources/color_picker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/color_picker.js
@@ -649,12 +649,23 @@
     this.colorSelectionRing_ = new ColorSelectionRing(this.colorPalette_);
     this.append(this.colorPalette_, this.colorSelectionRing_);
     this.initialized_ = false;
+
+    this.colorSelectionRing_.addEventListener('focus', this.onColorSelectionRingFocus_);
+    this.colorSelectionRing_.addEventListener('blur', this.onColorSelectionRingBlur_);
   }
 
   get initialized() {
     return this.initialized_;
   }
 
+  onColorSelectionRingFocus_ = () => {
+    this.focused_ = true;
+  }
+
+  onColorSelectionRingBlur_ = () => {
+    this.focused_ = false;
+  }
+
   /**
    * @param {!Point} point
    */
@@ -853,6 +864,7 @@
   constructor(backingColorPalette) {
     super();
 
+    this.setAttribute('tabIndex', 0);
     this.backingColorPalette_ = backingColorPalette;
     this.position_ = new Point(0, 0);
     this.drag_ = false;
@@ -1486,6 +1498,7 @@
   constructor(initialColorFormat) {
     super();
 
+    this.setAttribute('tabIndex', 0);
     this.currentColorFormat_ = initialColorFormat;
     this.hexFormatLabel_ = new FormatLabel(ColorFormat.HEX);
     this.rgbFormatLabel_ = new FormatLabel(ColorFormat.RGB);
diff --git a/third_party/blink/renderer/core/inspector/devtools_agent.cc b/third_party/blink/renderer/core/inspector/devtools_agent.cc
index 8ba37cd..b80640f 100644
--- a/third_party/blink/renderer/core/inspector/devtools_agent.cc
+++ b/third_party/blink/renderer/core/inspector/devtools_agent.cc
@@ -106,13 +106,12 @@
         session_receiver,
     mojo::PendingReceiver<mojom::blink::DevToolsSession> io_session_receiver,
     mojom::blink::DevToolsSessionStatePtr reattach_session_state,
-    bool client_expects_binary_responses,
-    const WTF::String& session_id) {
+    bool client_expects_binary_responses) {
   client_->DebuggerTaskStarted();
   DevToolsSession* session = MakeGarbageCollected<DevToolsSession>(
       this, std::move(host), std::move(session_receiver),
       std::move(io_session_receiver), std::move(reattach_session_state),
-      client_expects_binary_responses, session_id);
+      client_expects_binary_responses);
   sessions_.insert(session);
   client_->DebuggerTaskFinished();
 }
diff --git a/third_party/blink/renderer/core/inspector/devtools_agent.h b/third_party/blink/renderer/core/inspector/devtools_agent.h
index 1b08cd8..509e9e8 100644
--- a/third_party/blink/renderer/core/inspector/devtools_agent.h
+++ b/third_party/blink/renderer/core/inspector/devtools_agent.h
@@ -79,8 +79,7 @@
           main_session,
       mojo::PendingReceiver<mojom::blink::DevToolsSession> io_session,
       mojom::blink::DevToolsSessionStatePtr reattach_session_state,
-      bool client_expects_binary_responses,
-      const WTF::String& session_id) override;
+      bool client_expects_binary_responses) override;
   void InspectElement(const WebPoint& point) override;
   void ReportChildWorkers(bool report,
                           bool wait_for_debugger,
diff --git a/third_party/blink/renderer/core/inspector/devtools_session.cc b/third_party/blink/renderer/core/inspector/devtools_session.cc
index 77e7396..789b094 100644
--- a/third_party/blink/renderer/core/inspector/devtools_session.cc
+++ b/third_party/blink/renderer/core/inspector/devtools_session.cc
@@ -28,14 +28,11 @@
 namespace {
 using ::inspector_protocol_encoding::span;
 using ::inspector_protocol_encoding::SpanFrom;
-using ::inspector_protocol_encoding::cbor::AppendString8EntryToCBORMap;
 using ::inspector_protocol_encoding::cbor::IsCBORMessage;
 using ::inspector_protocol_encoding::json::ConvertCBORToJSON;
 using IPEStatus = ::inspector_protocol_encoding::Status;
 
 const char kV8StateKey[] = "v8";
-static const char kSessionId[] = "sessionId";
-
 bool ShouldInterruptForMethod(const String& method) {
   // Keep in sync with DevToolsSession::ShouldSendOnIO.
   // TODO(dgozman): find a way to share this.
@@ -55,6 +52,20 @@
   return unwrap_message;
 }
 
+mojom::blink::DevToolsMessagePtr WrapMessage(
+    protocol::ProtocolMessage message) {
+  auto result = mojom::blink::DevToolsMessage::New();
+
+  if (message.json.IsEmpty()) {
+    result->data = message.binary.ReleaseVector();
+  } else {
+    WTF::StringUTF8Adaptor adaptor(message.json);
+    result->data =
+        mojo_base::BigBuffer(base::as_bytes(base::make_span(adaptor)));
+  }
+  return result;
+}
+
 protocol::ProtocolMessage ToProtocolMessage(
     std::unique_ptr<v8_inspector::StringBuffer> buffer) {
   protocol::ProtocolMessage message;
@@ -148,14 +159,12 @@
     mojom::blink::DevToolsSessionAssociatedRequest main_request,
     mojom::blink::DevToolsSessionRequest io_request,
     mojom::blink::DevToolsSessionStatePtr reattach_session_state,
-    bool client_expects_binary_responses,
-    const WTF::String& session_id)
+    bool client_expects_binary_responses)
     : agent_(agent),
       binding_(this, std::move(main_request)),
       inspector_backend_dispatcher_(new protocol::UberDispatcher(this)),
       session_state_(std::move(reattach_session_state)),
       client_expects_binary_responses_(client_expects_binary_responses),
-      session_id_(session_id),
       v8_session_state_(kV8StateKey),
       v8_session_state_cbor_(&v8_session_state_,
                              /*default_value=*/{}) {
@@ -314,7 +323,16 @@
   // protocol response in any of them.
   if (WebTestSupport::IsRunningWebTest())
     agent_->FlushProtocolNotifications();
-  host_ptr_->DispatchProtocolResponse(FinalizeMessage(message), call_id,
+
+  mojom::blink::DevToolsMessagePtr serialized = WrapMessage(message);
+  if (!client_expects_binary_responses_) {
+    std::vector<uint8_t> json;
+    IPEStatus status = ConvertCBORToJSON(
+        span<uint8_t>(serialized->data.data(), serialized->data.size()), &json);
+    CHECK(status.ok()) << status.ToASCIIString();
+    serialized->data = mojo_base::BigBuffer(json);
+  }
+  host_ptr_->DispatchProtocolResponse(std::move(serialized), call_id,
                                       session_state_.TakeUpdates());
 }
 
@@ -339,16 +357,16 @@
       std::unique_ptr<v8_inspector::StringBuffer> notification)
       : v8_notification_(std::move(notification)) {}
 
-  protocol::ProtocolMessage Serialize() {
-    protocol::ProtocolMessage result;
+  mojom::blink::DevToolsMessagePtr Serialize() {
+    protocol::ProtocolMessage serialized;
     if (blink_notification_) {
-      result = blink_notification_->serialize(/*binary=*/true);
+      serialized = blink_notification_->serialize(/*binary=*/true);
       blink_notification_.reset();
     } else if (v8_notification_) {
-      result = ToProtocolMessage(std::move(v8_notification_));
+      serialized = ToProtocolMessage(std::move(v8_notification_));
       v8_notification_.reset();
     }
-    return result;
+    return WrapMessage(std::move(serialized));
   }
 
  private:
@@ -382,9 +400,18 @@
   if (v8_session_)
     v8_session_state_cbor_.Set(v8_session_->state());
   for (wtf_size_t i = 0; i < notification_queue_.size(); ++i) {
-    host_ptr_->DispatchProtocolNotification(
-        FinalizeMessage(notification_queue_[i]->Serialize()),
-        session_state_.TakeUpdates());
+    mojom::blink::DevToolsMessagePtr serialized =
+        notification_queue_[i]->Serialize();
+    if (!client_expects_binary_responses_) {
+      std::vector<uint8_t> json;
+      IPEStatus status = ConvertCBORToJSON(
+          span<uint8_t>(serialized->data.data(), serialized->data.size()),
+          &json);
+      CHECK(status.ok()) << status.ToASCIIString();
+      serialized->data = mojo_base::BigBuffer(json);
+    }
+    host_ptr_->DispatchProtocolNotification(std::move(serialized),
+                                            session_state_.TakeUpdates());
   }
   notification_queue_.clear();
 }
@@ -394,23 +421,4 @@
   visitor->Trace(agents_);
 }
 
-blink::mojom::blink::DevToolsMessagePtr DevToolsSession::FinalizeMessage(
-    protocol::ProtocolMessage message) {
-  std::vector<uint8_t> message_to_send = message.binary.ReleaseVector();
-  if (!session_id_.IsEmpty()) {
-    IPEStatus status = AppendString8EntryToCBORMap(
-        SpanFrom(kSessionId), SpanFrom(session_id_.Ascii()), &message_to_send);
-    CHECK(status.ok()) << status.ToASCIIString();
-  }
-  if (!client_expects_binary_responses_) {
-    std::vector<uint8_t> json;
-    IPEStatus status = ConvertCBORToJSON(SpanFrom(message_to_send), &json);
-    CHECK(status.ok()) << status.ToASCIIString();
-    message_to_send = std::move(json);
-  }
-  auto mojo_msg = mojom::blink::DevToolsMessage::New();
-  mojo_msg->data = std::move(message_to_send);
-  return mojo_msg;
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/devtools_session.h b/third_party/blink/renderer/core/inspector/devtools_session.h
index f6389a9..3b7d428 100644
--- a/third_party/blink/renderer/core/inspector/devtools_session.h
+++ b/third_party/blink/renderer/core/inspector/devtools_session.h
@@ -36,8 +36,7 @@
       mojom::blink::DevToolsSessionAssociatedRequest main_request,
       mojom::blink::DevToolsSessionRequest io_request,
       mojom::blink::DevToolsSessionStatePtr reattach_session_state,
-      bool client_expects_binary_responses,
-      const String& session_id);
+      bool client_expects_binary_responses);
   ~DevToolsSession() override;
 
   void ConnectToV8(v8_inspector::V8Inspector*, int context_group_id);
@@ -87,11 +86,6 @@
   void SendProtocolResponse(int call_id,
                             const protocol::ProtocolMessage& message);
 
-  // Inserts the session id (if non-empty) and converts to JSON
-  // if requested by the client.
-  blink::mojom::blink::DevToolsMessagePtr FinalizeMessage(
-      protocol::ProtocolMessage message);
-
   Member<DevToolsAgent> agent_;
   mojo::AssociatedBinding<mojom::blink::DevToolsSession> binding_;
   mojom::blink::DevToolsSessionHostAssociatedPtr host_ptr_;
@@ -103,7 +97,6 @@
   class Notification;
   Vector<std::unique_ptr<Notification>> notification_queue_;
   const bool client_expects_binary_responses_;
-  const String session_id_;
   InspectorAgentState v8_session_state_;
   InspectorAgentState::Bytes v8_session_state_cbor_;
 
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 60c9df4f..f85531e2 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -1844,6 +1844,26 @@
                          StyleRef().UserModify() == EUserModify::kReadOnly);
 }
 
+const ComputedStyle& LayoutObject::SlowEffectiveStyle(
+    NGStyleVariant style_variant) const {
+  switch (style_variant) {
+    case NGStyleVariant::kStandard:
+      return StyleRef();
+    case NGStyleVariant::kFirstLine:
+      return FirstLineStyleRef();
+    case NGStyleVariant::kEllipsis:
+      // The ellipsis is styled according to the line style.
+      // https://www.w3.org/TR/css-overflow-3/#ellipsing-details
+      // Use first-line style if exists since most cases it is the first line.
+      DCHECK(IsInline());
+      if (LayoutObject* block = ContainingBlock())
+        return block->FirstLineStyleRef();
+      return FirstLineStyleRef();
+  }
+  NOTREACHED();
+  return StyleRef();
+}
+
 // Called when an object that was floating or positioned becomes a normal flow
 // object again. We have to make sure the layout tree updates as needed to
 // accommodate the new normal flow object.
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index 2d5c077..074ee188 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -46,6 +46,7 @@
 #include "third_party/blink/renderer/core/layout/layout_object_child_list.h"
 #include "third_party/blink/renderer/core/layout/map_coordinates_flags.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_outline_type.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_style_variant.h"
 #include "third_party/blink/renderer/core/layout/subtree_layout_scope.h"
 #include "third_party/blink/renderer/core/loader/resource/image_resource_observer.h"
 #include "third_party/blink/renderer/core/paint/compositing/compositing_state.h"
@@ -1770,6 +1771,12 @@
   inline const ComputedStyle* Style(bool first_line) const;
   inline const ComputedStyle& StyleRef(bool first_line) const;
 
+  const ComputedStyle& EffectiveStyle(NGStyleVariant style_variant) const {
+    return style_variant == NGStyleVariant::kStandard
+               ? StyleRef()
+               : SlowEffectiveStyle(style_variant);
+  }
+
   static inline Color ResolveColor(const ComputedStyle& style_to_use,
                                    const CSSProperty& color_property) {
     return style_to_use.VisitedDependentColor(color_property);
@@ -2554,6 +2561,8 @@
   };
   virtual bool IsOfType(LayoutObjectType type) const { return false; }
 
+  const ComputedStyle& SlowEffectiveStyle(NGStyleVariant style_variant) const;
+
   // Updates only the local style ptr of the object.  Does not update the state
   // of the object, and so only should be called when the style is known not to
   // have changed (or from SetStyle).
diff --git a/third_party/blink/renderer/core/layout/layout_theme.cc b/third_party/blink/renderer/core/layout/layout_theme.cc
index d62aad7..077ad2b5 100644
--- a/third_party/blink/renderer/core/layout/layout_theme.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme.cc
@@ -712,7 +712,8 @@
   font_description.SetGenericFamily(FontDescription::kNoFamily);
 }
 
-Color LayoutTheme::SystemColor(CSSValueID css_value_id) const {
+Color LayoutTheme::SystemColor(CSSValueID css_value_id,
+                               WebColorScheme color_scheme) const {
   switch (css_value_id) {
     case CSSValueID::kActiveborder:
       return 0xFFFFFFFF;
@@ -793,19 +794,21 @@
 
 Color LayoutTheme::PlatformTextSearchHighlightColor(
     bool active_match,
-    bool in_forced_colors_mode) const {
+    bool in_forced_colors_mode,
+    WebColorScheme color_scheme) const {
   if (active_match) {
     if (in_forced_colors_mode)
-      return GetTheme().SystemColor(CSSValueID::kHighlight);
+      return GetTheme().SystemColor(CSSValueID::kHighlight, color_scheme);
     return Color(255, 150, 50);  // Orange.
   }
   return Color(255, 255, 0);     // Yellow.
 }
 
 Color LayoutTheme::PlatformTextSearchColor(bool active_match,
-                                           bool in_forced_colors_mode) const {
+                                           bool in_forced_colors_mode,
+                                           WebColorScheme color_scheme) const {
   if (in_forced_colors_mode && active_match)
-    return GetTheme().SystemColor(CSSValueID::kHighlighttext);
+    return GetTheme().SystemColor(CSSValueID::kHighlighttext, color_scheme);
   return Color::kBlack;
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_theme.h b/third_party/blink/renderer/core/layout/layout_theme.h
index a68f49a0..80c6604d 100644
--- a/third_party/blink/renderer/core/layout/layout_theme.h
+++ b/third_party/blink/renderer/core/layout/layout_theme.h
@@ -159,9 +159,11 @@
 
   // Highlight and text colors for TextMatches.
   Color PlatformTextSearchHighlightColor(bool active_match,
-                                         bool in_forced_colors_mode) const;
+                                         bool in_forced_colors_mode,
+                                         WebColorScheme color_scheme) const;
   Color PlatformTextSearchColor(bool active_match,
-                                bool in_forced_colors_mode) const;
+                                bool in_forced_colors_mode,
+                                WebColorScheme color_scheme) const;
 
   virtual bool IsFocusRingOutset() const;
   Color FocusRingColor() const;
@@ -191,7 +193,7 @@
                           float& font_size,
                           AtomicString& font_family) const = 0;
   void SystemFont(CSSValueID system_font_id, FontDescription&);
-  virtual Color SystemColor(CSSValueID) const;
+  virtual Color SystemColor(CSSValueID, WebColorScheme color_scheme) const;
 
   // Whether the default system font should have its average character width
   // adjusted to match MS Shell Dlg.
diff --git a/third_party/blink/renderer/core/layout/layout_theme_default.cc b/third_party/blink/renderer/core/layout/layout_theme_default.cc
index 84f738a..35587bc3 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_default.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme_default.cc
@@ -73,7 +73,8 @@
   return false;
 }
 
-Color LayoutThemeDefault::SystemColor(CSSValueID css_value_id) const {
+Color LayoutThemeDefault::SystemColor(CSSValueID css_value_id,
+                                      WebColorScheme color_scheme) const {
   constexpr Color kDefaultButtonGrayColor(0xffdddddd);
   constexpr Color kDefaultMenuColor(0xfff7f7f7);
 
@@ -84,7 +85,7 @@
   }
   if (css_value_id == CSSValueID::kMenu)
     return kDefaultMenuColor;
-  return LayoutTheme::SystemColor(css_value_id);
+  return LayoutTheme::SystemColor(css_value_id, color_scheme);
 }
 
 // Use the Windows style sheets to match their metrics.
diff --git a/third_party/blink/renderer/core/layout/layout_theme_default.h b/third_party/blink/renderer/core/layout/layout_theme_default.h
index 98d035b0..2000ab43 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_default.h
+++ b/third_party/blink/renderer/core/layout/layout_theme_default.h
@@ -42,7 +42,7 @@
   String ExtraDefaultStyleSheet() override;
   String ExtraQuirksStyleSheet() override;
 
-  Color SystemColor(CSSValueID) const override;
+  Color SystemColor(CSSValueID, WebColorScheme color_scheme) const override;
 
   bool ThemeDrawsFocusRing(const ComputedStyle&) const override;
 
diff --git a/third_party/blink/renderer/core/layout/layout_theme_mac.h b/third_party/blink/renderer/core/layout/layout_theme_mac.h
index 0a9b870..20e5a17 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_mac.h
+++ b/third_party/blink/renderer/core/layout/layout_theme_mac.h
@@ -90,7 +90,7 @@
   // Returns the duration of the animation for the progress bar.
   base::TimeDelta AnimationDurationForProgressBar() const override;
 
-  Color SystemColor(CSSValueID) const override;
+  Color SystemColor(CSSValueID, WebColorScheme color_scheme) const override;
 
   bool SupportsSelectionForegroundColors() const override { return false; }
 
diff --git a/third_party/blink/renderer/core/layout/layout_theme_mac.mm b/third_party/blink/renderer/core/layout/layout_theme_mac.mm
index 3d2e0a3..b80640e6 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_mac.mm
+++ b/third_party/blink/renderer/core/layout/layout_theme_mac.mm
@@ -259,7 +259,9 @@
   if (UsesTestModeFocusRingColor())
     return kOldAquaFocusRingColor;
 
-  return SystemColor(CSSValueID::kWebkitFocusRingColor);
+  // TODO(crbug.com/929098) Need to pass an appropriate color scheme here.
+  return SystemColor(CSSValueID::kWebkitFocusRingColor,
+                     ComputedStyle::InitialStyle().UsedColorScheme());
 }
 
 Color LayoutThemeMac::PlatformInactiveListBoxSelectionBackgroundColor() const {
@@ -329,7 +331,8 @@
   LayoutTheme::PlatformColorsDidChange();
 }
 
-Color LayoutThemeMac::SystemColor(CSSValueID css_value_id) const {
+Color LayoutThemeMac::SystemColor(CSSValueID css_value_id,
+                                  WebColorScheme color_scheme) const {
   {
     HashMap<CSSValueID, RGBA32>::iterator it =
         system_color_cache_.find(css_value_id);
@@ -442,7 +445,7 @@
   }
 
   if (needs_fallback)
-    color = LayoutTheme::SystemColor(css_value_id);
+    color = LayoutTheme::SystemColor(css_value_id, color_scheme);
 
   system_color_cache_.Set(css_value_id, color.Rgb());
 
diff --git a/third_party/blink/renderer/core/layout/layout_theme_win.cc b/third_party/blink/renderer/core/layout/layout_theme_win.cc
index 62e366d..43fd8ef9 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_win.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme_win.cc
@@ -19,9 +19,10 @@
   return *layout_theme;
 }
 
-Color LayoutThemeWin::SystemColor(CSSValueID css_value_id) const {
+Color LayoutThemeWin::SystemColor(CSSValueID css_value_id,
+                                  WebColorScheme color_scheme) const {
   if (!RuntimeEnabledFeatures::UseWindowsSystemColorsEnabled()) {
-    return LayoutThemeDefault::SystemColor(css_value_id);
+    return LayoutThemeDefault::SystemColor(css_value_id, color_scheme);
   }
 
   int system_index;
@@ -53,7 +54,7 @@
       system_index = COLOR_WINDOWTEXT;
       break;
     default:
-      return LayoutThemeDefault::SystemColor(css_value_id);
+      return LayoutThemeDefault::SystemColor(css_value_id, color_scheme);
   }
 
   return SystemColorBySystemIndex(system_index);
diff --git a/third_party/blink/renderer/core/layout/layout_theme_win.h b/third_party/blink/renderer/core/layout/layout_theme_win.h
index 4b772ba..842870a4 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_win.h
+++ b/third_party/blink/renderer/core/layout/layout_theme_win.h
@@ -13,7 +13,8 @@
  public:
   static scoped_refptr<LayoutTheme> Create();
 
-  Color SystemColor(CSSValueID css_value_id) const override;
+  Color SystemColor(CSSValueID css_value_id,
+                    WebColorScheme color_scheme) const override;
 
  private:
   static Color SystemColorBySystemIndex(int system_index);
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
index 6ece8b2bd..9acbfa50 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
@@ -7,6 +7,7 @@
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/geometry/logical_offset.h"
+#include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h"
 #include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h"
@@ -72,8 +73,14 @@
   bool UsesFirstLineStyle() const {
     return StyleVariant() == NGStyleVariant::kFirstLine;
   }
+  // Returns the style for this fragment.
+  //
+  // For a line box, this returns the style of the containing block. This mostly
+  // represents the style for the line box, except 1) |style.Direction()| maybe
+  // incorrect, use |BaseDirection()| instead, and 2) margin/border/padding,
+  // background etc. do not apply to the line box.
   const ComputedStyle& Style() const {
-    return layout_object_->StyleRef(UsesFirstLineStyle());
+    return layout_object_->EffectiveStyle(StyleVariant());
   }
   const LayoutObject* GetLayoutObject() const { return layout_object_; }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
index 0ee7f7b..d71cc25 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
@@ -113,7 +113,9 @@
   if (!inset_end_length.IsAuto()) {
     inset_end = MinimumValueForLength(inset_end_length, available_size);
   }
-
+#if DCHECK_IS_ON()
+  bool values_might_be_saturated = false;
+#endif
   // Solving the equation:
   // |inset_start| + |margin_start| + |size| + |margin_end| + |inset_end| =
   // |available_size|
@@ -145,6 +147,10 @@
     // Compute margins.
     LayoutUnit margin_space =
         available_size - *inset_start - *inset_end - *size;
+#if DCHECK_IS_ON()
+    values_might_be_saturated |= margin_space.MightBeSaturated();
+#endif
+
     if (!margin_start && !margin_end) {
       // When both margins are auto.
       if (margin_space > 0 || is_block_direction) {
@@ -167,6 +173,9 @@
     } else {
       // Are the values over-constrained?
       LayoutUnit margin_extra = margin_space - *margin_start - *margin_end;
+#if DCHECK_IS_ON()
+      values_might_be_saturated |= margin_extra.MightBeSaturated();
+#endif
       if (margin_extra) {
         // Relax the end.
         if (is_start_dominant)
@@ -226,10 +235,12 @@
   }
 
 #if DCHECK_IS_ON()
+  values_might_be_saturated |=
+      inset_start->MightBeSaturated() || inset_end->MightBeSaturated() ||
+      size->MightBeSaturated() || margin_start->MightBeSaturated() ||
+      margin_end->MightBeSaturated() || available_size.MightBeSaturated();
   // The DCHECK is useful, but only holds true if the values aren't saturated.
-  if (!inset_start->MightBeSaturated() && !inset_end->MightBeSaturated() &&
-      !size->MightBeSaturated() && !margin_start->MightBeSaturated() &&
-      !margin_end->MightBeSaturated() && !available_size.MightBeSaturated()) {
+  if (!values_might_be_saturated) {
     DCHECK_EQ(available_size,
               *inset_start + *inset_end + *margin_start + *margin_end + *size);
   }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
index 38a958f4..259d886 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
@@ -134,6 +134,11 @@
     PropagateBreak(child_layout_result);
 }
 
+void NGBoxFragmentBuilder::AddBreakToken(
+    scoped_refptr<const NGBreakToken> token) {
+  child_break_tokens_.push_back(std::move(token));
+}
+
 void NGBoxFragmentBuilder::AddOutOfFlowLegacyCandidate(
     NGBlockNode node,
     const NGLogicalStaticPosition& static_position,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
index feb649c0..6f8858ab9 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
@@ -99,6 +99,8 @@
                  const LogicalOffset,
                  const LayoutInline* = nullptr);
 
+  void AddBreakToken(scoped_refptr<const NGBreakToken>);
+
   void AddOutOfFlowLegacyCandidate(NGBlockNode,
                                    const NGLogicalStaticPosition&,
                                    const LayoutInline* inline_container);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
index 123a1f3..a13734cb 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
@@ -21,12 +21,6 @@
 
 namespace {
 
-inline bool NeedsColumnBalancing(LayoutUnit block_size,
-                                 const ComputedStyle& style) {
-  return block_size == kIndefiniteSize ||
-         style.GetColumnFill() == EColumnFill::kBalance;
-}
-
 LayoutUnit CalculateColumnContentBlockSize(
     const NGPhysicalContainerFragment& fragment,
     bool multicol_is_horizontal_writing_mode) {
@@ -89,6 +83,7 @@
 
   if (ConstraintSpace().HasBlockFragmentation())
     container_builder_.SetHasBlockFragmentation();
+  container_builder_.SetIsBlockFragmentationContextRoot();
 
   // Omit leading border+padding+scrollbar for all fragments but the first.
   if (!IsResumingLayout(BreakToken()))
@@ -107,8 +102,8 @@
     // container.
     const auto child_tokens = token->ChildBreakTokens();
     if (child_tokens.size()) {
-      child_break_token =
-          To<NGBlockBreakToken>(child_tokens[child_tokens.size() - 1]);
+      DCHECK_EQ(child_tokens.size(), 1u);
+      child_break_token = child_tokens[0];
     }
   }
 
@@ -147,6 +142,9 @@
   }
 
   if (ConstraintSpace().HasBlockFragmentation()) {
+    if (child_break_token)
+      container_builder_.AddBreakToken(std::move(child_break_token));
+
     // In addition to establishing one, we're nested inside another
     // fragmentation context.
     FinishFragmentation(&container_builder_, block_size, intrinsic_block_size_,
@@ -207,28 +205,54 @@
 scoped_refptr<const NGBreakToken> NGColumnLayoutAlgorithm::LayoutRow(
     const NGBlockBreakToken* next_column_token) {
   LayoutUnit column_block_offset = intrinsic_block_size_;
-  LogicalSize column_size = CalculateColumnSize(content_box_size_);
+  LogicalSize column_size(column_inline_size_, content_box_size_.block_size);
+
+  // If block-size is non-auto, subtract the space for content we've consumed in
+  // previous fragments. This is necessary when we're nested inside another
+  // fragmentation context.
+  if (ConstraintSpace().HasBlockFragmentation() &&
+      column_size.block_size != kIndefiniteSize) {
+    if (const auto* token = BreakToken()) {
+      column_size.block_size -= token->ConsumedBlockSize();
+      column_size.block_size = column_size.block_size.ClampNegativeToZero();
+    }
+  }
+
+  // We balance if block-size is unconstrained, or when we're explicitly told
+  // to. Note that the block-size may be constrained by outer fragmentation
+  // contexts, not just by a block-size specified on this multicol container.
+  bool balance_columns = Style().GetColumnFill() == EColumnFill::kBalance ||
+                         (column_size.block_size == kIndefiniteSize &&
+                          !ConstraintSpace().HasBlockFragmentation());
+
+  if (balance_columns) {
+    column_size.block_size =
+        CalculateBalancedColumnBlockSize(column_size, next_column_token);
+  }
 
   bool needs_more_fragments_in_outer = false;
   if (ConstraintSpace().HasBlockFragmentation()) {
-    // Subtract the space for content we've processed in previous fragments.
-    if (const auto* token = BreakToken())
-      column_size.block_size -= token->ConsumedBlockSize();
+    LayoutUnit available_outer_space =
+        ConstraintSpace().FragmentainerSpaceAtBfcStart() - column_block_offset;
+
+    // TODO(mstensho): This should never be negative, or even zero. Turn into a
+    // DCHECK when the underlying problem is fixed.
+    available_outer_space = available_outer_space.ClampNegativeToZero();
 
     // Check if we can fit everything (that's remaining), block-wise, within the
     // current outer fragmentainer. If we can't, we need to adjust the block
     // size, and allow the multicol container to continue in a subsequent outer
-    // fragmentainer.
-    LayoutUnit available_outer_space =
-        ConstraintSpace().FragmentainerSpaceAtBfcStart() - column_block_offset;
-    if (column_size.block_size > available_outer_space) {
+    // fragmentainer. Note that we also need to handle indefinite block-size,
+    // because that may happen in a nested multicol container with auto
+    // block-size and column balancing disabled.
+    if (column_size.block_size > available_outer_space ||
+        column_size.block_size == kIndefiniteSize) {
       column_size.block_size = available_outer_space;
       needs_more_fragments_in_outer = true;
     }
   }
 
-  bool balance_columns =
-      NeedsColumnBalancing(content_box_size_.block_size, Style());
+  DCHECK_GE(column_size.block_size, LayoutUnit());
 
   // New column fragments won't be added to the fragment builder right away,
   // since we may need to delete them and try again with a different block-size
@@ -243,6 +267,10 @@
     scoped_refptr<const NGBlockBreakToken> column_break_token =
         next_column_token;
 
+    // This is the first column in this fragmentation context if there are no
+    // preceding columns in this row and there are also no preceding rows.
+    bool is_first_fragmentainer = !column_break_token && !BreakToken();
+
     LayoutUnit column_inline_offset(border_scrollbar_padding_.inline_start);
     int actual_column_count = 0;
     int forced_break_count = 0;
@@ -254,10 +282,6 @@
     LayoutUnit minimal_space_shortage(LayoutUnit::Max());
 
     do {
-      // This is the first column in this fragmentation context if there are no
-      // preceding columns in this row and there are also no preceding rows.
-      bool is_first_fragmentainer = !column_break_token && !BreakToken();
-
       // Lay out one column. Each column will become a fragment.
       NGConstraintSpace child_space = CreateConstraintSpaceForColumns(
           column_size, is_first_fragmentainer, balance_columns);
@@ -305,6 +329,8 @@
         container_builder_.SetDidBreak();
         break;
       }
+
+      is_first_fragmentainer = false;
     } while (column_break_token);
 
     // TODO(mstensho): Nested column balancing.
@@ -327,13 +353,12 @@
     // cases, that piece of information may have to be propagated to the outer
     // multicol, and instead stretch there (not here). We have no such mechanism
     // in place yet.
-    if (actual_column_count > used_column_count_ &&
+    if (balance_columns && actual_column_count > used_column_count_ &&
         actual_column_count > forced_break_count + 1 &&
         minimal_space_shortage != LayoutUnit::Max() &&
         !ConstraintSpace().IsInsideBalancedColumns()) {
-      LayoutUnit new_column_block_size =
-          StretchColumnBlockSize(minimal_space_shortage, column_size.block_size,
-                                 content_box_size_.block_size);
+      LayoutUnit new_column_block_size = StretchColumnBlockSize(
+          minimal_space_shortage, column_size.block_size);
 
       DCHECK_GE(new_column_block_size, column_size.block_size);
       if (new_column_block_size > column_size.block_size) {
@@ -370,37 +395,22 @@
   return last_break_token;
 }
 
-LogicalSize NGColumnLayoutAlgorithm::CalculateColumnSize(
-    const LogicalSize& content_box_size) {
-  LogicalSize column_size = content_box_size;
-  DCHECK_GE(column_size.inline_size, LayoutUnit());
-  column_size.inline_size =
-      ResolveUsedColumnInlineSize(column_size.inline_size, Style());
-
-  if (NeedsColumnBalancing(column_size.block_size, Style())) {
-    int used_count =
-        ResolveUsedColumnCount(content_box_size.inline_size, Style());
-    column_size.block_size =
-        CalculateBalancedColumnBlockSize(column_size, used_count);
-  }
-
-  return column_size;
-}
-
 LayoutUnit NGColumnLayoutAlgorithm::CalculateBalancedColumnBlockSize(
     const LogicalSize& column_size,
-    int column_count) {
+    const NGBlockBreakToken* child_break_token) {
   // To calculate a balanced column size, we need to figure out how tall our
   // content is. To do that we need to lay out. Create a special constraint
   // space for column balancing, without splitting into fragmentainers. It will
   // make us lay out all the multicol content as one single tall strip. When
   // we're done with this layout pass, we can examine the result and calculate
   // an ideal column block size.
+  int column_count =
+      ResolveUsedColumnCount(content_box_size_.inline_size, Style());
   NGConstraintSpace space = CreateConstraintSpaceForBalancing(column_size);
   NGFragmentGeometry fragment_geometry =
       CalculateInitialFragmentGeometry(space, Node());
   NGBlockLayoutAlgorithm balancing_algorithm(
-      {Node(), fragment_geometry, space});
+      {Node(), fragment_geometry, space, child_break_token});
   scoped_refptr<const NGLayoutResult> result = balancing_algorithm.Layout();
 
   LayoutUnit single_strip_block_size = CalculateColumnContentBlockSize(
@@ -421,10 +431,7 @@
 
 LayoutUnit NGColumnLayoutAlgorithm::StretchColumnBlockSize(
     LayoutUnit minimal_space_shortage,
-    LayoutUnit current_column_size,
-    LayoutUnit container_content_box_block_size) const {
-  if (!NeedsColumnBalancing(container_content_box_block_size, Style()))
-    return current_column_size;
+    LayoutUnit current_column_size) const {
   LayoutUnit length = current_column_size + minimal_space_shortage;
   // Honor {,min-,max-}{height,width} properties.
   return ConstrainColumnBlockSize(length);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h
index 0da9dad..a91e2e39 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h
@@ -31,17 +31,14 @@
   scoped_refptr<const NGBreakToken> LayoutRow(
       const NGBlockBreakToken* next_column_token);
 
-  LogicalSize CalculateColumnSize(const LogicalSize& content_box_size);
-  LayoutUnit CalculateBalancedColumnBlockSize(const LogicalSize& column_size,
-                                              int column_count);
+  LayoutUnit CalculateBalancedColumnBlockSize(
+      const LogicalSize& column_size,
+      const NGBlockBreakToken* child_break_token);
 
-  // Stretch the column length, if allowed. We do this during column balancing,
-  // when we discover that the current length isn't large enough to fit all
-  // content.
-  LayoutUnit StretchColumnBlockSize(
-      LayoutUnit minimal_space_shortage,
-      LayoutUnit current_column_size,
-      LayoutUnit container_content_box_block_size) const;
+  // Stretch the column length. We do this during column balancing, when we
+  // discover that the current length isn't large enough to fit all content.
+  LayoutUnit StretchColumnBlockSize(LayoutUnit minimal_space_shortage,
+                                    LayoutUnit current_column_size) const;
 
   LayoutUnit ConstrainColumnBlockSize(LayoutUnit size) const;
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc
index c804777..648dff9 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc
@@ -3222,6 +3222,52 @@
   EXPECT_EQ(expectation, dump);
 }
 
+TEST_F(NGColumnLayoutAlgorithmTest, NestedUnbalancedInnerAutoHeight) {
+  // The fragments generated by an inner multicol are block-size constrained by
+  // the outer multicol, so if column-fill is auto, we shouldn't forcefully
+  // balance.
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .outer { columns:2; height:50px; column-fill:auto; width:210px; }
+      .inner { columns:2; column-fill:auto; }
+      .outer, .inner { column-gap:10px; }
+      .content { break-inside:avoid; height:20px; }
+    </style>
+    <div id="container">
+      <div class="outer">
+        <div class="inner">
+          <div class="content"></div>
+          <div class="content"></div>
+          <div class="content"></div>
+          <div class="content"></div>
+          <div class="content"></div>
+          <div class="content"></div>
+        </div>
+      </div>
+    </div>
+  )HTML");
+
+  String dump = DumpFragmentTree(GetElementById("container"));
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x50
+    offset:0,0 size:210x50
+      offset:0,0 size:100x50
+        offset:0,0 size:100x50
+          offset:0,0 size:45x50
+            offset:0,0 size:45x20
+            offset:0,20 size:45x20
+          offset:55,0 size:45x50
+            offset:0,0 size:45x20
+            offset:0,20 size:45x20
+      offset:110,0 size:100x50
+        offset:0,0 size:100x50
+          offset:0,0 size:45x40
+            offset:0,0 size:45x20
+            offset:0,20 size:45x20
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
 TEST_F(NGColumnLayoutAlgorithmTest, AbsposFitsInOneColumn) {
   SetBodyInnerHTML(R"HTML(
     <div id="container">
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
index 145e401..afe4ffa 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
@@ -105,26 +105,29 @@
     }
   }
 
-  // Collect any (block) break tokens.
-  const NGBreakToken* child_break_token = child.BreakToken();
-  if (child_break_token && has_block_fragmentation_) {
-    switch (child.Type()) {
-      case NGPhysicalFragment::kFragmentBox:
-      case NGPhysicalFragment::kFragmentRenderedLegend:
-        if (To<NGBlockBreakToken>(child_break_token)->HasLastResortBreak())
-          has_last_resort_break_ = true;
-        child_break_tokens_.push_back(child_break_token);
-        break;
-      case NGPhysicalFragment::kFragmentLineBox:
-        // NGInlineNode produces multiple line boxes in an anonymous box. We
-        // won't know up front which line box to insert a fragment break before
-        // (due to widows), so keep them all until we know.
-        inline_break_tokens_.push_back(child_break_token);
-        break;
-      case NGPhysicalFragment::kFragmentText:
-      default:
-        NOTREACHED();
-        break;
+  // Collect any (block) break tokens, unless this is a fragmentation context
+  // root. Break tokens should only escape a fragmentation context at the
+  // discretion of the fragmentation context.
+  if (has_block_fragmentation_ && !is_fragmentation_context_root_) {
+    if (const NGBreakToken* child_break_token = child.BreakToken()) {
+      switch (child.Type()) {
+        case NGPhysicalFragment::kFragmentBox:
+        case NGPhysicalFragment::kFragmentRenderedLegend:
+          if (To<NGBlockBreakToken>(child_break_token)->HasLastResortBreak())
+            has_last_resort_break_ = true;
+          child_break_tokens_.push_back(child_break_token);
+          break;
+        case NGPhysicalFragment::kFragmentLineBox:
+          // NGInlineNode produces multiple line boxes in an anonymous box. We
+          // won't know up front which line box to insert a fragment break
+          // before (due to widows), so keep them all until we know.
+          inline_break_tokens_.push_back(child_break_token);
+          break;
+        case NGPhysicalFragment::kFragmentText:
+        default:
+          NOTREACHED();
+          break;
+      }
     }
   }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
index 0827130d..f3a810e6f 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
@@ -160,6 +160,12 @@
 
   void SetHasBlockFragmentation() { has_block_fragmentation_ = true; }
 
+  // Set for any node that establishes a fragmentation context, such as multicol
+  // containers.
+  void SetIsBlockFragmentationContextRoot() {
+    is_fragmentation_context_root_ = true;
+  }
+
   const NGConstraintSpace* ConstraintSpace() const { return space_; }
 
 #if DCHECK_IS_ON()
@@ -215,6 +221,7 @@
   bool has_orthogonal_flow_roots_ = false;
   bool has_descendant_that_depends_on_percentage_block_size_ = false;
   bool has_block_fragmentation_ = false;
+  bool is_fragmentation_context_root_ = false;
   bool may_have_descendant_above_block_start_ = false;
 };
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc
index 4014cf8..b630a5ab 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc
@@ -43,6 +43,8 @@
   // TODO(mstensho): Handle auto block size.
   LogicalOffset page_progression(LayoutUnit(), page_size.block_size);
 
+  container_builder_.SetIsBlockFragmentationContextRoot();
+
   do {
     // Lay out one page. Each page will become a fragment.
     NGFragmentGeometry fragment_geometry =
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
index 4d88bbcf1..c916a7e0 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
@@ -269,28 +269,6 @@
   }
 }
 
-const ComputedStyle& NGPhysicalFragment::SlowEffectiveStyle() const {
-  switch (StyleVariant()) {
-    case NGStyleVariant::kStandard:
-      return layout_object_.StyleRef();
-    case NGStyleVariant::kFirstLine:
-      return layout_object_.FirstLineStyleRef();
-    case NGStyleVariant::kEllipsis:
-      DCHECK_EQ(Type(), kFragmentText);
-      DCHECK_EQ(StyleVariant(), NGStyleVariant::kEllipsis);
-      // The ellipsis is styled according to the line style.
-      // https://drafts.csswg.org/css-ui/#ellipsing-details
-      // Use first-line style if exists since most cases it is the first line.
-      // TODO(kojii): Should determine if it's really in the first line.
-      DCHECK(layout_object_.IsInline());
-      if (LayoutObject* block = layout_object_.ContainingBlock())
-        return block->FirstLineStyleRef();
-      return layout_object_.FirstLineStyleRef();
-  }
-  NOTREACHED();
-  return layout_object_.StyleRef();
-}
-
 Node* NGPhysicalFragment::GetNode() const {
   return !IsLineBox() ? layout_object_.GetNode() : nullptr;
 }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
index 9311bed..7d04ca4b 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
@@ -171,9 +171,7 @@
   // incorrect, use |BaseDirection()| instead, and 2) margin/border/padding,
   // background etc. do not apply to the line box.
   const ComputedStyle& Style() const {
-    return StyleVariant() == NGStyleVariant::kStandard
-               ? layout_object_.StyleRef()
-               : SlowEffectiveStyle();
+    return layout_object_.EffectiveStyle(StyleVariant());
   }
   Node* GetNode() const;
 
diff --git a/third_party/blink/renderer/core/paint/document_marker_painter.cc b/third_party/blink/renderer/core/paint/document_marker_painter.cc
index 147f153..d21714e 100644
--- a/third_party/blink/renderer/core/paint/document_marker_painter.cc
+++ b/third_party/blink/renderer/core/paint/document_marker_painter.cc
@@ -237,7 +237,7 @@
     const TextMatchMarker& marker,
     bool in_forced_colors_mode) {
   const Color text_color = LayoutTheme::GetTheme().PlatformTextSearchColor(
-      marker.IsActiveMatch(), in_forced_colors_mode);
+      marker.IsActiveMatch(), in_forced_colors_mode, style.UsedColorScheme());
   if (style.VisitedDependentColor(GetCSSPropertyColor()) == text_color)
     return {};
 
diff --git a/third_party/blink/renderer/core/paint/inline_text_box_painter.cc b/third_party/blink/renderer/core/paint/inline_text_box_painter.cc
index dc3e5e4..64c9cd83 100644
--- a/third_party/blink/renderer/core/paint/inline_text_box_painter.cc
+++ b/third_party/blink/renderer/core/paint/inline_text_box_painter.cc
@@ -884,7 +884,8 @@
 
   Color color = LayoutTheme::GetTheme().PlatformTextSearchHighlightColor(
       marker.IsActiveMatch(),
-      inline_text_box_.GetLineLayoutItem().GetDocument().InForcedColorsMode());
+      inline_text_box_.GetLineLayoutItem().GetDocument().InForcedColorsMode(),
+      style.UsedColorScheme());
   GraphicsContext& context = paint_info.context;
   GraphicsContextStateSaver state_saver(context);
 
diff --git a/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
index 23129467..b174e978 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
@@ -174,7 +174,8 @@
           const Color color =
               LayoutTheme::GetTheme().PlatformTextSearchHighlightColor(
                   text_match_marker.IsActiveMatch(),
-                  text_fragment.GetNode()->GetDocument().InForcedColorsMode());
+                  text_fragment.GetNode()->GetDocument().InForcedColorsMode(),
+                  style.UsedColorScheme());
           PaintRect(
               context, PhysicalOffset(box_origin),
               text_fragment.LocalRect(paint_start_offset, paint_end_offset),
diff --git a/third_party/blink/renderer/core/paint/selection_painting_utils.cc b/third_party/blink/renderer/core/paint/selection_painting_utils.cc
index a6c6279..f5fe71f 100644
--- a/third_party/blink/renderer/core/paint/selection_painting_utils.cc
+++ b/third_party/blink/renderer/core/paint/selection_painting_utils.cc
@@ -78,13 +78,17 @@
   if (scoped_refptr<ComputedStyle> pseudo_style =
           GetUncachedSelectionStyle(node)) {
     if (document.InForcedColorsMode() &&
-        pseudo_style->ForcedColorAdjust() != EForcedColorAdjust::kNone)
-      return LayoutTheme::GetTheme().SystemColor(CSSValueID::kHighlighttext);
+        pseudo_style->ForcedColorAdjust() != EForcedColorAdjust::kNone) {
+      return LayoutTheme::GetTheme().SystemColor(CSSValueID::kHighlighttext,
+                                                 style.UsedColorScheme());
+    }
     return pseudo_style->VisitedDependentColor(color_property);
   }
 
-  if (document.InForcedColorsMode())
-    return LayoutTheme::GetTheme().SystemColor(CSSValueID::kHighlighttext);
+  if (document.InForcedColorsMode()) {
+    return LayoutTheme::GetTheme().SystemColor(CSSValueID::kHighlighttext,
+                                               style.UsedColorScheme());
+  }
 
   if (!LayoutTheme::GetTheme().SupportsSelectionForegroundColors())
     return style.VisitedDependentColor(color_property);
@@ -115,14 +119,18 @@
   if (scoped_refptr<ComputedStyle> pseudo_style =
           GetUncachedSelectionStyle(node)) {
     if (document.InForcedColorsMode() &&
-        pseudo_style->ForcedColorAdjust() != EForcedColorAdjust::kNone)
-      return LayoutTheme::GetTheme().SystemColor(CSSValueID::kHighlight);
+        pseudo_style->ForcedColorAdjust() != EForcedColorAdjust::kNone) {
+      return LayoutTheme::GetTheme().SystemColor(CSSValueID::kHighlight,
+                                                 style.UsedColorScheme());
+    }
     return pseudo_style->VisitedDependentColor(GetCSSPropertyBackgroundColor())
         .BlendWithWhite();
   }
 
-  if (document.InForcedColorsMode())
-    return LayoutTheme::GetTheme().SystemColor(CSSValueID::kHighlight);
+  if (document.InForcedColorsMode()) {
+    return LayoutTheme::GetTheme().SystemColor(CSSValueID::kHighlight,
+                                               style.UsedColorScheme());
+  }
 
   return document.GetFrame()->Selection().FrameIsFocusedAndActive()
              ? LayoutTheme::GetTheme().ActiveSelectionBackgroundColor()
diff --git a/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc b/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc
index 6d1b3f9..e0c348e 100644
--- a/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc
@@ -599,9 +599,11 @@
     return;
 
   Color text_color = LayoutTheme::GetTheme().PlatformTextSearchColor(
-      marker.IsActiveMatch(), svg_inline_text_box_.GetLineLayoutItem()
-                                  .GetDocument()
-                                  .InForcedColorsMode());
+      marker.IsActiveMatch(),
+      svg_inline_text_box_.GetLineLayoutItem()
+          .GetDocument()
+          .InForcedColorsMode(),
+      style.UsedColorScheme());
 
   PaintFlags fill_flags;
   fill_flags.setColor(text_color.Rgb());
@@ -644,9 +646,11 @@
     return;
 
   Color color = LayoutTheme::GetTheme().PlatformTextSearchHighlightColor(
-      marker.IsActiveMatch(), svg_inline_text_box_.GetLineLayoutItem()
-                                  .GetDocument()
-                                  .InForcedColorsMode());
+      marker.IsActiveMatch(),
+      svg_inline_text_box_.GetLineLayoutItem()
+          .GetDocument()
+          .InForcedColorsMode(),
+      style.UsedColorScheme());
   for (const SVGTextFragmentWithRange& text_match_info : text_match_info_list) {
     const SVGTextFragment& fragment = text_match_info.fragment;
 
diff --git a/third_party/blink/renderer/core/svg/svg_use_element.cc b/third_party/blink/renderer/core/svg/svg_use_element.cc
index eea9dae..eb92759 100644
--- a/third_party/blink/renderer/core/svg/svg_use_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_use_element.cc
@@ -106,9 +106,9 @@
 }
 
 #if DCHECK_IS_ON()
-static inline bool IsWellFormedDocument(Document* document) {
-  if (document->IsXMLDocument())
-    return static_cast<XMLDocumentParser*>(document->Parser())->WellFormed();
+static inline bool IsWellFormedDocument(const Document& document) {
+  if (document.IsXMLDocument())
+    return static_cast<XMLDocumentParser*>(document.Parser())->WellFormed();
   return true;
 }
 #endif
@@ -121,7 +121,7 @@
   if (!root_parent.isConnected())
     return kInsertionDone;
 #if DCHECK_IS_ON()
-  DCHECK(!target_element_instance_ || !IsWellFormedDocument(&GetDocument()));
+  DCHECK(!target_element_instance_ || !IsWellFormedDocument(GetDocument()));
 #endif
   InvalidateShadowTree();
   return kInsertionDone;
@@ -315,21 +315,23 @@
 }
 
 void SVGUseElement::BuildPendingResource() {
-  // Do not build the shadow/instance tree for nested <use> elements
-  // because they will get expanded in a second pass -- see
-  // ExpandUseElementsInShadowTree().
-  if (InUseShadowTree())
-    return;
+  // This runs just before computed style is updated, so this SVGUseElement
+  // should always be connected to the Document. It should also not be an
+  // SVGUseElement that is part of a shadow tree, since we should never
+  // schedule shadow tree updates for those.
+  DCHECK(!InUseShadowTree());
+  DCHECK(isConnected());
+
   DetachShadowTree();
   ClearResourceReference();
   CancelShadowTreeRecreation();
-  if (!isConnected())
-    return;
-  auto* target = DynamicTo<SVGElement>(ResolveTargetElement(kAddObserver));
-  // TODO(fs): Why would the Element not be "connected" at this point?
-  if (target && target->isConnected())
-    AttachShadowTree(*target);
+  DCHECK(isConnected());
 
+  auto* target = DynamicTo<SVGElement>(ResolveTargetElement(kAddObserver));
+  if (target) {
+    DCHECK(target->isConnected());
+    AttachShadowTree(*target);
+  }
   DCHECK(!needs_shadow_tree_recreation_);
 }
 
@@ -395,9 +397,8 @@
   }
 }
 
-Element* SVGUseElement::CreateInstanceTree(SVGElement& target_root) const {
-  Element* instance_root = &target_root.CloneWithChildren();
-  DCHECK(instance_root->IsSVGElement());
+SVGElement* SVGUseElement::CreateInstanceTree(SVGElement& target_root) const {
+  SVGElement* instance_root = &To<SVGElement>(target_root.CloneWithChildren());
   if (IsSVGSymbolElement(target_root)) {
     // Spec: The referenced 'symbol' and its contents are deep-cloned into
     // the generated tree, with the exception that the 'symbol' is replaced
@@ -416,10 +417,9 @@
     MoveChildrenToReplacementElement(*instance_root, *svg_element);
     instance_root = svg_element;
   }
-  TransferUseWidthAndHeightIfNeeded(*this, To<SVGElement>(*instance_root),
-                                    target_root);
-  AssociateCorrespondingElements(target_root, To<SVGElement>(*instance_root));
-  RemoveDisallowedElementsFromSubtree(To<SVGElement>(*instance_root));
+  TransferUseWidthAndHeightIfNeeded(*this, *instance_root, target_root);
+  AssociateCorrespondingElements(target_root, *instance_root);
+  RemoveDisallowedElementsFromSubtree(*instance_root);
   return instance_root;
 }
 
@@ -435,10 +435,9 @@
   // Set up root SVG element in shadow tree.
   // Clone the target subtree into the shadow tree, not handling <use> and
   // <symbol> yet.
-  Element* instance_root = CreateInstanceTree(target);
-  target_element_instance_ = To<SVGElement>(instance_root);
+  target_element_instance_ = CreateInstanceTree(target);
   ShadowRoot& shadow_root = UseShadowRoot();
-  shadow_root.AppendChild(instance_root);
+  shadow_root.AppendChild(target_element_instance_);
 
   AddReferencesToFirstDegreeNestedUseElements(target);
 
@@ -486,8 +485,6 @@
 
 LayoutObject* SVGUseElement::CreateLayoutObject(const ComputedStyle& style,
                                                 LegacyLayout) {
-  if (style.Display() == EDisplay::kContents)
-    return nullptr;
   return new LayoutSVGTransformableContainer(this);
 }
 
@@ -630,12 +627,10 @@
     if (target)
       clone_parent->AppendChild(use->CreateInstanceTree(*target));
 
-    SVGElement* replacing_element(clone_parent);
-
     // Replace <use> with referenced content.
     use->parentNode()->ReplaceChild(clone_parent, use);
 
-    use = Traversal<SVGUseElement>::Next(*replacing_element, &shadow_root);
+    use = Traversal<SVGUseElement>::Next(*clone_parent, &shadow_root);
   }
 }
 
diff --git a/third_party/blink/renderer/core/svg/svg_use_element.h b/third_party/blink/renderer/core/svg/svg_use_element.h
index 8536749..abb15090 100644
--- a/third_party/blink/renderer/core/svg/svg_use_element.h
+++ b/third_party/blink/renderer/core/svg/svg_use_element.h
@@ -106,7 +106,7 @@
   void AttachShadowTree(SVGElement& target);
   void DetachShadowTree();
   void ClearInstanceRoot();
-  Element* CreateInstanceTree(SVGElement& target_root) const;
+  SVGElement* CreateInstanceTree(SVGElement& target_root) const;
   void ClearResourceReference();
   bool HasCycleUseReferencing(const ContainerNode& target_instance,
                               const SVGElement& new_target) const;
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.cc
index 70c0445..96540a1 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
+#include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_gradient.h"
@@ -50,13 +51,14 @@
 };
 
 static ColorParseResult ParseColor(Color& parsed_color,
-                                   const String& color_string) {
+                                   const String& color_string,
+                                   WebColorScheme color_scheme) {
   if (DeprecatedEqualIgnoringCase(color_string, "currentcolor"))
     return kParsedCurrentColor;
   const bool kUseStrictParsing = true;
   if (CSSParser::ParseColor(parsed_color, color_string, kUseStrictParsing))
     return kParsedRGBA;
-  if (CSSParser::ParseSystemColor(parsed_color, color_string))
+  if (CSSParser::ParseSystemColor(parsed_color, color_string, color_scheme))
     return kParsedSystemColor;
   return kParseFailed;
 }
@@ -70,11 +72,20 @@
   return color;
 }
 
+static WebColorScheme ColorScheme(HTMLCanvasElement* canvas) {
+  if (canvas && canvas->isConnected()) {
+    if (auto* style = canvas->GetComputedStyle())
+      return style->UsedColorScheme();
+  }
+  return ComputedStyle::InitialStyle().UsedColorScheme();
+}
+
 bool ParseColorOrCurrentColor(Color& parsed_color,
                               const String& color_string,
                               HTMLCanvasElement* canvas) {
-  ColorParseResult parse_result = ParseColor(
-      parsed_color, color_string.StripWhiteSpace(IsHTMLSpace<UChar>));
+  ColorParseResult parse_result =
+      ParseColor(parsed_color, color_string.StripWhiteSpace(IsHTMLSpace<UChar>),
+                 ColorScheme(canvas));
   switch (parse_result) {
     case kParsedRGBA:
     case kParsedSystemColor:
diff --git a/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_test.cc b/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_test.cc
index 9b4af063..d2562abc 100644
--- a/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_test.cc
+++ b/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_test.cc
@@ -4,8 +4,6 @@
 
 #include "third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module.h"
 
-#include <memory>
-
 #include "build/build_config.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "services/viz/public/mojom/hit_test/hit_test_region_list.mojom-blink.h"
@@ -102,10 +100,10 @@
   // EmbeddedFrameSinkProvider interface impl and its CompositorFrameSinkClient.
   MockEmbeddedFrameSinkProvider mock_embedded_frame_sink_provider;
   mojo::Receiver<mojom::blink::EmbeddedFrameSinkProvider>
-      embedded_frame_sink_provider_binding(&mock_embedded_frame_sink_provider);
+      embedded_frame_sink_provider_receiver(&mock_embedded_frame_sink_provider);
   auto override =
       mock_embedded_frame_sink_provider.CreateScopedOverrideMojoInterface(
-          &embedded_frame_sink_provider_binding);
+          &embedded_frame_sink_provider_receiver);
 
   const bool context_alpha = GetParam();
   CanvasContextCreationAttributesCore attrs;
diff --git a/third_party/blink/renderer/modules/exported/BUILD.gn b/third_party/blink/renderer/modules/exported/BUILD.gn
index 01feb46..77551bb 100644
--- a/third_party/blink/renderer/modules/exported/BUILD.gn
+++ b/third_party/blink/renderer/modules/exported/BUILD.gn
@@ -5,7 +5,6 @@
 
 blink_modules_sources("exported") {
   sources = [
-    "web_apply_constraints_request.cc",
     "web_ax_context.cc",
     "web_ax_object.cc",
     "web_crypto_normalize.cc",
diff --git a/third_party/blink/renderer/modules/exported/web_apply_constraints_request.cc b/third_party/blink/renderer/modules/exported/web_apply_constraints_request.cc
deleted file mode 100644
index d9f2c26..0000000
--- a/third_party/blink/renderer/modules/exported/web_apply_constraints_request.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/public/web/web_apply_constraints_request.h"
-
-#include "third_party/blink/public/platform/web_media_constraints.h"
-#include "third_party/blink/public/platform/web_media_stream_track.h"
-#include "third_party/blink/public/platform/web_string.h"
-#include "third_party/blink/renderer/modules/mediastream/apply_constraints_request.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-
-namespace blink {
-
-WebApplyConstraintsRequest::WebApplyConstraintsRequest(
-    ApplyConstraintsRequest* request)
-    : private_(request) {}
-
-WebApplyConstraintsRequest::~WebApplyConstraintsRequest() {
-  Reset();
-}
-
-WebApplyConstraintsRequest WebApplyConstraintsRequest::CreateForTesting(
-    const WebMediaStreamTrack& track,
-    const WebMediaConstraints& constraints) {
-  return WebApplyConstraintsRequest(
-      ApplyConstraintsRequest::CreateForTesting(track, constraints));
-}
-
-void WebApplyConstraintsRequest::Reset() {
-  private_.Reset();
-}
-
-WebMediaStreamTrack WebApplyConstraintsRequest::Track() const {
-  DCHECK(!IsNull());
-  return private_->Track();
-}
-
-WebMediaConstraints WebApplyConstraintsRequest::Constraints() const {
-  DCHECK(!IsNull());
-  return private_->Constraints();
-}
-
-void WebApplyConstraintsRequest::RequestSucceeded() {
-  DCHECK(!IsNull());
-  private_->RequestSucceeded();
-}
-
-void WebApplyConstraintsRequest::RequestFailed(const WebString& constraint,
-                                               const WebString& message) {
-  DCHECK(!IsNull());
-  private_->RequestFailed(constraint, message);
-}
-
-bool WebApplyConstraintsRequest::operator==(
-    const WebApplyConstraintsRequest& other) const {
-  return private_.Get() == other.private_.Get();
-}
-
-void WebApplyConstraintsRequest::Assign(
-    const WebApplyConstraintsRequest& other) {
-  private_ = other.private_;
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.cc b/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.cc
index 8c01c273..c86663a 100644
--- a/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.cc
@@ -23,14 +23,16 @@
 namespace blink {
 namespace {
 
-void RequestFailed(blink::WebApplyConstraintsRequest request,
+void RequestFailed(blink::ApplyConstraintsRequest* request,
                    const String& constraint,
                    const String& message) {
-  request.RequestFailed(constraint, message);
+  DCHECK(request);
+  request->RequestFailed(constraint, message);
 }
 
-void RequestSucceeded(blink::WebApplyConstraintsRequest request) {
-  request.RequestSucceeded();
+void RequestSucceeded(blink::ApplyConstraintsRequest* request) {
+  DCHECK(request);
+  request->RequestSucceeded();
 }
 
 }  // namespace
@@ -46,24 +48,24 @@
 }
 
 void ApplyConstraintsProcessor::ProcessRequest(
-    const blink::WebApplyConstraintsRequest& request,
+    blink::ApplyConstraintsRequest* request,
     base::OnceClosure callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!request_completed_cb_);
-  DCHECK(current_request_.IsNull());
-  DCHECK(!request.Track().IsNull());
-  if (request.Track().Source().IsNull()) {
+  DCHECK(!current_request_);
+  DCHECK(!request->Track().IsNull());
+  if (request->Track().Source().IsNull()) {
     CannotApplyConstraints(
         "Track has no source. ApplyConstraints not possible.");
     return;
   }
   request_completed_cb_ = std::move(callback);
   current_request_ = request;
-  if (current_request_.Track().Source().GetType() ==
+  if (current_request_->Track().Source().GetType() ==
       blink::WebMediaStreamSource::kTypeVideo) {
     ProcessVideoRequest();
   } else {
-    DCHECK_EQ(current_request_.Track().Source().GetType(),
+    DCHECK_EQ(current_request_->Track().Source().GetType(),
               blink::WebMediaStreamSource::kTypeAudio);
     ProcessAudioRequest();
   }
@@ -71,8 +73,8 @@
 
 void ApplyConstraintsProcessor::ProcessAudioRequest() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(!current_request_.IsNull());
-  DCHECK_EQ(current_request_.Track().Source().GetType(),
+  DCHECK(current_request_);
+  DCHECK_EQ(current_request_->Track().Source().GetType(),
             blink::WebMediaStreamSource::kTypeAudio);
   DCHECK(request_completed_cb_);
   blink::MediaStreamAudioSource* audio_source = GetCurrentAudioSource();
@@ -82,7 +84,7 @@
   }
 
   blink::AudioCaptureSettings settings =
-      SelectSettingsAudioCapture(audio_source, current_request_.Constraints());
+      SelectSettingsAudioCapture(audio_source, current_request_->Constraints());
   if (settings.HasValue()) {
     ApplyConstraintsSucceeded();
   } else {
@@ -92,8 +94,8 @@
 
 void ApplyConstraintsProcessor::ProcessVideoRequest() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(!current_request_.IsNull());
-  DCHECK_EQ(current_request_.Track().Source().GetType(),
+  DCHECK(current_request_);
+  DCHECK_EQ(current_request_->Track().Source().GetType(),
             blink::WebMediaStreamSource::kTypeVideo);
   DCHECK(request_completed_cb_);
   video_source_ = GetCurrentVideoSource();
@@ -232,15 +234,15 @@
 blink::VideoCaptureSettings ApplyConstraintsProcessor::SelectVideoSettings(
     Vector<media::VideoCaptureFormat> formats) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(!current_request_.IsNull());
-  DCHECK_EQ(current_request_.Track().Source().GetType(),
+  DCHECK(current_request_);
+  DCHECK_EQ(current_request_->Track().Source().GetType(),
             blink::WebMediaStreamSource::kTypeVideo);
   DCHECK(request_completed_cb_);
   DCHECK_GT(formats.size(), 0U);
 
   blink::VideoInputDeviceCapabilities device_capabilities;
-  device_capabilities.device_id = current_request_.Track().Source().Id();
-  device_capabilities.group_id = current_request_.Track().Source().GroupId();
+  device_capabilities.device_id = current_request_->Track().Source().Id();
+  device_capabilities.group_id = current_request_->Track().Source().GroupId();
   device_capabilities.facing_mode =
       GetCurrentVideoSource() ? GetCurrentVideoSource()->device().video_facing
                               : media::MEDIA_VIDEO_FACING_NONE;
@@ -263,22 +265,24 @@
   GetCurrentVideoTrack()->GetSettings(settings);
 
   return SelectSettingsVideoDeviceCapture(
-      video_capabilities, current_request_.Constraints(), settings.width,
+      video_capabilities, current_request_->Constraints(), settings.width,
       settings.height, settings.frame_rate);
 }
 
 blink::MediaStreamAudioSource*
 ApplyConstraintsProcessor::GetCurrentAudioSource() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(!current_request_.Track().IsNull());
-  return blink::MediaStreamAudioSource::From(current_request_.Track().Source());
+  DCHECK(current_request_);
+  DCHECK(!current_request_->Track().IsNull());
+  return blink::MediaStreamAudioSource::From(
+      current_request_->Track().Source());
 }
 
 blink::MediaStreamVideoTrack*
 ApplyConstraintsProcessor::GetCurrentVideoTrack() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   blink::MediaStreamVideoTrack* track =
-      blink::MediaStreamVideoTrack::GetVideoTrack(current_request_.Track());
+      blink::MediaStreamVideoTrack::GetVideoTrack(current_request_->Track());
   DCHECK(track);
   return track;
 }
@@ -291,8 +295,8 @@
 
 bool ApplyConstraintsProcessor::AbortIfVideoRequestStateInvalid() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(!current_request_.IsNull());
-  DCHECK_EQ(current_request_.Track().Source().GetType(),
+  DCHECK(current_request_);
+  DCHECK_EQ(current_request_->Track().Source().GetType(),
             blink::WebMediaStreamSource::kTypeVideo);
   DCHECK(request_completed_cb_);
   if (GetCurrentVideoSource() != video_source_) {
@@ -306,20 +310,23 @@
 void ApplyConstraintsProcessor::ApplyConstraintsSucceeded() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   task_runner_->PostTask(
-      FROM_HERE, WTF::Bind(&ApplyConstraintsProcessor::CleanupRequest,
-                           WrapWeakPersistent(this),
-                           WTF::Bind(&RequestSucceeded, current_request_)));
+      FROM_HERE,
+      WTF::Bind(&ApplyConstraintsProcessor::CleanupRequest,
+                WrapWeakPersistent(this),
+                WTF::Bind(&RequestSucceeded,
+                          WrapWeakPersistent(current_request_.Get()))));
 }
 
 void ApplyConstraintsProcessor::ApplyConstraintsFailed(
     const char* failed_constraint_name) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   task_runner_->PostTask(
-      FROM_HERE, WTF::Bind(&ApplyConstraintsProcessor::CleanupRequest,
-                           WrapWeakPersistent(this),
-                           WTF::Bind(&RequestFailed, current_request_,
-                                     String(failed_constraint_name),
-                                     String("Cannot satisfy constraints"))));
+      FROM_HERE,
+      WTF::Bind(
+          &ApplyConstraintsProcessor::CleanupRequest, WrapWeakPersistent(this),
+          WTF::Bind(&RequestFailed, WrapWeakPersistent(current_request_.Get()),
+                    String(failed_constraint_name),
+                    String("Cannot satisfy constraints"))));
 }
 
 void ApplyConstraintsProcessor::CannotApplyConstraints(const String& message) {
@@ -328,17 +335,18 @@
       FROM_HERE,
       WTF::Bind(
           &ApplyConstraintsProcessor::CleanupRequest, WrapWeakPersistent(this),
-          WTF::Bind(&RequestFailed, current_request_, String(), message)));
+          WTF::Bind(&RequestFailed, WrapWeakPersistent(current_request_.Get()),
+                    String(), message)));
 }
 
 void ApplyConstraintsProcessor::CleanupRequest(
     base::OnceClosure web_request_callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(!current_request_.IsNull());
+  DCHECK(current_request_);
   DCHECK(request_completed_cb_);
   std::move(request_completed_cb_).Run();
   std::move(web_request_callback).Run();
-  current_request_.Reset();
+  current_request_ = nullptr;
   video_source_ = nullptr;
 }
 
diff --git a/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.h b/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.h
index ee0b623..185bf2c 100644
--- a/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.h
+++ b/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.h
@@ -13,7 +13,7 @@
 #include "third_party/blink/public/mojom/mediastream/media_devices.mojom-blink.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_constraints_util.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h"
-#include "third_party/blink/public/web/web_apply_constraints_request.h"
+#include "third_party/blink/renderer/modules/mediastream/apply_constraints_request.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -40,10 +40,10 @@
   // it notifies by invoking |callback|.
   // This method must be called only if there is no request currently being
   // processed.
-  void ProcessRequest(const blink::WebApplyConstraintsRequest& request,
+  void ProcessRequest(blink::ApplyConstraintsRequest* request,
                       base::OnceClosure callback);
 
-  void Trace(Visitor*) {}
+  void Trace(Visitor* visitor) { visitor->Trace(current_request_); }
 
  private:
   // Helpers for video device-capture requests.
@@ -82,7 +82,7 @@
   // contains the request currently being processed, if any.
   // |video_source_| and |request_completed_cb_| are the video source and
   // reply callback for the current request.
-  blink::WebApplyConstraintsRequest current_request_;
+  WeakMember<blink::ApplyConstraintsRequest> current_request_;
 
   // TODO(crbug.com/704136): Change to use Member.
   blink::MediaStreamVideoSource* video_source_ = nullptr;
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track.cc
index de3354c..ff6b3c3 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track.cc
@@ -218,17 +218,14 @@
                                    MediaStreamComponent* component)
     : MediaStreamTrack(context,
                        component,
-                       component->Source()->GetReadyState(),
-                       false /* stopped */) {}
+                       component->Source()->GetReadyState()) {}
 
 MediaStreamTrack::MediaStreamTrack(ExecutionContext* context,
                                    MediaStreamComponent* component,
-                                   MediaStreamSource::ReadyState ready_state,
-                                   bool stopped)
-    : ContextLifecycleObserver(context),
-      ready_state_(ready_state),
-      stopped_(stopped),
-      component_(component) {
+                                   MediaStreamSource::ReadyState ready_state)
+    : ready_state_(ready_state),
+      component_(component),
+      execution_context_(context) {
   component_->Source()->AddObserver(this);
 
   // If the source is already non-live at this point, the observer won't have
@@ -380,8 +377,7 @@
 MediaStreamTrack* MediaStreamTrack::clone(ScriptState* script_state) {
   MediaStreamComponent* cloned_component = Component()->Clone();
   MediaStreamTrack* cloned_track = MakeGarbageCollected<MediaStreamTrack>(
-      ExecutionContext::From(script_state), cloned_component, ready_state_,
-      stopped_);
+      ExecutionContext::From(script_state), cloned_component, ready_state_);
   DidCloneMediaStreamTrack(Component(), cloned_component);
   return cloned_track;
 }
@@ -705,7 +701,8 @@
 }
 
 bool MediaStreamTrack::Ended() const {
-  return stopped_ || (ready_state_ == MediaStreamSource::kReadyStateEnded);
+  return (execution_context_ && execution_context_->IsContextDestroyed()) ||
+         (ready_state_ == MediaStreamSource::kReadyStateEnded);
 }
 
 void MediaStreamTrack::SourceChangedState() {
@@ -739,10 +736,6 @@
   is_iterating_registered_media_streams_ = false;
 }
 
-void MediaStreamTrack::ContextDestroyed(ExecutionContext*) {
-  stopped_ = true;
-}
-
 bool MediaStreamTrack::HasPendingActivity() const {
   // If 'ended' listeners exist and the object hasn't yet reached
   // that state, keep the object alive.
@@ -785,15 +778,15 @@
 }
 
 ExecutionContext* MediaStreamTrack::GetExecutionContext() const {
-  return ContextLifecycleObserver::GetExecutionContext();
+  return execution_context_.Get();
 }
 
 void MediaStreamTrack::Trace(blink::Visitor* visitor) {
   visitor->Trace(registered_media_streams_);
   visitor->Trace(component_);
   visitor->Trace(image_capture_);
+  visitor->Trace(execution_context_);
   EventTargetWithInlineData::Trace(visitor);
-  ContextLifecycleObserver::Trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track.h b/third_party/blink/renderer/modules/mediastream/media_stream_track.h
index dbf1979..fe0be2d8 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track.h
@@ -29,7 +29,6 @@
 #include <memory>
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
-#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/modules/event_target_modules.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h"
@@ -50,7 +49,6 @@
 class MODULES_EXPORT MediaStreamTrack
     : public EventTargetWithInlineData,
       public ActiveScriptWrappable<MediaStreamTrack>,
-      public ContextLifecycleObserver,
       public MediaStreamSource::Observer {
   USING_GARBAGE_COLLECTED_MIXIN(MediaStreamTrack);
   DEFINE_WRAPPERTYPEINFO();
@@ -61,8 +59,7 @@
   MediaStreamTrack(ExecutionContext*, MediaStreamComponent*);
   MediaStreamTrack(ExecutionContext*,
                    MediaStreamComponent*,
-                   MediaStreamSource::ReadyState,
-                   bool stopped);
+                   MediaStreamSource::ReadyState);
   ~MediaStreamTrack() override;
 
   String kind() const;
@@ -108,9 +105,6 @@
   // ScriptWrappable
   bool HasPendingActivity() const final;
 
-  // ContextLifecycleObserver
-  void ContextDestroyed(ExecutionContext*) override;
-
   std::unique_ptr<AudioSourceProvider> CreateWebAudioSource(
       int context_sample_rate);
 
@@ -129,9 +123,9 @@
   MediaStreamSource::ReadyState ready_state_;
   HeapHashSet<Member<MediaStream>> registered_media_streams_;
   bool is_iterating_registered_media_streams_ = false;
-  bool stopped_;
   Member<MediaStreamComponent> component_;
   Member<ImageCapture> image_capture_;
+  WeakMember<ExecutionContext> execution_context_;
 };
 
 typedef HeapVector<Member<MediaStreamTrack>> MediaStreamTrackVector;
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_client.cc b/third_party/blink/renderer/modules/mediastream/user_media_client.cc
index 0ee43d1..316c44b9 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_client.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_client.cc
@@ -52,14 +52,13 @@
 UserMediaClient::Request::Request(std::unique_ptr<UserMediaRequestInfo> request)
     : user_media_request_(std::move(request)) {
   DCHECK(user_media_request_);
-  DCHECK(apply_constraints_request_.IsNull());
+  DCHECK(!apply_constraints_request_);
   DCHECK(web_track_to_stop_.IsNull());
 }
 
-UserMediaClient::Request::Request(
-    const blink::WebApplyConstraintsRequest& request)
+UserMediaClient::Request::Request(blink::ApplyConstraintsRequest* request)
     : apply_constraints_request_(request) {
-  DCHECK(!apply_constraints_request_.IsNull());
+  DCHECK(apply_constraints_request_);
   DCHECK(!user_media_request_);
   DCHECK(web_track_to_stop_.IsNull());
 }
@@ -69,28 +68,9 @@
     : web_track_to_stop_(web_track_to_stop) {
   DCHECK(!web_track_to_stop_.IsNull());
   DCHECK(!user_media_request_);
-  DCHECK(apply_constraints_request_.IsNull());
+  DCHECK(!apply_constraints_request_);
 }
 
-UserMediaClient::Request::Request(Request&& other)
-    : user_media_request_(std::move(other.user_media_request_)),
-      apply_constraints_request_(other.apply_constraints_request_),
-      web_track_to_stop_(other.web_track_to_stop_) {
-#if DCHECK_IS_ON()
-  int num_types = 0;
-  if (IsUserMedia())
-    num_types++;
-  if (IsApplyConstraints())
-    num_types++;
-  if (IsStopTrack())
-    num_types++;
-
-  DCHECK_EQ(num_types, 1);
-#endif
-}
-
-UserMediaClient::Request& UserMediaClient::Request::operator=(Request&& other) =
-    default;
 UserMediaClient::Request::~Request() = default;
 
 std::unique_ptr<UserMediaRequestInfo>
@@ -187,22 +167,23 @@
   std::unique_ptr<UserMediaRequestInfo> request_info =
       std::make_unique<UserMediaRequestInfo>(request_id, web_request,
                                              user_gesture);
-  pending_request_infos_.push_back(Request(std::move(request_info)));
+  pending_request_infos_.push_back(
+      MakeGarbageCollected<Request>(std::move(request_info)));
   if (!is_processing_request_)
     MaybeProcessNextRequestInfo();
 }
 
 void UserMediaClient::ApplyConstraints(
-    const blink::WebApplyConstraintsRequest& web_request) {
+    blink::ApplyConstraintsRequest* web_request) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  pending_request_infos_.push_back(Request(web_request));
+  pending_request_infos_.push_back(MakeGarbageCollected<Request>(web_request));
   if (!is_processing_request_)
     MaybeProcessNextRequestInfo();
 }
 
 void UserMediaClient::StopTrack(const blink::WebMediaStreamTrack& web_track) {
-  pending_request_infos_.push_back(Request(web_track));
+  pending_request_infos_.push_back(MakeGarbageCollected<Request>(web_track));
   if (!is_processing_request_)
     MaybeProcessNextRequestInfo();
 }
@@ -216,25 +197,25 @@
   if (is_processing_request_ || pending_request_infos_.empty())
     return;
 
-  Request current_request = std::move(pending_request_infos_.front());
+  auto current_request = std::move(pending_request_infos_.front());
   pending_request_infos_.pop_front();
   is_processing_request_ = true;
 
-  if (current_request.IsUserMedia()) {
+  if (current_request->IsUserMedia()) {
     user_media_processor_->ProcessRequest(
-        current_request.MoveUserMediaRequest(),
+        current_request->MoveUserMediaRequest(),
         WTF::Bind(&UserMediaClient::CurrentRequestCompleted,
                   WrapWeakPersistent(this)));
-  } else if (current_request.IsApplyConstraints()) {
+  } else if (current_request->IsApplyConstraints()) {
     apply_constraints_processor_->ProcessRequest(
-        current_request.apply_constraints_request(),
+        current_request->apply_constraints_request(),
         WTF::Bind(&UserMediaClient::CurrentRequestCompleted,
                   WrapWeakPersistent(this)));
   } else {
-    DCHECK(current_request.IsStopTrack());
+    DCHECK(current_request->IsStopTrack());
     blink::WebPlatformMediaStreamTrack* track =
         blink::WebPlatformMediaStreamTrack::GetTrack(
-            current_request.web_track_to_stop());
+            current_request->web_track_to_stop());
     if (track) {
       track->StopAndNotify(WTF::Bind(&UserMediaClient::CurrentRequestCompleted,
                                      WrapWeakPersistent(this)));
@@ -273,8 +254,8 @@
   } else {
     for (auto it = pending_request_infos_.begin();
          it != pending_request_infos_.end(); ++it) {
-      if (it->IsUserMedia() &&
-          it->user_media_request()->web_request == web_request) {
+      if ((*it)->IsUserMedia() &&
+          (*it)->user_media_request()->web_request == web_request) {
         pending_request_infos_.erase(it);
         did_remove_request = true;
         break;
@@ -310,6 +291,7 @@
   visitor->Trace(frame_);
   visitor->Trace(user_media_processor_);
   visitor->Trace(apply_constraints_processor_);
+  visitor->Trace(pending_request_infos_);
 }
 
 void UserMediaClient::SetMediaDevicesDispatcherForTesting(
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_client.h b/third_party/blink/renderer/modules/mediastream/user_media_client.h
index a77df340..a955fd2 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_client.h
+++ b/third_party/blink/renderer/modules/mediastream/user_media_client.h
@@ -12,9 +12,9 @@
 #include "base/threading/thread_checker.h"
 #include "third_party/blink/public/common/mediastream/media_devices.h"
 #include "third_party/blink/public/mojom/mediastream/media_devices.mojom-blink.h"
-#include "third_party/blink/public/web/web_apply_constraints_request.h"
 #include "third_party/blink/public/web/web_user_media_request.h"
 #include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
+#include "third_party/blink/renderer/modules/mediastream/apply_constraints_request.h"
 #include "third_party/blink/renderer/modules/mediastream/user_media_processor.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/wtf/deque.h"
@@ -47,7 +47,7 @@
 
   void RequestUserMedia(const blink::WebUserMediaRequest& web_request);
   void CancelUserMediaRequest(const blink::WebUserMediaRequest& web_request);
-  void ApplyConstraints(const blink::WebApplyConstraintsRequest& web_request);
+  void ApplyConstraints(blink::ApplyConstraintsRequest* web_request);
   void StopTrack(const blink::WebMediaStreamTrack& web_track);
   void ContextDestroyed();
 
@@ -60,13 +60,11 @@
           media_devices_dispatcher);
 
  private:
-  class Request {
+  class Request : public GarbageCollectedFinalized<Request> {
    public:
     explicit Request(std::unique_ptr<UserMediaRequestInfo> request);
-    explicit Request(const blink::WebApplyConstraintsRequest& request);
+    explicit Request(blink::ApplyConstraintsRequest* request);
     explicit Request(const blink::WebMediaStreamTrack& request);
-    Request(Request&& other);
-    Request& operator=(Request&& other);
     ~Request();
 
     std::unique_ptr<UserMediaRequestInfo> MoveUserMediaRequest();
@@ -74,7 +72,7 @@
     UserMediaRequestInfo* user_media_request() const {
       return user_media_request_.get();
     }
-    const blink::WebApplyConstraintsRequest& apply_constraints_request() const {
+    blink::ApplyConstraintsRequest* apply_constraints_request() const {
       return apply_constraints_request_;
     }
     const blink::WebMediaStreamTrack& web_track_to_stop() const {
@@ -82,15 +80,17 @@
     }
 
     bool IsUserMedia() const { return !!user_media_request_; }
-    bool IsApplyConstraints() const {
-      return !apply_constraints_request_.IsNull();
-    }
+    bool IsApplyConstraints() const { return apply_constraints_request_; }
     bool IsStopTrack() const { return !web_track_to_stop_.IsNull(); }
 
+    void Trace(Visitor* visitor) { visitor->Trace(apply_constraints_request_); }
+
    private:
     std::unique_ptr<UserMediaRequestInfo> user_media_request_;
-    blink::WebApplyConstraintsRequest apply_constraints_request_;
+    Member<blink::ApplyConstraintsRequest> apply_constraints_request_;
     blink::WebMediaStreamTrack web_track_to_stop_;
+
+    DISALLOW_COPY_AND_ASSIGN(Request);
   };
 
   void MaybeProcessNextRequestInfo();
@@ -119,7 +119,7 @@
   // and |pending_request_infos_| is a list of queued requests.
   bool is_processing_request_ = false;
 
-  Deque<Request> pending_request_infos_;
+  HeapDeque<Member<Request>> pending_request_infos_;
 
   THREAD_CHECKER(thread_checker_);
 
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_client_test.cc b/third_party/blink/renderer/modules/mediastream/user_media_client_test.cc
index 3d68b99..f440e113 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_client_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_client_test.cc
@@ -634,8 +634,8 @@
     if (frame_rate)
       factory.basic().frame_rate.SetExact(*frame_rate);
 
-    blink::WebApplyConstraintsRequest apply_constraints_request =
-        blink::WebApplyConstraintsRequest::CreateForTesting(
+    auto* apply_constraints_request =
+        blink::ApplyConstraintsRequest::CreateForTesting(
             web_track, factory.CreateWebMediaConstraints());
     user_media_client_impl_->ApplyConstraints(apply_constraints_request);
     base::RunLoop().RunUntilIdle();
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_controller.h b/third_party/blink/renderer/modules/mediastream/user_media_controller.h
index 2652458..a214acfe 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_controller.h
+++ b/third_party/blink/renderer/modules/mediastream/user_media_controller.h
@@ -81,7 +81,7 @@
 
 inline void UserMediaController::ApplyConstraints(
     ApplyConstraintsRequest* request) {
-  Client()->ApplyConstraints(WebApplyConstraintsRequest(request));
+  Client()->ApplyConstraints(request);
 }
 
 inline void UserMediaController::StopTrack(MediaStreamComponent* track) {
diff --git a/third_party/blink/renderer/modules/presentation/mock_presentation_service.h b/third_party/blink/renderer/modules/presentation/mock_presentation_service.h
index 45ef4c6..cc570dac 100644
--- a/third_party/blink/renderer/modules/presentation/mock_presentation_service.h
+++ b/third_party/blink/renderer/modules/presentation/mock_presentation_service.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PRESENTATION_MOCK_PRESENTATION_SERVICE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PRESENTATION_MOCK_PRESENTATION_SERVICE_H_
 
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom-blink.h"
 
@@ -12,8 +13,10 @@
 
 class MockPresentationService : public mojom::blink::PresentationService {
  public:
-  void SetController(mojom::blink::PresentationControllerPtr) override {}
-  void SetReceiver(mojom::blink::PresentationReceiverPtr) override {}
+  void SetController(
+      mojo::PendingRemote<mojom::blink::PresentationController>) override {}
+  void SetReceiver(
+      mojo::PendingRemote<mojom::blink::PresentationReceiver>) override {}
   MOCK_METHOD1(SetDefaultPresentationUrls, void(const Vector<KURL>&));
   MOCK_METHOD1(ListenForScreenAvailability, void(const KURL&));
   MOCK_METHOD1(StopListeningForScreenAvailability, void(const KURL&));
diff --git a/third_party/blink/renderer/modules/presentation/presentation_connection.cc b/third_party/blink/renderer/modules/presentation/presentation_connection.cc
index 46e222b242..3068367 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_connection.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_connection.cc
@@ -163,7 +163,6 @@
       id_(id),
       url_(url),
       state_(mojom::blink::PresentationConnectionState::CONNECTING),
-      connection_binding_(this),
       binary_type_(kBinaryTypeArrayBuffer),
       file_reading_task_runner_(frame.GetTaskRunner(TaskType::kFileReading)) {}
 
@@ -271,21 +270,22 @@
 }
 
 void ControllerPresentationConnection::Init(
-    mojom::blink::PresentationConnectionPtr connection_ptr,
-    mojom::blink::PresentationConnectionRequest connection_request) {
+    mojo::PendingRemote<mojom::blink::PresentationConnection> connection_remote,
+    mojo::PendingReceiver<mojom::blink::PresentationConnection>
+        connection_receiver) {
   // Note that it is possible for the binding to be already bound here, because
   // the ControllerPresentationConnection object could be reused when
   // reconnecting in the same frame. In this case the existing connections are
   // discarded.
-  if (connection_binding_.is_bound()) {
-    connection_binding_.Close();
+  if (connection_receiver_.is_bound()) {
+    connection_receiver_.reset();
     target_connection_.reset();
   }
 
   DidChangeState(mojom::blink::PresentationConnectionState::CONNECTING);
-  target_connection_ = std::move(connection_ptr);
-  connection_binding_.Bind(
-      std::move(connection_request),
+  target_connection_.Bind(std::move(connection_remote));
+  connection_receiver_.Bind(
+      std::move(connection_receiver),
       GetExecutionContext()->GetTaskRunner(blink::TaskType::kPresentation));
 }
 
@@ -305,8 +305,10 @@
 ReceiverPresentationConnection* ReceiverPresentationConnection::Take(
     PresentationReceiver* receiver,
     const mojom::blink::PresentationInfo& presentation_info,
-    mojom::blink::PresentationConnectionPtr controller_connection,
-    mojom::blink::PresentationConnectionRequest receiver_connection_request) {
+    mojo::PendingRemote<mojom::blink::PresentationConnection>
+        controller_connection,
+    mojo::PendingReceiver<mojom::blink::PresentationConnection>
+        receiver_connection_receiver) {
   DCHECK(receiver);
 
   ReceiverPresentationConnection* connection =
@@ -314,7 +316,7 @@
           *receiver->GetFrame(), receiver, presentation_info.id,
           presentation_info.url);
   connection->Init(std::move(controller_connection),
-                   std::move(receiver_connection_request));
+                   std::move(receiver_connection_receiver));
 
   receiver->RegisterConnection(connection);
 
@@ -331,11 +333,13 @@
 ReceiverPresentationConnection::~ReceiverPresentationConnection() = default;
 
 void ReceiverPresentationConnection::Init(
-    mojom::blink::PresentationConnectionPtr controller_connection_ptr,
-    mojom::blink::PresentationConnectionRequest receiver_connection_request) {
-  target_connection_ = std::move(controller_connection_ptr);
-  connection_binding_.Bind(
-      std::move(receiver_connection_request),
+    mojo::PendingRemote<mojom::blink::PresentationConnection>
+        controller_connection_remote,
+    mojo::PendingReceiver<mojom::blink::PresentationConnection>
+        receiver_connection_receiver) {
+  target_connection_.Bind(std::move(controller_connection_remote));
+  connection_receiver_.Bind(
+      std::move(receiver_connection_receiver),
       GetExecutionContext()->GetTaskRunner(blink::TaskType::kPresentation));
 
   target_connection_->DidChangeState(
@@ -410,7 +414,7 @@
 void PresentationConnection::ContextDestroyed(ExecutionContext*) {
   DoClose(mojom::blink::PresentationConnectionCloseReason::WENT_AWAY);
   target_connection_.reset();
-  connection_binding_.Close();
+  connection_receiver_.reset();
 }
 
 void PresentationConnection::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/modules/presentation/presentation_connection.h b/third_party/blink/renderer/modules/presentation/presentation_connection.h
index f425188..ec1c0ec 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_connection.h
+++ b/third_party/blink/renderer/modules/presentation/presentation_connection.h
@@ -6,7 +6,10 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PRESENTATION_PRESENTATION_CONNECTION_H_
 
 #include <memory>
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
@@ -100,13 +103,14 @@
   KURL url_;
   mojom::blink::PresentationConnectionState state_;
 
-  mojo::Binding<mojom::blink::PresentationConnection> connection_binding_;
+  mojo::Receiver<mojom::blink::PresentationConnection> connection_receiver_{
+      this};
 
   // The other end of a PresentationConnection. For controller connections, this
   // can point to the browser (2-UA) or another renderer (1-UA). For receiver
-  // connections, this currently only points to another renderer. This ptr can
-  // be used to send messages directly to the other end.
-  mojom::blink::PresentationConnectionPtr target_connection_;
+  // connections, this currently only points to another renderer. This remote
+  // can be used to send messages directly to the other end.
+  mojo::Remote<mojom::blink::PresentationConnection> target_connection_;
 
  private:
   class BlobLoader;
@@ -180,8 +184,10 @@
   void Trace(blink::Visitor*) override;
 
   // Initializes Mojo message pipes and registers with the PresentationService.
-  void Init(mojom::blink::PresentationConnectionPtr connection_ptr,
-            mojom::blink::PresentationConnectionRequest connection_request);
+  void Init(mojo::PendingRemote<mojom::blink::PresentationConnection>
+                connection_remote,
+            mojo::PendingReceiver<mojom::blink::PresentationConnection>
+                connection_receiver);
 
  private:
   // PresentationConnection implementation.
@@ -200,8 +206,10 @@
   static ReceiverPresentationConnection* Take(
       PresentationReceiver*,
       const mojom::blink::PresentationInfo&,
-      mojom::blink::PresentationConnectionPtr controller_connection,
-      mojom::blink::PresentationConnectionRequest receiver_connection_request);
+      mojo::PendingRemote<mojom::blink::PresentationConnection>
+          controller_connection,
+      mojo::PendingReceiver<mojom::blink::PresentationConnection>
+          receiver_connection_receiver);
 
   ReceiverPresentationConnection(LocalFrame&,
                                  PresentationReceiver*,
@@ -211,9 +219,10 @@
 
   void Trace(blink::Visitor*) override;
 
-  void Init(
-      mojom::blink::PresentationConnectionPtr controller_connection_ptr,
-      mojom::blink::PresentationConnectionRequest receiver_connection_request);
+  void Init(mojo::PendingRemote<mojom::blink::PresentationConnection>
+                controller_connection_remote,
+            mojo::PendingReceiver<mojom::blink::PresentationConnection>
+                receiver_connection_receiver);
 
   // PresentationConnection override
   void DidChangeState(mojom::blink::PresentationConnectionState) override;
diff --git a/third_party/blink/renderer/modules/presentation/presentation_connection_callbacks.cc b/third_party/blink/renderer/modules/presentation/presentation_connection_callbacks.cc
index 506a1ece..aed6db7 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_connection_callbacks.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_connection_callbacks.cc
@@ -37,12 +37,10 @@
   }
 
   if (result) {
-    DCHECK(result->connection_ptr);
-    DCHECK(result->connection_request);
-    OnSuccess(*result->presentation_info,
-              mojom::blink::PresentationConnectionPtr(
-                  std::move(result->connection_ptr)),
-              std::move(result->connection_request));
+    DCHECK(result->connection_remote);
+    DCHECK(result->connection_receiver);
+    OnSuccess(*result->presentation_info, std::move(result->connection_remote),
+              std::move(result->connection_receiver));
   } else {
     OnError(*error);
   }
@@ -50,8 +48,9 @@
 
 void PresentationConnectionCallbacks::OnSuccess(
     const mojom::blink::PresentationInfo& presentation_info,
-    mojom::blink::PresentationConnectionPtr connection_ptr,
-    mojom::blink::PresentationConnectionRequest connection_request) {
+    mojo::PendingRemote<mojom::blink::PresentationConnection> connection_remote,
+    mojo::PendingReceiver<mojom::blink::PresentationConnection>
+        connection_receiver) {
   // Reconnect to existing connection.
   if (connection_ && connection_->GetState() ==
                          mojom::blink::PresentationConnectionState::CLOSED) {
@@ -66,7 +65,8 @@
   }
 
   resolver_->Resolve(connection_);
-  connection_->Init(std::move(connection_ptr), std::move(connection_request));
+  connection_->Init(std::move(connection_remote),
+                    std::move(connection_receiver));
 }
 
 void PresentationConnectionCallbacks::OnError(
diff --git a/third_party/blink/renderer/modules/presentation/presentation_connection_callbacks.h b/third_party/blink/renderer/modules/presentation/presentation_connection_callbacks.h
index 86b9868..ef6bb42e 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_connection_callbacks.h
+++ b/third_party/blink/renderer/modules/presentation/presentation_connection_callbacks.h
@@ -6,6 +6,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PRESENTATION_PRESENTATION_CONNECTION_CALLBACKS_H_
 
 #include "base/macros.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom-blink.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -35,10 +37,11 @@
                                   mojom::blink::PresentationErrorPtr);
 
  private:
-  void OnSuccess(
-      const mojom::blink::PresentationInfo&,
-      mojom::blink::PresentationConnectionPtr connection_ptr,
-      mojom::blink::PresentationConnectionRequest connection_request);
+  void OnSuccess(const mojom::blink::PresentationInfo&,
+                 mojo::PendingRemote<mojom::blink::PresentationConnection>
+                     connection_remote,
+                 mojo::PendingReceiver<mojom::blink::PresentationConnection>
+                     connection_receiver);
   void OnError(const mojom::blink::PresentationError&);
 
   Persistent<ScriptPromiseResolver> resolver_;
diff --git a/third_party/blink/renderer/modules/presentation/presentation_controller.cc b/third_party/blink/renderer/modules/presentation/presentation_controller.cc
index e281522..880e3228 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_controller.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_controller.cc
@@ -20,8 +20,7 @@
 
 PresentationController::PresentationController(LocalFrame& frame)
     : Supplement<LocalFrame>(frame),
-      ContextLifecycleObserver(frame.GetDocument()),
-      controller_binding_(this) {}
+      ContextLifecycleObserver(frame.GetDocument()) {}
 
 PresentationController::~PresentationController() = default;
 
@@ -119,7 +118,7 @@
     mojom::blink::PresentationConnectionResultPtr result) {
   DCHECK(result);
   DCHECK(result->presentation_info);
-  DCHECK(result->connection_ptr && result->connection_request);
+  DCHECK(result->connection_remote && result->connection_receiver);
   if (!presentation_ || !presentation_->defaultRequest())
     return;
 
@@ -128,13 +127,12 @@
   // TODO(btolsch): Convert this and similar calls to just use InterfacePtrInfo
   // instead of constructing an InterfacePtr every time we have
   // InterfacePtrInfo.
-  connection->Init(mojom::blink::PresentationConnectionPtr(
-                       std::move(result->connection_ptr)),
-                   std::move(result->connection_request));
+  connection->Init(std::move(result->connection_remote),
+                   std::move(result->connection_receiver));
 }
 
 void PresentationController::ContextDestroyed(ExecutionContext*) {
-  controller_binding_.Close();
+  presentation_controller_receiver_.reset();
 }
 
 ControllerPresentationConnection*
@@ -153,22 +151,21 @@
   return nullptr;
 }
 
-mojom::blink::PresentationServicePtr&
+mojo::Remote<mojom::blink::PresentationService>&
 PresentationController::GetPresentationService() {
-  if (!presentation_service_ && GetFrame() && GetFrame()->Client()) {
+  if (!presentation_service_remote_ && GetFrame() && GetFrame()->Client()) {
     auto* interface_provider = GetFrame()->Client()->GetInterfaceProvider();
 
     scoped_refptr<base::SingleThreadTaskRunner> task_runner =
         GetFrame()->GetTaskRunner(TaskType::kPresentation);
     interface_provider->GetInterface(
-        mojo::MakeRequest(&presentation_service_, task_runner));
+        presentation_service_remote_.BindNewPipeAndPassReceiver(task_runner));
 
-    mojom::blink::PresentationControllerPtr controller_ptr;
-    controller_binding_.Bind(mojo::MakeRequest(&controller_ptr, task_runner),
-                             task_runner);
-    presentation_service_->SetController(std::move(controller_ptr));
+    presentation_service_remote_->SetController(
+        presentation_controller_receiver_.BindNewPipeAndPassRemote(
+            task_runner));
   }
-  return presentation_service_;
+  return presentation_service_remote_;
 }
 
 ControllerPresentationConnection* PresentationController::FindConnection(
diff --git a/third_party/blink/renderer/modules/presentation/presentation_controller.h b/third_party/blink/renderer/modules/presentation/presentation_controller.h
index 98b4847..635f6da 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_controller.h
+++ b/third_party/blink/renderer/modules/presentation/presentation_controller.h
@@ -6,7 +6,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PRESENTATION_PRESENTATION_CONTROLLER_H_
 
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom-blink.h"
 #include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
@@ -65,10 +66,10 @@
       const blink::WebVector<blink::WebURL>& presentation_urls,
       const blink::WebString& presentation_id);
 
-  // Returns a reference to the PresentationService ptr, requesting the remote
-  // service if needed. May return an invalid ptr if the associated Document is
-  // detached.
-  mojom::blink::PresentationServicePtr& GetPresentationService();
+  // Returns a reference to the PresentationService remote, requesting the
+  // remote service if needed. May return an invalid remote if the associated
+  // Document is detached.
+  mojo::Remote<mojom::blink::PresentationService>& GetPresentationService();
 
   // Returns the PresentationAvailabilityState owned by |this|, creating it if
   // needed. Always non-null.
@@ -108,12 +109,13 @@
   // The presentation connections associated with that frame.
   HeapHashSet<WeakMember<ControllerPresentationConnection>> connections_;
 
-  // Lazily-initialized pointer to PresentationService.
-  mojom::blink::PresentationServicePtr presentation_service_;
+  // Holder of the Mojo connection to the PresentationService remote.
+  mojo::Remote<mojom::blink::PresentationService> presentation_service_remote_;
 
   // Lazily-initialized binding for mojom::blink::PresentationController. Sent
   // to |presentation_service_|'s implementation.
-  mojo::Binding<mojom::blink::PresentationController> controller_binding_;
+  mojo::Receiver<mojom::blink::PresentationController>
+      presentation_controller_receiver_{this};
 
   DISALLOW_COPY_AND_ASSIGN(PresentationController);
 };
diff --git a/third_party/blink/renderer/modules/presentation/presentation_receiver.cc b/third_party/blink/renderer/modules/presentation/presentation_receiver.cc
index 66c8961..21d9751 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_receiver.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_receiver.cc
@@ -26,17 +26,19 @@
 PresentationReceiver::PresentationReceiver(LocalFrame* frame)
     : ContextLifecycleObserver(frame->GetDocument()),
       connection_list_(MakeGarbageCollected<PresentationConnectionList>(
-          frame->GetDocument())),
-      receiver_binding_(this) {
+          frame->GetDocument())) {
   auto* interface_provider = GetFrame()->Client()->GetInterfaceProvider();
-  interface_provider->GetInterface(mojo::MakeRequest(&presentation_service_));
+  interface_provider->GetInterface(
+      presentation_service_remote_.BindNewPipeAndPassReceiver());
 
-  mojom::blink::PresentationReceiverPtr receiver_ptr;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
       frame->GetTaskRunner(TaskType::kPresentation);
-  receiver_binding_.Bind(mojo::MakeRequest(&receiver_ptr, task_runner),
-                         task_runner);
-  presentation_service_->SetReceiver(std::move(receiver_ptr));
+
+  // Set the mojo::Remote<T> that remote implementation of PresentationService
+  // will use to interact with the associated PresentationReceiver, in order to
+  // receive updates on new connections becoming available.
+  presentation_service_remote_->SetReceiver(
+      presentation_receiver_receiver_.BindNewPipeAndPassRemote(task_runner));
 }
 
 // static
@@ -85,13 +87,15 @@
 
 void PresentationReceiver::OnReceiverConnectionAvailable(
     mojom::blink::PresentationInfoPtr info,
-    mojom::blink::PresentationConnectionPtr controller_connection,
-    mojom::blink::PresentationConnectionRequest receiver_connection_request) {
+    mojo::PendingRemote<mojom::blink::PresentationConnection>
+        controller_connection,
+    mojo::PendingReceiver<mojom::blink::PresentationConnection>
+        receiver_connection_receiver) {
   // Take() will call PresentationReceiver::registerConnection()
   // and register the connection.
   auto* connection = ReceiverPresentationConnection::Take(
       this, *info, std::move(controller_connection),
-      std::move(receiver_connection_request));
+      std::move(receiver_connection_receiver));
 
   // Only notify receiver.connectionList property if it has been acccessed
   // previously.
@@ -126,8 +130,8 @@
 }
 
 void PresentationReceiver::ContextDestroyed(ExecutionContext*) {
-  receiver_binding_.Close();
-  presentation_service_.reset();
+  presentation_receiver_receiver_.reset();
+  presentation_service_remote_.reset();
 }
 
 void PresentationReceiver::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/modules/presentation/presentation_receiver.h b/third_party/blink/renderer/modules/presentation/presentation_receiver.h
index 5a36ba7..714eba95 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_receiver.h
+++ b/third_party/blink/renderer/modules/presentation/presentation_receiver.h
@@ -5,7 +5,10 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PRESENTATION_PRESENTATION_RECEIVER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PRESENTATION_PRESENTATION_RECEIVER_H_
 
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_property.h"
@@ -49,8 +52,8 @@
   // mojom::blink::PresentationReceiver
   void OnReceiverConnectionAvailable(
       mojom::blink::PresentationInfoPtr,
-      mojom::blink::PresentationConnectionPtr,
-      mojom::blink::PresentationConnectionRequest) override;
+      mojo::PendingRemote<mojom::blink::PresentationConnection>,
+      mojo::PendingReceiver<mojom::blink::PresentationConnection>) override;
 
   void RegisterConnection(ReceiverPresentationConnection*);
   void RemoveConnection(ReceiverPresentationConnection*);
@@ -69,8 +72,9 @@
   Member<ConnectionListProperty> connection_list_property_;
   Member<PresentationConnectionList> connection_list_;
 
-  mojo::Binding<mojom::blink::PresentationReceiver> receiver_binding_;
-  mojom::blink::PresentationServicePtr presentation_service_;
+  mojo::Receiver<mojom::blink::PresentationReceiver>
+      presentation_receiver_receiver_{this};
+  mojo::Remote<mojom::blink::PresentationService> presentation_service_remote_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/presentation/presentation_receiver_test.cc b/third_party/blink/renderer/modules/presentation/presentation_receiver_test.cc
index 61b676c..cc6e8a48 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_receiver_test.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_receiver_test.cc
@@ -6,6 +6,8 @@
 
 #include <memory>
 
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
@@ -38,15 +40,21 @@
 
  protected:
   void SetUp() override {
-    controller_connection_request_ = mojo::MakeRequest(&controller_connection_);
-    receiver_connection_request_ = mojo::MakeRequest(&receiver_connection_);
+    controller_connection_receiver_ =
+        controller_connection_.InitWithNewPipeAndPassReceiver();
+    receiver_connection_receiver_ =
+        receiver_connection_.InitWithNewPipeAndPassReceiver();
   }
 
   mojom::blink::PresentationInfo connection_info_;
-  mojom::blink::PresentationConnectionRequest controller_connection_request_;
-  mojom::blink::PresentationConnectionPtr controller_connection_;
-  mojom::blink::PresentationConnectionRequest receiver_connection_request_;
-  mojom::blink::PresentationConnectionPtr receiver_connection_;
+  mojo::PendingReceiver<mojom::blink::PresentationConnection>
+      controller_connection_receiver_;
+  mojo::PendingRemote<mojom::blink::PresentationConnection>
+      controller_connection_;
+  mojo::PendingReceiver<mojom::blink::PresentationConnection>
+      receiver_connection_receiver_;
+  mojo::PendingRemote<mojom::blink::PresentationConnection>
+      receiver_connection_;
 };
 
 void PresentationReceiverTest::AddConnectionavailableEventListener(
@@ -102,7 +110,7 @@
   // Receive first connection.
   receiver->OnReceiverConnectionAvailable(
       connection_info_.Clone(), std::move(controller_connection_),
-      std::move(receiver_connection_request_));
+      std::move(receiver_connection_receiver_));
 
   VerifyConnectionListPropertyState(ScriptPromisePropertyBase::kResolved,
                                     receiver);
@@ -125,19 +133,23 @@
   // Receive first connection.
   receiver->OnReceiverConnectionAvailable(
       connection_info_.Clone(), std::move(controller_connection_),
-      std::move(receiver_connection_request_));
+      std::move(receiver_connection_receiver_));
 
-  mojom::blink::PresentationConnectionPtr controller_connection_2;
-  mojom::blink::PresentationConnectionPtr receiver_connection_2;
-  mojom::blink::PresentationConnectionRequest controller_connection_request_2 =
-      mojo::MakeRequest(&controller_connection_2);
-  mojom::blink::PresentationConnectionRequest receiver_connection_request_2 =
-      mojo::MakeRequest(&receiver_connection_2);
+  mojo::PendingRemote<mojom::blink::PresentationConnection>
+      controller_connection_2_;
+  mojo::PendingRemote<mojom::blink::PresentationConnection>
+      receiver_connection_2_;
+  mojo::PendingReceiver<mojom::blink::PresentationConnection>
+      controller_connection_receiver_2 =
+          controller_connection_2_.InitWithNewPipeAndPassReceiver();
+  mojo::PendingReceiver<mojom::blink::PresentationConnection>
+      receiver_connection_receiver_2 =
+          receiver_connection_2_.InitWithNewPipeAndPassReceiver();
 
   // Receive second connection.
   receiver->OnReceiverConnectionAvailable(
-      connection_info_.Clone(), std::move(controller_connection_2),
-      std::move(receiver_connection_request_2));
+      connection_info_.Clone(), std::move(controller_connection_2_),
+      std::move(receiver_connection_receiver_2));
 
   VerifyConnectionListSize(2, receiver);
 }
@@ -156,19 +168,23 @@
   // Receive first connection.
   receiver->OnReceiverConnectionAvailable(
       connection_info_.Clone(), std::move(controller_connection_),
-      std::move(receiver_connection_request_));
+      std::move(receiver_connection_receiver_));
 
-  mojom::blink::PresentationConnectionPtr controller_connection_2;
-  mojom::blink::PresentationConnectionPtr receiver_connection_2;
-  mojom::blink::PresentationConnectionRequest controller_connection_request_2 =
-      mojo::MakeRequest(&controller_connection_2);
-  mojom::blink::PresentationConnectionRequest receiver_connection_request_2 =
-      mojo::MakeRequest(&receiver_connection_2);
+  mojo::PendingRemote<mojom::blink::PresentationConnection>
+      controller_connection_2_;
+  mojo::PendingRemote<mojom::blink::PresentationConnection>
+      receiver_connection_2_;
+  mojo::PendingReceiver<mojom::blink::PresentationConnection>
+      controller_connection_receiver_2 =
+          controller_connection_2_.InitWithNewPipeAndPassReceiver();
+  mojo::PendingReceiver<mojom::blink::PresentationConnection>
+      receiver_connection_receiver_2 =
+          receiver_connection_2_.InitWithNewPipeAndPassReceiver();
 
   // Receive second connection.
   receiver->OnReceiverConnectionAvailable(
-      connection_info_.Clone(), std::move(controller_connection_2),
-      std::move(receiver_connection_request_2));
+      connection_info_.Clone(), std::move(controller_connection_2_),
+      std::move(receiver_connection_receiver_2));
 
   receiver->connectionList(scope.GetScriptState());
   VerifyConnectionListPropertyState(ScriptPromisePropertyBase::kResolved,
diff --git a/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc b/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
index 9e411fa..10cff33 100644
--- a/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
+++ b/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
@@ -513,9 +513,10 @@
   if (!presentation_controller)
     return;
 
-  // Note: Messages on |connection_request| are ignored.
-  target_presentation_connection_.Bind(std::move(result->connection_ptr));
-  presentation_connection_receiver_.Bind(std::move(result->connection_request));
+  // Note: Messages on |connection_receiver| are ignored.
+  target_presentation_connection_.Bind(std::move(result->connection_remote));
+  presentation_connection_receiver_.Bind(
+      std::move(result->connection_receiver));
 }
 
 void RemotePlayback::OnConnectionError(
diff --git a/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc b/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc
index 2603048..0d7ea36 100644
--- a/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/platform/graphics/begin_frame_provider.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/single_thread_task_runner.h"
 #include "services/viz/public/mojom/compositing/frame_timing_details.mojom-blink.h"
@@ -19,16 +21,14 @@
     BeginFrameProviderClient* client)
     : needs_begin_frame_(false),
       requested_needs_begin_frame_(false),
-      cfs_binding_(this),
-      efs_binding_(this),
       frame_sink_id_(begin_frame_provider_params.frame_sink_id),
       parent_frame_sink_id_(begin_frame_provider_params.parent_frame_sink_id),
       begin_frame_client_(client) {}
 
 void BeginFrameProvider::ResetCompositorFrameSink() {
   compositor_frame_sink_.reset();
-  efs_binding_.Close();
-  cfs_binding_.Close();
+  efs_receiver_.reset();
+  cfs_receiver_.reset();
   if (needs_begin_frame_) {
     needs_begin_frame_ = false;
     RequestBeginFrame();
@@ -59,26 +59,21 @@
   if (compositor_frame_sink_.is_bound())
     return;
 
-  mojom::blink::EmbeddedFrameSinkProviderPtr provider;
+  mojo::Remote<mojom::blink::EmbeddedFrameSinkProvider> provider;
   Platform::Current()->GetInterfaceProvider()->GetInterface(
-      mojo::MakeRequest(&provider));
+      provider.BindNewPipeAndPassReceiver());
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
       ThreadScheduler::Current()->CompositorTaskRunner();
 
-  mojom::blink::EmbeddedFrameSinkClientPtr efs_client;
-  efs_binding_.Bind(mojo::MakeRequest(&efs_client), task_runner);
-
-  viz::mojom::blink::CompositorFrameSinkClientPtr client;
-  cfs_binding_.Bind(mojo::MakeRequest(&client), task_runner);
-
   provider->CreateSimpleCompositorFrameSink(
-      parent_frame_sink_id_, frame_sink_id_, std::move(efs_client),
-      std::move(client), mojo::MakeRequest(&compositor_frame_sink_));
+      parent_frame_sink_id_, frame_sink_id_,
+      efs_receiver_.BindNewPipeAndPassRemote(task_runner),
+      cfs_receiver_.BindNewPipeAndPassRemote(task_runner),
+      compositor_frame_sink_.BindNewPipeAndPassReceiver());
 
-  compositor_frame_sink_.set_connection_error_with_reason_handler(
-      base::BindOnce(&BeginFrameProvider::OnMojoConnectionError,
-                     weak_factory_.GetWeakPtr()));
+  compositor_frame_sink_.set_disconnect_with_reason_handler(base::BindOnce(
+      &BeginFrameProvider::OnMojoConnectionError, weak_factory_.GetWeakPtr()));
 }
 
 void BeginFrameProvider::RequestBeginFrame() {
diff --git a/third_party/blink/renderer/platform/graphics/begin_frame_provider.h b/third_party/blink/renderer/platform/graphics/begin_frame_provider.h
index edfd386..af52da0 100644
--- a/third_party/blink/renderer/platform/graphics/begin_frame_provider.h
+++ b/third_party/blink/renderer/platform/graphics/begin_frame_provider.h
@@ -5,7 +5,11 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_BEGIN_FRAME_PROVIDER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_BEGIN_FRAME_PROVIDER_H_
 
-#include "mojo/public/cpp/bindings/binding.h"
+#include <string>
+
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom-blink.h"
 #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink.h"
 #include "third_party/blink/renderer/platform/graphics/begin_frame_provider.h"
@@ -52,7 +56,7 @@
 
   // viz::mojom::blink::EmbeddedFrameSinkClient implementation.
   void BindSurfaceEmbedder(
-      mojom::blink::SurfaceEmbedderRequest request) override {
+      mojo::PendingReceiver<mojom::blink::SurfaceEmbedder> receiver) override {
     NOTIMPLEMENTED();
   }
 
@@ -69,11 +73,12 @@
   bool needs_begin_frame_;
   bool requested_needs_begin_frame_;
 
-  mojo::Binding<viz::mojom::blink::CompositorFrameSinkClient> cfs_binding_;
-  mojo::Binding<mojom::blink::EmbeddedFrameSinkClient> efs_binding_;
+  mojo::Receiver<viz::mojom::blink::CompositorFrameSinkClient> cfs_receiver_{
+      this};
+  mojo::Receiver<mojom::blink::EmbeddedFrameSinkClient> efs_receiver_{this};
   viz::FrameSinkId frame_sink_id_;
   viz::FrameSinkId parent_frame_sink_id_;
-  viz::mojom::blink::CompositorFrameSinkPtr compositor_frame_sink_;
+  mojo::Remote<viz::mojom::blink::CompositorFrameSink> compositor_frame_sink_;
   BeginFrameProviderClient* begin_frame_client_;
 
   base::WeakPtrFactory<BeginFrameProvider> weak_factory_{this};
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image.cc b/third_party/blink/renderer/platform/graphics/bitmap_image.cc
index dd58b1d..b52d106c 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image.cc
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image.cc
@@ -223,7 +223,8 @@
       decoder_->FilenameExtension() == "jpg") {
     BitmapImageMetrics::CountImageJpegDensity(
         std::min(Size().Width(), Size().Height()),
-        ImageDensityInCentiBpp(Size(), decoder_->ByteSize()));
+        ImageDensityInCentiBpp(Size(), decoder_->ByteSize()),
+        decoder_->ByteSize());
   }
 
   // Feed all the data we've seen so far to the image decoder.
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
index 830737fa..8170d9a4 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
@@ -45,9 +45,17 @@
 }
 
 void BitmapImageMetrics::CountImageJpegDensity(int image_min_side,
-                                               uint64_t density_centi_bpp) {
-  // Values are reported in the range 0.01 to 10 bpp, in different metrics
-  // depending on the image category (small, medium, large).
+                                               uint64_t density_centi_bpp,
+                                               size_t image_size_bytes) {
+  // All bpp samples are reported in the range 0.01 to 10 bpp as integer number
+  // of 0.01 bpp. We don't report for any sample for small images (0 to 99px on
+  // the smallest dimension).
+  //
+  // The histograms JpegDensity.1000px, JpegDensity.400px and JpegDensity.100px
+  // report the number of images decoded for a given bpp value.
+  //
+  // The histogram JpegDensity.KiBWeighted reports the number of KiB decoded for
+  // a given bpp value.
   if (image_min_side >= 1000) {
     DEFINE_THREAD_SAFE_STATIC_LOCAL(
         CustomCountHistogram, density_histogram,
@@ -69,6 +77,18 @@
   } else {
     // We don't report for images with 0 to 99px on the smallest dimension.
   }
+
+  if (image_min_side >= 100) {
+    DEFINE_THREAD_SAFE_STATIC_LOCAL(
+        CustomCountHistogram, density_histogram,
+        ("Blink.DecodedImage.JpegDensity.KiBWeighted", 1, 1000, 100));
+    int image_size_kib = (image_size_bytes + 512) / 1024;
+    if (image_size_kib > 0) {
+      density_histogram.CountMany(
+          base::saturated_cast<base::Histogram::Sample>(density_centi_bpp),
+          image_size_kib);
+    }
+  }
 }
 
 void BitmapImageMetrics::CountJpegArea(const IntSize& size) {
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h
index e97697d..2d4fb112 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h
@@ -73,9 +73,11 @@
   static void CountDecodedImageType(const String& type);
   static void CountImageOrientation(const ImageOrientationEnum);
   // Report the JPEG compression density in 0.01 bits per pixel for an image
-  // with a smallest side (width or length) of |image_min_side|.
+  // with a smallest side (width or length) of |image_min_side| and total size
+  // in bytes |image_size_bytes|.
   static void CountImageJpegDensity(int image_min_side,
-                                    uint64_t density_centi_bpp);
+                                    uint64_t density_centi_bpp,
+                                    size_t image_size_bytes);
   static void CountJpegArea(const IntSize& size);
   static void CountJpegColorSpace(JpegColorSpace color_space);
 };
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc b/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
index 29043f7c..baece0b 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
@@ -788,8 +788,17 @@
 
 template <typename HistogramEnumType>
 struct HistogramTestParams {
+  HistogramTestParams(const char* filename, HistogramEnumType type, int count)
+      : filename(filename), type(type), count(count) {}
+  HistogramTestParams(const char* filename, HistogramEnumType type)
+      : HistogramTestParams(filename, type, 1) {}
+
   const char* filename;
   HistogramEnumType type;
+
+  // The number of events reported in the histogram when |type| is not
+  // kNoSamplesReported, otherwise is ignored.
+  int count;
 };
 
 template <typename HistogramEnumType>
@@ -810,7 +819,7 @@
       histogram_tester.ExpectTotalCount(histogram_name, 0);
     } else {
       histogram_tester.ExpectUniqueSample(histogram_name, this->GetParam().type,
-                                          1);
+                                          this->GetParam().count);
     }
   }
 };
@@ -905,4 +914,25 @@
     DecodedImageDensityHistogramTest400px,
     testing::ValuesIn(kDecodedImageDensityHistogramTest400pxParams));
 
+using DecodedImageDensityHistogramTestKiBWeighted = BitmapHistogramTest<int>;
+
+TEST_P(DecodedImageDensityHistogramTestKiBWeighted, JpegDensity) {
+  RunTest("Blink.DecodedImage.JpegDensity.KiBWeighted");
+}
+
+const DecodedImageDensityHistogramTestKiBWeighted::ParamType
+    kDecodedImageDensityHistogramTestKiBWeightedParams[] = {
+        // 64x64 too small to report any metric
+        {"rgb-jpeg-red.jpg",
+         DecodedImageDensityHistogramTest100px::kNoSamplesReported},
+        // 439x154, 23220 bytes --> 2.74 bpp, 23 KiB (rounded up)
+        {"cropped_mandrill.jpg", 274, 23},
+        // 320x320, 74017 bytes --> 5.78, 72 KiB (rounded down)
+        {"blue-wheel-srgb-color-profile.jpg", 578, 72}};
+
+INSTANTIATE_TEST_SUITE_P(
+    DecodedImageDensityHistogramTestKiBWeighted,
+    DecodedImageDensityHistogramTestKiBWeighted,
+    testing::ValuesIn(kDecodedImageDensityHistogramTestKiBWeightedParams));
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
index 4ed51ee..d42333b 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
@@ -4,7 +4,8 @@
 
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h"
 
-#include <memory>
+#include <utility>
+
 #include "base/single_thread_task_runner.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
@@ -57,7 +58,6 @@
       size_(size),
       change_size_for_next_commit_(false),
       needs_begin_frame_(false),
-      binding_(this),
       placeholder_canvas_id_(canvas_id),
       num_unreclaimed_frames_posted_(0),
       client_(client) {
@@ -67,16 +67,16 @@
     return;
 
   DCHECK(!sink_.is_bound());
-  mojom::blink::EmbeddedFrameSinkProviderPtr provider;
+  mojo::Remote<mojom::blink::EmbeddedFrameSinkProvider> provider;
   Platform::Current()->GetInterfaceProvider()->GetInterface(
-      mojo::MakeRequest(&provider));
+      provider.BindNewPipeAndPassReceiver());
 
   DCHECK(provider);
-  binding_.Bind(mojo::MakeRequest(&client_ptr_));
-  provider->CreateCompositorFrameSink(frame_sink_id_, std::move(client_ptr_),
-                                      mojo::MakeRequest(&sink_));
+  provider->CreateCompositorFrameSink(frame_sink_id_,
+                                      receiver_.BindNewPipeAndPassRemote(),
+                                      sink_.BindNewPipeAndPassReceiver());
   provider->ConnectToEmbedder(frame_sink_id_,
-                              mojo::MakeRequest(&surface_embedder_));
+                              surface_embedder_.BindNewPipeAndPassReceiver());
 }
 
 CanvasResourceDispatcher::~CanvasResourceDispatcher() = default;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h
index d4b1639..fef3d71 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h
@@ -6,11 +6,13 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_DISPATCHER_H_
 
 #include <memory>
+
 #include "base/memory/read_only_shared_memory_region.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/resources/resource_id.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom-blink.h"
 #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink.h"
 
@@ -109,10 +111,9 @@
   void ReclaimResourceInternal(viz::ResourceId resource_id);
   void ReclaimResourceInternal(const ResourceMap::iterator&);
 
-  viz::mojom::blink::CompositorFrameSinkPtr sink_;
-  mojom::blink::SurfaceEmbedderPtr surface_embedder_;
-  mojo::Binding<viz::mojom::blink::CompositorFrameSinkClient> binding_;
-  viz::mojom::blink::CompositorFrameSinkClientPtr client_ptr_;
+  mojo::Remote<viz::mojom::blink::CompositorFrameSink> sink_;
+  mojo::Remote<mojom::blink::SurfaceEmbedder> surface_embedder_;
+  mojo::Receiver<viz::mojom::blink::CompositorFrameSinkClient> receiver_{this};
 
   int placeholder_canvas_id_;
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc
index 0003a58..ce82e6a 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc
@@ -208,10 +208,10 @@
   // EmbeddedFrameSinkProvider interface impl and its CompositorFrameSinkClient.
   MockEmbeddedFrameSinkProvider mock_embedded_frame_sink_provider;
   mojo::Receiver<mojom::blink::EmbeddedFrameSinkProvider>
-      embedded_frame_sink_provider_binding(&mock_embedded_frame_sink_provider);
+      embedded_frame_sink_provider_receiver(&mock_embedded_frame_sink_provider);
   auto override =
       mock_embedded_frame_sink_provider.CreateScopedOverrideMojoInterface(
-          &embedded_frame_sink_provider_binding);
+          &embedded_frame_sink_provider_receiver);
 
   CreateCanvasResourceDispatcher();
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index 3cc3bbc..2afe35d 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -1171,6 +1171,9 @@
       case CanvasResourceType::kSharedImage: {
         DCHECK_NE(usage, ResourceUsage::kSoftwareResourceUsage);
 
+        if (!context_provider_wrapper)
+          continue;
+
         const bool usage_wants_single_buffered =
             usage == ResourceUsage::kAcceleratedDirect2DResourceUsage ||
             usage == ResourceUsage::kAcceleratedDirect3DResourceUsage ||
@@ -1187,9 +1190,14 @@
             context_provider_wrapper->ContextProvider()
                 ->GetCapabilities()
                 .texture_storage_image;
+
+        const int max_texture_size = context_provider_wrapper->ContextProvider()
+                                         ->GetCapabilities()
+                                         .max_texture_size;
         const bool can_use_gmbs =
             is_gpu_memory_buffer_image_allowed &&
-            Platform::Current()->GetGpuMemoryBufferManager();
+            Platform::Current()->GetGpuMemoryBufferManager() &&
+            size.Width() < max_texture_size && size.Height() < max_texture_size;
 
         const bool is_overlay_candidate =
             usage_wants_overlays && can_use_overlays;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
index c0419f4..091c951 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
@@ -146,6 +146,7 @@
     auto* context_provider = context_provider_wrapper_->ContextProvider();
     auto capabilities = context_provider->GetCapabilities();
     capabilities.texture_storage_image = true;
+    capabilities.max_texture_size = 1024;
     static_cast<MockWebGraphisContext3DProviderWrapper*>(context_provider)
         ->SetCapabilities(capabilities);
   }
diff --git a/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc b/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc
index ac68fcd0..4f3d1cc 100644
--- a/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc
+++ b/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc
@@ -13,6 +13,7 @@
 #include "components/viz/common/surfaces/surface_id.h"
 #include "components/viz/common/surfaces/surface_info.h"
 #include "media/base/media_switches.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink.h"
 #include "third_party/blink/public/platform/interface_provider.h"
 #include "third_party/blink/public/platform/platform.h"
@@ -29,20 +30,16 @@
     : observer_(observer),
       update_submission_state_callback_(
           std::move(update_submission_state_callback)),
-      binding_(this),
-      surface_embedder_binding_(this),
       frame_sink_id_(Platform::Current()->GenerateFrameSinkId()),
       parent_frame_sink_id_(parent_frame_sink_id) {
-  mojom::blink::EmbeddedFrameSinkProviderPtr provider;
+  mojo::Remote<mojom::blink::EmbeddedFrameSinkProvider> provider;
   Platform::Current()->GetInterfaceProvider()->GetInterface(
-      mojo::MakeRequest(&provider));
+      provider.BindNewPipeAndPassReceiver());
   // TODO(xlai): Ensure OffscreenCanvas commit() is still functional when a
   // frame-less HTML canvas's document is reparenting under another frame.
   // See crbug.com/683172.
-  blink::mojom::blink::EmbeddedFrameSinkClientPtr client;
-  binding_.Bind(mojo::MakeRequest(&client));
   provider->RegisterEmbeddedFrameSink(parent_frame_sink_id_, frame_sink_id_,
-                                      std::move(client));
+                                      receiver_.BindNewPipeAndPassRemote());
 }
 
 SurfaceLayerBridge::~SurfaceLayerBridge() = default;
@@ -91,8 +88,8 @@
 }
 
 void SurfaceLayerBridge::BindSurfaceEmbedder(
-    mojom::blink::SurfaceEmbedderRequest request) {
-  surface_embedder_binding_.Bind(std::move(request));
+    mojo::PendingReceiver<mojom::blink::SurfaceEmbedder> receiver) {
+  surface_embedder_receiver_.Bind(std::move(receiver));
 }
 
 cc::Layer* SurfaceLayerBridge::GetCcLayer() const {
diff --git a/third_party/blink/renderer/platform/graphics/surface_layer_bridge.h b/third_party/blink/renderer/platform/graphics/surface_layer_bridge.h
index aa37c6c..81b5898 100644
--- a/third_party/blink/renderer/platform/graphics/surface_layer_bridge.h
+++ b/third_party/blink/renderer/platform/graphics/surface_layer_bridge.h
@@ -11,7 +11,8 @@
 #include "base/time/time.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/common/surfaces/surface_id.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink.h"
 #include "third_party/blink/public/platform/web_surface_layer_bridge.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
@@ -40,7 +41,7 @@
 
   // Implementation of blink::mojom::blink::EmbeddedFrameSinkClient
   void BindSurfaceEmbedder(
-      mojom::blink::SurfaceEmbedderRequest request) override;
+      mojo::PendingReceiver<mojom::blink::SurfaceEmbedder> receiver) override;
 
   void EmbedSurface(const viz::SurfaceId& surface_id);
 
@@ -68,8 +69,9 @@
   WebSurfaceLayerBridgeObserver* observer_;
   cc::UpdateSubmissionStateCB update_submission_state_callback_;
   viz::ParentLocalSurfaceIdAllocator parent_local_surface_id_allocator_;
-  mojo::Binding<blink::mojom::blink::EmbeddedFrameSinkClient> binding_;
-  mojo::Binding<blink::mojom::blink::SurfaceEmbedder> surface_embedder_binding_;
+  mojo::Receiver<blink::mojom::blink::EmbeddedFrameSinkClient> receiver_{this};
+  mojo::Receiver<blink::mojom::blink::SurfaceEmbedder>
+      surface_embedder_receiver_{this};
 
   const viz::FrameSinkId frame_sink_id_;
   viz::SurfaceId current_surface_id_;
diff --git a/third_party/blink/renderer/platform/graphics/test/mock_compositor_frame_sink.h b/third_party/blink/renderer/platform/graphics/test/mock_compositor_frame_sink.h
index 3cca9de..4ddae51 100644
--- a/third_party/blink/renderer/platform/graphics/test/mock_compositor_frame_sink.h
+++ b/third_party/blink/renderer/platform/graphics/test/mock_compositor_frame_sink.h
@@ -5,9 +5,13 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TEST_MOCK_COMPOSITOR_FRAME_SINK_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TEST_MOCK_COMPOSITOR_FRAME_SINK_H_
 
+#include <utility>
+
 #include "base/memory/read_only_shared_memory_region.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "gpu/ipc/common/mailbox.mojom-blink.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom-blink.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink.h"
@@ -20,9 +24,9 @@
 class MockCompositorFrameSink : public viz::mojom::blink::CompositorFrameSink {
  public:
   MockCompositorFrameSink(
-      viz::mojom::blink::CompositorFrameSinkRequest request,
-      int num_expected_set_needs_begin_frame_on_construction)
-      : binding_(this, std::move(request)) {
+      mojo::PendingReceiver<viz::mojom::blink::CompositorFrameSink> receiver,
+      int num_expected_set_needs_begin_frame_on_construction) {
+    receiver_.Bind(std::move(receiver));
     EXPECT_CALL(*this, SetNeedsBeginFrame(true))
         .Times(num_expected_set_needs_begin_frame_on_construction);
     if (!num_expected_set_needs_begin_frame_on_construction)
@@ -56,7 +60,7 @@
   MOCK_METHOD1(SetPreferredFrameInterval, void(base::TimeDelta));
 
  private:
-  mojo::Binding<viz::mojom::blink::CompositorFrameSink> binding_;
+  mojo::Receiver<viz::mojom::blink::CompositorFrameSink> receiver_{this};
 
   DISALLOW_COPY_AND_ASSIGN(MockCompositorFrameSink);
 };
diff --git a/third_party/blink/renderer/platform/graphics/test/mock_embedded_frame_sink_provider.h b/third_party/blink/renderer/platform/graphics/test/mock_embedded_frame_sink_provider.h
index 289b4ec..e54ebf4 100644
--- a/third_party/blink/renderer/platform/graphics/test/mock_embedded_frame_sink_provider.h
+++ b/third_party/blink/renderer/platform/graphics/test/mock_embedded_frame_sink_provider.h
@@ -6,8 +6,11 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TEST_MOCK_EMBEDDED_FRAME_SINK_PROVIDER_H_
 
 #include <memory>
+#include <utility>
 
 #include "components/viz/common/surfaces/frame_sink_id.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink.h"
@@ -23,36 +26,38 @@
     : public mojom::blink::EmbeddedFrameSinkProvider {
  public:
   // EmbeddedFrameSinkProvider implementation.
-  MOCK_METHOD3(RegisterEmbeddedFrameSink,
-               void(const viz::FrameSinkId&,
-                    const viz::FrameSinkId&,
-                    mojom::blink::EmbeddedFrameSinkClientPtr));
+  MOCK_METHOD3(
+      RegisterEmbeddedFrameSink,
+      void(const viz::FrameSinkId&,
+           const viz::FrameSinkId&,
+           mojo::PendingRemote<mojom::blink::EmbeddedFrameSinkClient>));
   void CreateCompositorFrameSink(
       const viz::FrameSinkId& frame_sink_id,
-      viz::mojom::blink::CompositorFrameSinkClientPtr client,
-      viz::mojom::blink::CompositorFrameSinkRequest sink) override {
+      mojo::PendingRemote<viz::mojom::blink::CompositorFrameSinkClient> client,
+      mojo::PendingReceiver<viz::mojom::blink::CompositorFrameSink> sink)
+      override {
     mock_compositor_frame_sink_ = std::make_unique<MockCompositorFrameSink>(
         std::move(sink),
         num_expected_set_needs_begin_frame_on_sink_construction_);
     CreateCompositorFrameSink_(frame_sink_id);
   }
   MOCK_METHOD1(CreateCompositorFrameSink_, void(const viz::FrameSinkId&));
-  MOCK_METHOD5(CreateSimpleCompositorFrameSink,
-               void(const viz::FrameSinkId&,
-                    const viz::FrameSinkId&,
-                    mojom::blink::EmbeddedFrameSinkClientPtr,
-                    viz::mojom::blink::CompositorFrameSinkClientPtr,
-                    viz::mojom::blink::CompositorFrameSinkRequest));
+  MOCK_METHOD5(
+      CreateSimpleCompositorFrameSink,
+      void(const viz::FrameSinkId&,
+           const viz::FrameSinkId&,
+           mojo::PendingRemote<mojom::blink::EmbeddedFrameSinkClient>,
+           mojo::PendingRemote<viz::mojom::blink::CompositorFrameSinkClient>,
+           mojo::PendingReceiver<viz::mojom::blink::CompositorFrameSink>));
   MOCK_METHOD2(ConnectToEmbedder,
                void(const viz::FrameSinkId&,
-                    mojom::blink::SurfaceEmbedderRequest));
+                    mojo::PendingReceiver<mojom::blink::SurfaceEmbedder>));
 
   // Utility method to create a scoped EmbeddedFrameSinkProvider override.
   std::unique_ptr<TestingPlatformSupport::ScopedOverrideMojoInterface>
   CreateScopedOverrideMojoInterface(
       mojo::Receiver<mojom::blink::EmbeddedFrameSinkProvider>* receiver) {
     using mojom::blink::EmbeddedFrameSinkProvider;
-    using mojom::blink::EmbeddedFrameSinkProviderRequest;
 
     return std::make_unique<
         TestingPlatformSupport::ScopedOverrideMojoInterface>(WTF::BindRepeating(
@@ -61,7 +66,8 @@
           if (strcmp(interface_name, EmbeddedFrameSinkProvider::Name_))
             return;
           receiver->reset();
-          receiver->Bind(EmbeddedFrameSinkProviderRequest(std::move(pipe)));
+          receiver->Bind(mojo::PendingReceiver<EmbeddedFrameSinkProvider>(
+              std::move(pipe)));
         },
         WTF::Unretained(receiver)));
   }
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
index e06c961..0bae6d34 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/platform/graphics/video_frame_submitter.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/threading/sequenced_task_runner_handle.h"
@@ -12,6 +14,7 @@
 #include "components/viz/common/resources/returned_resource.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_types.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom-blink.h"
 #include "services/viz/public/mojom/hit_test/hit_test_region_list.mojom-blink.h"
@@ -26,8 +29,7 @@
 VideoFrameSubmitter::VideoFrameSubmitter(
     WebContextProviderCallback context_provider_callback,
     std::unique_ptr<VideoFrameResourceProvider> resource_provider)
-    : binding_(this),
-      context_provider_callback_(context_provider_callback),
+    : context_provider_callback_(context_provider_callback),
       resource_provider_(std::move(resource_provider)),
       rotation_(media::VIDEO_ROTATION_0) {
   DETACH_FROM_THREAD(thread_checker_);
@@ -135,8 +137,7 @@
 
 void VideoFrameSubmitter::OnContextLost() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  if (binding_.is_bound())
-    binding_.Unbind();
+  receiver_.reset();
 
   if (context_provider_)
     context_provider_->RemoveObserver(this);
@@ -298,14 +299,12 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(frame_sink_id_.is_valid());
 
-  mojom::blink::EmbeddedFrameSinkProviderPtr provider;
+  mojo::Remote<mojom::blink::EmbeddedFrameSinkProvider> provider;
   Platform::Current()->GetInterfaceProvider()->GetInterface(
-      mojo::MakeRequest(&provider));
+      provider.BindNewPipeAndPassReceiver());
 
-  viz::mojom::blink::CompositorFrameSinkClientPtr client;
-  binding_.Bind(mojo::MakeRequest(&client));
   provider->CreateCompositorFrameSink(
-      frame_sink_id_, std::move(client),
+      frame_sink_id_, receiver_.BindNewPipeAndPassRemote(),
       mojo::MakeRequest(&compositor_frame_sink_));
   if (!surface_embedder_.is_bound()) {
     provider->ConnectToEmbedder(frame_sink_id_,
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.h b/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
index 50e4887..949a261 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
@@ -6,7 +6,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_VIDEO_FRAME_SUBMITTER_H_
 
 #include <memory>
-#include <utility>
 
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/weak_ptr.h"
@@ -17,7 +16,7 @@
 #include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/resources/shared_bitmap.h"
 #include "components/viz/common/surfaces/child_local_surface_id_allocator.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/system/buffer.h"
 #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom-blink.h"
 #include "services/viz/public/mojom/compositing/frame_timing_details.mojom-blink.h"
@@ -132,7 +131,7 @@
   scoped_refptr<viz::RasterContextProvider> context_provider_;
   viz::mojom::blink::CompositorFrameSinkPtr compositor_frame_sink_;
   mojom::blink::SurfaceEmbedderPtr surface_embedder_;
-  mojo::Binding<viz::mojom::blink::CompositorFrameSinkClient> binding_;
+  mojo::Receiver<viz::mojom::blink::CompositorFrameSinkClient> receiver_{this};
   WebContextProviderCallback context_provider_callback_;
   std::unique_ptr<VideoFrameResourceProvider> resource_provider_;
   bool waiting_for_compositor_ack_ = false;
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
index 0d8f68d..3e786d9f 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
@@ -5,6 +5,8 @@
 #include "third_party/blink/renderer/platform/graphics/video_frame_submitter.h"
 
 #include <memory>
+#include <utility>
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/memory/ptr_util.h"
@@ -19,7 +21,8 @@
 #include "components/viz/test/fake_external_begin_frame_source.h"
 #include "components/viz/test/test_context_provider.h"
 #include "media/base/video_frame.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom-blink.h"
 #include "services/viz/public/mojom/hit_test/hit_test_region_list.mojom-blink.h"
@@ -64,8 +67,9 @@
     : public viz::mojom::blink::CompositorFrameSink {
  public:
   VideoMockCompositorFrameSink(
-      viz::mojom::blink::CompositorFrameSinkRequest* request)
-      : binding_(this, std::move(*request)) {}
+      mojo::PendingReceiver<viz::mojom::blink::CompositorFrameSink> receiver) {
+    receiver_.Bind(std::move(receiver));
+  }
   ~VideoMockCompositorFrameSink() override = default;
 
   const viz::CompositorFrame& last_submitted_compositor_frame() const {
@@ -111,7 +115,7 @@
   }
 
  private:
-  mojo::Binding<viz::mojom::blink::CompositorFrameSink> binding_;
+  mojo::Receiver<viz::mojom::blink::CompositorFrameSink> receiver_{this};
 
   viz::CompositorFrame last_submitted_compositor_frame_;
 
@@ -171,16 +175,15 @@
         base::WrapUnique<MockVideoFrameResourceProvider>(resource_provider_));
 
     submitter_->Initialize(video_frame_provider_.get());
-    viz::mojom::blink::CompositorFrameSinkPtr submitter_sink;
-    viz::mojom::blink::CompositorFrameSinkRequest request =
-        mojo::MakeRequest(&submitter_sink);
-    sink_ =
-        std::make_unique<StrictMock<VideoMockCompositorFrameSink>>(&request);
+    mojo::PendingRemote<viz::mojom::blink::CompositorFrameSink> submitter_sink;
+    sink_ = std::make_unique<StrictMock<VideoMockCompositorFrameSink>>(
+        submitter_sink.InitWithNewPipeAndPassReceiver());
 
     // By setting the submission state before we set the sink, we can make
     // testing easier without having to worry about the first sent frame.
     submitter_->SetIsSurfaceVisible(true);
-    submitter_->compositor_frame_sink_ = std::move(submitter_sink);
+    submitter_->compositor_frame_sink_ =
+        viz::mojom::blink::CompositorFrameSinkPtr(std::move(submitter_sink));
     mojom::blink::SurfaceEmbedderPtr embedder;
     mojo::MakeRequest(&embedder);
     submitter_->surface_embedder_ = std::move(embedder);
diff --git a/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.cc
index 3f22017..0dc767d 100644
--- a/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.cc
@@ -98,21 +98,17 @@
 bool BMPImageDecoder::ProcessFileHeader(size_t& img_data_offset) {
   // Read file header.
   DCHECK(!decoded_offset_);
-  if (data_->size() < kSizeOfFileHeader)
-    return false;
-
-  char buffer[kSizeOfFileHeader];
   FastSharedBufferReader fast_reader(data_);
-  const char* file_header =
-      fast_reader.GetConsecutiveData(0, kSizeOfFileHeader, buffer);
-  const uint16_t file_type =
-      (file_header[0] << 8) | static_cast<uint8_t>(file_header[1]);
-  img_data_offset = BMPImageReader::ReadUint32(&file_header[10]);
-  decoded_offset_ = kSizeOfFileHeader;
+  char buffer[kSizeOfFileHeader];
+  const char* file_header;
+  uint16_t file_type;
+  if (!GetFileType(fast_reader, buffer, file_header, file_type))
+    return false;
 
   // See if this is a bitmap filetype we understand.
   enum {
-    BMAP = 0x424D,  // "BM"
+    BMAP = 0x424D,         // "BM"
+    BITMAPARRAY = 0x4241,  // "BA"
     // The following additional OS/2 2.x header values (see
     // http://www.fileformat.info/format/os2bmp/egff.htm ) aren't widely
     // decoded, and are unlikely to be in much use.
@@ -121,10 +117,32 @@
     POINTER = 0x5054,  // "PT"
     COLORICON = 0x4349,  // "CI"
     COLORPOINTER = 0x4350,  // "CP"
-    BITMAPARRAY = 0x4241,  // "BA"
     */
   };
-  return (file_type == BMAP) || SetFailed();
+  if (file_type == BITMAPARRAY) {
+    // Skip initial 14-byte header, try to read the first entry as a BMAP.
+    decoded_offset_ += kSizeOfFileHeader;
+    if (!GetFileType(fast_reader, buffer, file_header, file_type))
+      return false;
+  }
+  if (file_type != BMAP)
+    return SetFailed();
+
+  img_data_offset = BMPImageReader::ReadUint32(&file_header[10]);
+  decoded_offset_ += kSizeOfFileHeader;
+  return true;
+}
+
+bool BMPImageDecoder::GetFileType(const FastSharedBufferReader& fast_reader,
+                                  char* buffer,
+                                  const char*& file_header,
+                                  uint16_t& file_type) const {
+  if (data_->size() - decoded_offset_ < kSizeOfFileHeader)
+    return false;
+  file_header = fast_reader.GetConsecutiveData(decoded_offset_,
+                                               kSizeOfFileHeader, buffer);
+  file_type = (file_header[0] << 8) | static_cast<uint8_t>(file_header[1]);
+  return true;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.h b/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.h
index 6280693..931f360 100644
--- a/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.h
+++ b/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.h
@@ -37,6 +37,7 @@
 namespace blink {
 
 class BMPImageReader;
+class FastSharedBufferReader;
 
 // This class decodes the BMP image format.
 class PLATFORM_EXPORT BMPImageDecoder final : public ImageDecoder {
@@ -72,6 +73,14 @@
   // file header could be decoded.
   bool ProcessFileHeader(size_t& img_data_offset);
 
+  // Uses |fast_reader| and |buffer| to read the file header into |file_header|.
+  // Computes |file_type| from the file header.  Returns whether there was
+  // sufficient data available to read the header.
+  bool GetFileType(const FastSharedBufferReader& fast_reader,
+                   char* buffer,
+                   const char*& file_header,
+                   uint16_t& file_type) const;
+
   // An index into |data_| representing how much we've already decoded.
   // Note that this only tracks data _this_ class decodes; once the
   // BMPImageReader takes over this will not be updated further.
diff --git a/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.cc b/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.cc
index 0320face..a43a482 100644
--- a/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.cc
+++ b/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.cc
@@ -94,10 +94,10 @@
   fast_reader_.ClearCache();
 
   // Calculate size of info header.
-  if (!info_header_.bi_size && !ReadInfoHeaderSize())
+  if (!info_header_.size && !ReadInfoHeaderSize())
     return false;
 
-  const size_t header_end = header_offset_ + info_header_.bi_size;
+  const size_t header_end = header_offset_ + info_header_.size;
   // Read and process info header.
   if ((decoded_offset_ < header_end) && !ProcessInfoHeader())
     return false;
@@ -138,22 +138,22 @@
 
   // Decode the data.
   if (!decoding_and_mask_ && !PastEndOfImage(0) &&
-      !DecodePixelData((info_header_.bi_compression != RLE4) &&
-                       (info_header_.bi_compression != RLE8) &&
-                       (info_header_.bi_compression != RLE24)))
+      !DecodePixelData((info_header_.compression != RLE4) &&
+                       (info_header_.compression != RLE8) &&
+                       (info_header_.compression != RLE24)))
     return false;
 
   // If the image has an AND mask and there was no alpha data, process the
   // mask.
   if (is_in_ico_ && !decoding_and_mask_ &&
-      ((info_header_.bi_bit_count < 16) || !bit_masks_[3] ||
+      ((info_header_.bit_count < 16) || !bit_masks_[3] ||
        !seen_non_zero_alpha_pixel_)) {
     // Reset decoding coordinates to start of image.
     coord_.SetX(0);
     coord_.SetY(is_top_down_ ? 0 : (parent_->Size().Height() - 1));
 
     // The AND mask is stored as 1-bit data.
-    info_header_.bi_bit_count = 1;
+    info_header_.bit_count = 1;
 
     decoding_and_mask_ = true;
   }
@@ -180,29 +180,30 @@
   if ((decoded_offset_ > data_->size()) ||
       ((data_->size() - decoded_offset_) < 4))
     return false;
-  info_header_.bi_size = ReadUint32(0);
+  info_header_.size = ReadUint32(0);
   // Don't increment decoded_offset here, it just makes the code in
   // ProcessInfoHeader() more confusing.
 
   // Don't allow the header to overflow (which would be harmless here, but
   // problematic or at least confusing in other places), or to overrun the
   // image data.
-  const size_t header_end = header_offset_ + info_header_.bi_size;
+  const size_t header_end = header_offset_ + info_header_.size;
   if ((header_end < header_offset_) ||
       (img_data_offset_ && (img_data_offset_ < header_end)))
     return parent_->SetFailed();
 
-  // See if this is a header size we understand:
-  // OS/2 1.x: 12
-  if (info_header_.bi_size == 12)
+  // See if this is a header size we understand.  See comments in
+  // ReadInfoHeader() for more.
+  // OS/2 1.x (and Windows V2): 12
+  if (info_header_.size == 12)
     is_os21x_ = true;
-  // Windows V3: 40
-  else if ((info_header_.bi_size == 40) || IsWindowsV4Plus())
+  // Windows V3+: 40, 52, 56, 108, 124
+  else if ((info_header_.size == 40) || HasRGBMasksInHeader())
     ;
   // OS/2 2.x: any multiple of 4 between 16 and 64, inclusive, or 42 or 46
-  else if ((info_header_.bi_size >= 16) && (info_header_.bi_size <= 64) &&
-           (!(info_header_.bi_size & 3) || (info_header_.bi_size == 42) ||
-            (info_header_.bi_size == 46)))
+  else if ((info_header_.size >= 16) && (info_header_.size <= 64) &&
+           (!(info_header_.size & 3) || (info_header_.size == 42) ||
+            (info_header_.size == 46)))
     is_os22x_ = true;
   else
     return parent_->SetFailed();
@@ -214,113 +215,134 @@
   // Read info header.
   DCHECK_EQ(decoded_offset_, header_offset_);
   if ((decoded_offset_ > data_->size()) ||
-      ((data_->size() - decoded_offset_) < info_header_.bi_size) ||
+      ((data_->size() - decoded_offset_) < info_header_.size) ||
       !ReadInfoHeader())
     return false;
-  decoded_offset_ += info_header_.bi_size;
+  decoded_offset_ += info_header_.size;
 
   // Sanity-check header values.
   if (!IsInfoHeaderValid())
     return parent_->SetFailed();
 
   // Set our size.
-  if (!parent_->SetSize(info_header_.bi_width, info_header_.bi_height))
+  if (!parent_->SetSize(info_header_.width, info_header_.height))
     return false;
 
-  // For paletted images, bitmaps can set biClrUsed to 0 to mean "all
-  // colors", so set it to the maximum number of colors for this bit depth.
-  // Also do this for bitmaps that put too large a value here.
-  if (info_header_.bi_bit_count < 16) {
-    const uint32_t max_colors = static_cast<uint32_t>(1)
-                                << info_header_.bi_bit_count;
-    if (!info_header_.bi_clr_used || (info_header_.bi_clr_used > max_colors))
-      info_header_.bi_clr_used = max_colors;
+  // For paletted images, bitmaps can set clr_used to 0 to mean "all colors", so
+  // set it to the maximum number of colors for this bit depth.  Also do this
+  // for bitmaps that put too large a value here.
+  if (info_header_.bit_count < 16) {
+    const uint32_t max_colors = uint32_t{1} << info_header_.bit_count;
+    if (!info_header_.clr_used || (info_header_.clr_used > max_colors))
+      info_header_.clr_used = max_colors;
   }
 
   // For any bitmaps that set their BitCount to the wrong value, reset the
   // counts now that we've calculated the number of necessary colors, since
   // other code relies on this value being correct.
-  if (info_header_.bi_compression == RLE8)
-    info_header_.bi_bit_count = 8;
-  else if (info_header_.bi_compression == RLE4)
-    info_header_.bi_bit_count = 4;
+  if (info_header_.compression == RLE8)
+    info_header_.bit_count = 8;
+  else if (info_header_.compression == RLE4)
+    info_header_.bit_count = 4;
 
   // Tell caller what still needs to be processed.
-  if (info_header_.bi_bit_count >= 16)
+  if (info_header_.bit_count >= 16)
     need_to_process_bitmasks_ = true;
-  else if (info_header_.bi_bit_count)
+  else if (info_header_.bit_count)
     need_to_process_color_table_ = true;
 
   return true;
 }
 
 bool BMPImageReader::ReadInfoHeader() {
+  // Supported info header formats:
+  // * BITMAPCOREHEADER/OS21XBITMAPHEADER/"Windows V2".  Windows 2.x (?),
+  //   OS/2 1.x.  12 bytes.  Incompatible with all below headers.
+  // * BITMAPINFOHEADER/"Windows V3".  Windows 3.x.  40 bytes.  Changes width/
+  //   height fields to 32 bit and adds features such as compression types.
+  //   (Nomenclature: Note that "Windows V3" here and "BITMAPV3..." below are
+  //   different things.)
+  // * OS22XBITMAPHEADER/BITMAPCOREHEADER2.  OS/2 2.x.  16-64 bytes.  The first
+  //   40 bytes are basically identical to BITMAPINFOHEADER, save that most
+  //   fields are optional.  Further fields, if present, are incompatible with
+  //   all below headers.  Adds features such as halftoning and color spaces
+  //   (not implemented here).
+  // * BITMAPV2HEADER/BITMAPV2INFOHEADER.  52 bytes.  Extends BITMAPINFOHEADER
+  //   with R/G/B masks.  Poorly-documented and obscure.
+  // * BITMAPV3HEADER/BITMAPV3INFOHEADER.  56 bytes.  Extends BITMAPV2HEADER
+  //   with an alpha mask.  Poorly-documented and obscure.
+  // * BITMAPV4HEADER/"Windows V4".  Windows 95.  108 bytes.  Extends
+  //   BITMAPV3HEADER with color space support.
+  // * BITMAPV5HEADER/"Windows V5".  Windows 98.  124 bytes.  Extends
+  //   BITMAPV4HEADER with ICC profile support.
+
   // Pre-initialize some fields that not all headers set.
-  info_header_.bi_compression = RGB;
-  info_header_.bi_clr_used = 0;
+  info_header_.compression = RGB;
+  info_header_.clr_used = 0;
 
   if (is_os21x_) {
-    info_header_.bi_width = ReadUint16(4);
-    info_header_.bi_height = ReadUint16(6);
+    info_header_.width = ReadUint16(4);
+    info_header_.height = ReadUint16(6);
     DCHECK(!is_in_ico_);  // ICO is a Windows format, not OS/2!
-    info_header_.bi_bit_count = ReadUint16(10);
+    info_header_.bit_count = ReadUint16(10);
     return true;
   }
 
-  info_header_.bi_width = ReadUint32(4);
-  info_header_.bi_height = ReadUint32(8);
+  info_header_.width = ReadUint32(4);
+  info_header_.height = ReadUint32(8);
   if (is_in_ico_)
-    info_header_.bi_height /= 2;
-  info_header_.bi_bit_count = ReadUint16(14);
+    info_header_.height /= 2;
+  info_header_.bit_count = ReadUint16(14);
 
   // Read compression type, if present.
-  if (info_header_.bi_size >= 20) {
-    uint32_t bi_compression = ReadUint32(16);
+  if (info_header_.size >= 20) {
+    uint32_t compression = ReadUint32(16);
 
     // Detect OS/2 2.x-specific compression types.
-    if ((bi_compression == 3) && (info_header_.bi_bit_count == 1)) {
-      info_header_.bi_compression = HUFFMAN1D;
+    if ((compression == 3) && (info_header_.bit_count == 1)) {
+      info_header_.compression = HUFFMAN1D;
       is_os22x_ = true;
-    } else if ((bi_compression == 4) && (info_header_.bi_bit_count == 24)) {
-      info_header_.bi_compression = RLE24;
+    } else if ((compression == 4) && (info_header_.bit_count == 24)) {
+      info_header_.compression = RLE24;
       is_os22x_ = true;
-    } else if (bi_compression > 5)
+    } else if (compression > ALPHABITFIELDS) {
       return parent_->SetFailed();  // Some type we don't understand.
-    else
-      info_header_.bi_compression =
-          static_cast<CompressionType>(bi_compression);
+    } else {
+      info_header_.compression = static_cast<CompressionType>(compression);
+    }
   }
 
   // Read colors used, if present.
-  if (info_header_.bi_size >= 36)
-    info_header_.bi_clr_used = ReadUint32(32);
+  if (info_header_.size >= 36)
+    info_header_.clr_used = ReadUint32(32);
 
-  // Windows V4+ can safely read the four bitmasks from 40-56 bytes in, so do
-  // that here. If the bit depth is less than 16, these values will be ignored
-  // by the image data decoders. If the bit depth is at least 16 but the
-  // compression format isn't BITFIELDS, the RGB bitmasks will be ignored and
-  // overwritten in processBitmasks(). (The alpha bitmask will never be
-  // overwritten: images that actually want alpha have to specify a valid
-  // alpha mask. See comments in ProcessBitmasks().)
+  // If we can safely read the four bitmasks from 40-56 bytes in, do that here.
+  // If the bit depth is less than 16, these values will be ignored by the image
+  // data decoders. If the bit depth is at least 16 but the compression format
+  // isn't [ALPHA]BITFIELDS, the RGB bitmasks will be ignored and overwritten in
+  // processBitmasks(). (The alpha bitmask will never be overwritten: images
+  // that actually want alpha have to specify a valid alpha mask. See comments
+  // in ProcessBitmasks().)
   //
-  // For non-Windows V4+, bit_masks_[] et. al will be initialized later
-  // during ProcessBitmasks().
-  if (IsWindowsV4Plus()) {
+  // For other BMPs, bit_masks_[] et. al will be initialized later during
+  // ProcessBitmasks().
+  if (HasRGBMasksInHeader()) {
     bit_masks_[0] = ReadUint32(40);
     bit_masks_[1] = ReadUint32(44);
     bit_masks_[2] = ReadUint32(48);
-    bit_masks_[3] = ReadUint32(52);
   }
+  if (HasAlphaMaskInHeader())
+    bit_masks_[3] = ReadUint32(52);
 
   // Detect top-down BMPs.
-  if (info_header_.bi_height < 0) {
+  if (info_header_.height < 0) {
     // We can't negate INT32_MIN below to get a positive int32_t.
     // IsInfoHeaderValid() will reject heights of 1 << 16 or larger anyway,
     // so just reject this bitmap now.
-    if (info_header_.bi_height == INT32_MIN)
+    if (info_header_.height == INT32_MIN)
       return parent_->SetFailed();
     is_top_down_ = true;
-    info_header_.bi_height = -info_header_.bi_height;
+    info_header_.height = -info_header_.height;
   }
 
   return true;
@@ -329,7 +351,7 @@
 bool BMPImageReader::IsInfoHeaderValid() const {
   // Non-positive widths/heights are invalid.  (We've already flipped the
   // sign of the height for top-down bitmaps.)
-  if ((info_header_.bi_width <= 0) || !info_header_.bi_height)
+  if ((info_header_.width <= 0) || !info_header_.height)
     return false;
 
   // Only Windows V3+ has top-down bitmaps.
@@ -337,22 +359,22 @@
     return false;
 
   // Only bit depths of 1, 4, 8, or 24 are universally supported.
-  if ((info_header_.bi_bit_count != 1) && (info_header_.bi_bit_count != 4) &&
-      (info_header_.bi_bit_count != 8) && (info_header_.bi_bit_count != 24)) {
+  if ((info_header_.bit_count != 1) && (info_header_.bit_count != 4) &&
+      (info_header_.bit_count != 8) && (info_header_.bit_count != 24)) {
     // Windows V3+ additionally supports bit depths of 0 (for embedded
-    // JPEG/PNG images), 16, and 32.
+    // JPEG/PNG images), 2 (on Windows CE), 16, and 32.
     if (is_os21x_ || is_os22x_ ||
-        (info_header_.bi_bit_count && (info_header_.bi_bit_count != 16) &&
-         (info_header_.bi_bit_count != 32)))
+        (info_header_.bit_count && (info_header_.bit_count != 2) &&
+         (info_header_.bit_count != 16) && (info_header_.bit_count != 32)))
       return false;
   }
 
   // Each compression type is only valid with certain bit depths (except RGB,
   // which can be used with any bit depth). Also, some formats do not support
   // some compression types.
-  switch (info_header_.bi_compression) {
+  switch (info_header_.compression) {
     case RGB:
-      if (!info_header_.bi_bit_count)
+      if (!info_header_.bit_count)
         return false;
       break;
 
@@ -361,40 +383,40 @@
       // Compression = RLE4" (which means "4 bit, but with a 2-color table"),
       // so also allow the paletted RLE compression types to have too low a
       // bit count; we'll correct this later.
-      if (!info_header_.bi_bit_count || (info_header_.bi_bit_count > 8))
+      if (!info_header_.bit_count || (info_header_.bit_count > 8))
         return false;
       break;
 
     case RLE4:
       // See comments in RLE8.
-      if (!info_header_.bi_bit_count || (info_header_.bi_bit_count > 4))
+      if (!info_header_.bit_count || (info_header_.bit_count > 4))
         return false;
       break;
 
     case BITFIELDS:
+    case ALPHABITFIELDS:
       // Only valid for Windows V3+.
       if (is_os21x_ || is_os22x_ ||
-          ((info_header_.bi_bit_count != 16) &&
-           (info_header_.bi_bit_count != 32)))
+          ((info_header_.bit_count != 16) && (info_header_.bit_count != 32)))
         return false;
       break;
 
     case JPEG:
     case PNG:
       // Only valid for Windows V3+.
-      if (is_os21x_ || is_os22x_ || info_header_.bi_bit_count)
+      if (is_os21x_ || is_os22x_ || info_header_.bit_count)
         return false;
       break;
 
     case HUFFMAN1D:
       // Only valid for OS/2 2.x.
-      if (!is_os22x_ || (info_header_.bi_bit_count != 1))
+      if (!is_os22x_ || (info_header_.bit_count != 1))
         return false;
       break;
 
     case RLE24:
       // Only valid for OS/2 2.x.
-      if (!is_os22x_ || (info_header_.bi_bit_count != 24))
+      if (!is_os22x_ || (info_header_.bit_count != 24))
         return false;
       break;
 
@@ -405,11 +427,6 @@
       return false;
   }
 
-  // Top-down bitmaps cannot be compressed; they must be RGB or BITFIELDS.
-  if (is_top_down_ && (info_header_.bi_compression != RGB) &&
-      (info_header_.bi_compression != BITFIELDS))
-    return false;
-
   // Reject the following valid bitmap types that we don't currently bother
   // decoding.  Few other people decode these either, they're unlikely to be
   // in much use.
@@ -417,18 +434,16 @@
   //   * Bitmaps larger than 2^16 pixels in either dimension (Windows
   //     probably doesn't draw these well anyway, and the decoded data would
   //     take a lot of memory).
-  if ((info_header_.bi_width >= (1 << 16)) ||
-      (info_header_.bi_height >= (1 << 16)))
+  if ((info_header_.width >= (1 << 16)) || (info_header_.height >= (1 << 16)))
     return false;
   //   * Windows V3+ JPEG-in-BMP and PNG-in-BMP bitmaps (supposedly not found
   //     in the wild, only used to send data to printers?).
-  if ((info_header_.bi_compression == JPEG) ||
-      (info_header_.bi_compression == PNG))
+  if ((info_header_.compression == JPEG) || (info_header_.compression == PNG))
     return false;
   //   * OS/2 2.x Huffman-encoded monochrome bitmaps (see
   //      http://www.fileformat.info/mirror/egff/ch09_05.htm , re: "G31D"
   //      algorithm).
-  if (info_header_.bi_compression == HUFFMAN1D)
+  if (info_header_.compression == HUFFMAN1D)
     return false;
 
   return true;
@@ -436,24 +451,27 @@
 
 bool BMPImageReader::ProcessBitmasks() {
   // Create bit_masks_[] values for R/G/B.
-  if (info_header_.bi_compression != BITFIELDS) {
+  if ((info_header_.compression != BITFIELDS) &&
+      (info_header_.compression != ALPHABITFIELDS)) {
     // The format doesn't actually use bitmasks.  To simplify the decode
     // logic later, create bitmasks for the RGB data.  For Windows V4+,
     // this overwrites the masks we read from the header, which are
     // supposed to be ignored in non-BITFIELDS cases.
     // 16 bits:    MSB <-                     xRRRRRGG GGGBBBBB -> LSB
     // 24/32 bits: MSB <- [AAAAAAAA] RRRRRRRR GGGGGGGG BBBBBBBB -> LSB
-    const int num_bits = (info_header_.bi_bit_count == 16) ? 5 : 8;
-    for (int i = 0; i <= 2; ++i)
-      bit_masks_[i] = ((static_cast<uint32_t>(1) << (num_bits * (3 - i))) - 1) ^
-                      ((static_cast<uint32_t>(1) << (num_bits * (2 - i))) - 1);
-  } else if (!IsWindowsV4Plus()) {
-    // For Windows V4+ BITFIELDS mode bitmaps, this was already done when
-    // we read the info header.
+    const int num_bits = (info_header_.bit_count == 16) ? 5 : 8;
+    for (int i = 0; i <= 2; ++i) {
+      bit_masks_[i] = ((uint32_t{1} << (num_bits * (3 - i))) - 1) ^
+                      ((uint32_t{1} << (num_bits * (2 - i))) - 1);
+    }
+  } else if (!HasRGBMasksInHeader()) {
+    // For HasRGBMasksInHeader() bitmaps, this was already done when we read the
+    // info header.
 
     // Fail if we don't have enough file space for the bitmasks.
-    const size_t header_end = header_offset_ + info_header_.bi_size;
-    const size_t kBitmasksSize = 12;
+    const size_t header_end = header_offset_ + info_header_.size;
+    const bool read_alpha = info_header_.compression == ALPHABITFIELDS;
+    const size_t kBitmasksSize = read_alpha ? 16 : 12;
     const size_t bitmasks_end = header_end + kBitmasksSize;
     if ((bitmasks_end < header_end) ||
         (img_data_offset_ && (img_data_offset_ < bitmasks_end)))
@@ -465,23 +483,28 @@
     bit_masks_[0] = ReadUint32(0);
     bit_masks_[1] = ReadUint32(4);
     bit_masks_[2] = ReadUint32(8);
+    if (read_alpha)
+      bit_masks_[3] = ReadUint32(12);
 
     decoded_offset_ += kBitmasksSize;
   }
 
   // Alpha is a poorly-documented and inconsistently-used feature.
   //
-  // Windows V4+ has an alpha bitmask in the info header. Unlike the R/G/B
+  // BITMAPV3HEADER+ have an alpha bitmask in the info header.  Unlike the R/G/B
   // bitmasks, the MSDN docs don't indicate that it is only valid for the
   // BITFIELDS compression format, so we respect it at all times.
   //
-  // To complicate things, Windows V3 BMPs, which lack this mask, can specify
-  // 32bpp format, which to any sane reader would imply an 8-bit alpha
-  // channel -- and for BMPs-in-ICOs, that's precisely what's intended to
-  // happen. There also exist standalone BMPs in this format which clearly
-  // expect the alpha channel to be respected. However, there are many other
-  // BMPs which, for example, fill this channel with all 0s, yet clearly
-  // expect to not be displayed as a fully-transparent rectangle.
+  // Windows CE supports the ALPHABITFIELDS compression format, which is rare.
+  // We assume any mask specified by this format is valid as well.
+  //
+  // To complicate things, Windows V3 BMPs, which lack a mask, can specify 32bpp
+  // format, which to any sane reader would imply an 8-bit alpha channel -- and
+  // for BMPs-in-ICOs, that's precisely what's intended to happen. There also
+  // exist standalone BMPs in this format which clearly expect the alpha channel
+  // to be respected. However, there are many other BMPs which, for example,
+  // fill this channel with all 0s, yet clearly expect to not be displayed as a
+  // fully-transparent rectangle.
   //
   // If these were the only two types of Windows V3, 32bpp BMPs in the wild,
   // we could distinguish between them by scanning the alpha channel in the
@@ -500,11 +523,12 @@
   // and we have to choose what to break. Given the paragraph above, we match
   // other browsers and ignore alpha in Windows V3 BMPs except inside ICO
   // files.
-  if (!IsWindowsV4Plus())
-    bit_masks_[3] = (is_in_ico_ && (info_header_.bi_compression != BITFIELDS) &&
-                     (info_header_.bi_bit_count == 32))
-                        ? static_cast<uint32_t>(0xff000000)
-                        : 0;
+  if (!HasAlphaMaskInHeader() && (info_header_.compression != ALPHABITFIELDS)) {
+    const bool use_mask = is_in_ico_ &&
+                          (info_header_.compression != BITFIELDS) &&
+                          (info_header_.bit_count == 32);
+    bit_masks_[3] = use_mask ? uint32_t{0xff000000} : 0;
+  }
 
   // We've now decoded all the non-image data we care about.  Skip anything
   // else before the actual raster data.
@@ -517,9 +541,9 @@
     // Trim the mask to the allowed bit depth.  Some Windows V4+ BMPs
     // specify a bogus alpha channel in bits that don't exist in the pixel
     // data (for example, bits 25-31 in a 24-bit RGB format).
-    if (info_header_.bi_bit_count < 32)
-      bit_masks_[i] &=
-          ((static_cast<uint32_t>(1) << info_header_.bi_bit_count) - 1);
+    if (info_header_.bit_count < 32) {
+      bit_masks_[i] &= ((uint32_t{1} << info_header_.bit_count) - 1);
+    }
 
     // For empty masks (common on the alpha channel, especially after the
     // trimming above), quickly clear the shift and LUT address and
@@ -566,29 +590,40 @@
 }
 
 bool BMPImageReader::ProcessColorTable() {
-  // Fail if we don't have enough file space for the color table.
-  const size_t header_end = header_offset_ + info_header_.bi_size;
-  const size_t table_size_in_bytes =
-      info_header_.bi_clr_used * (is_os21x_ ? 3 : 4);
+  // On non-OS/2 1.x, an extra padding byte is present, which we need to skip.
+  const size_t bytes_per_color = is_os21x_ ? 3 : 4;
+
+  const size_t header_end = header_offset_ + info_header_.size;
+  size_t colors_in_palette = info_header_.clr_used;
+  size_t table_size_in_bytes = colors_in_palette * bytes_per_color;
   const size_t table_end = header_end + table_size_in_bytes;
-  if ((table_end < header_end) ||
-      (img_data_offset_ && (img_data_offset_ < table_end)))
+  if (table_end < header_end)
     return parent_->SetFailed();
 
+  // Some BMPs don't contain a complete palette.  Avoid reading off the end.
+  if (img_data_offset_ && (img_data_offset_ < table_end)) {
+    colors_in_palette = (img_data_offset_ - header_end) / bytes_per_color;
+    table_size_in_bytes = colors_in_palette * bytes_per_color;
+  }
+
   // Read color table.
   if ((decoded_offset_ > data_->size()) ||
       ((data_->size() - decoded_offset_) < table_size_in_bytes))
     return false;
-  color_table_.resize(info_header_.bi_clr_used);
+  color_table_.resize(info_header_.clr_used);
 
-  // On non-OS/2 1.x, an extra padding byte is present, which we need to skip.
-  const size_t bytes_per_color = is_os21x_ ? 3 : 4;
-  for (size_t i = 0; i < info_header_.bi_clr_used; ++i) {
+  for (size_t i = 0; i < colors_in_palette; ++i) {
     color_table_[i].rgb_blue = ReadUint8(0);
     color_table_[i].rgb_green = ReadUint8(1);
     color_table_[i].rgb_red = ReadUint8(2);
     decoded_offset_ += bytes_per_color;
   }
+  // Explicitly zero any colors past the end of a truncated palette.
+  for (size_t i = colors_in_palette; i < info_header_.clr_used; ++i) {
+    color_table_[i].rgb_blue = 0;
+    color_table_[i].rgb_green = 0;
+    color_table_[i].rgb_red = 0;
+  }
 
   // We've now decoded all the non-image data we care about.  Skip anything
   // else before the actual raster data.
@@ -706,7 +741,7 @@
       // here; ignore pixels past the end of the row.
       const int end_x = std::min(coord_.X() + count, parent_->Size().Width());
 
-      if (info_header_.bi_compression == RLE24) {
+      if (info_header_.compression == RLE24) {
         // Bail if there isn't enough data.
         if ((data_->size() - decoded_offset_) < 4)
           return kInsufficientData;
@@ -719,14 +754,14 @@
         // color indexes in the upper and lower 4 bits of the byte,
         // which are alternated.
         size_t color_indexes[2] = {code, code};
-        if (info_header_.bi_compression == RLE4) {
+        if (info_header_.compression == RLE4) {
           color_indexes[0] = (color_indexes[0] >> 4) & 0xf;
           color_indexes[1] &= 0xf;
         }
         for (int which = 0; coord_.X() < end_x;) {
           // Some images specify color values past the end of the
           // color table; set these pixels to black.
-          if (color_indexes[which] < info_header_.bi_clr_used)
+          if (color_indexes[which] < info_header_.clr_used)
             SetI(color_indexes[which]);
           else
             SetRGBA(0, 0, 0, 255);
@@ -755,10 +790,10 @@
 
   // Determine how many bytes of data the requested number of pixels
   // requires.
-  const size_t pixels_per_byte = 8 / info_header_.bi_bit_count;
-  const size_t bytes_per_pixel = info_header_.bi_bit_count / 8;
+  const size_t pixels_per_byte = 8 / info_header_.bit_count;
+  const size_t bytes_per_pixel = info_header_.bit_count / 8;
   const size_t unpadded_num_bytes =
-      (info_header_.bi_bit_count < 16)
+      (info_header_.bit_count < 16)
           ? ((num_pixels + pixels_per_byte - 1) / pixels_per_byte)
           : (num_pixels * bytes_per_pixel);
   // RLE runs are zero-padded at the end to a multiple of 16 bits.  Non-RLE
@@ -774,18 +809,18 @@
     if ((data_->size() - decoded_offset_) < padded_num_bytes)
       return kInsufficientData;
 
-    if (info_header_.bi_bit_count < 16) {
+    if (info_header_.bit_count < 16) {
       // Paletted data.  Pixels are stored little-endian within bytes.
       // Decode pixels one byte at a time, left to right (so, starting at
       // the most significant bits in the byte).
-      const uint8_t mask = (1 << info_header_.bi_bit_count) - 1;
+      const uint8_t mask = (1 << info_header_.bit_count) - 1;
       for (size_t end_offset = decoded_offset_ + unpadded_num_bytes;
            decoded_offset_ < end_offset; ++decoded_offset_) {
         uint8_t pixel_data = ReadUint8(0);
         for (size_t pixel = 0;
              (pixel < pixels_per_byte) && (coord_.X() < end_x); ++pixel) {
           const size_t color_index =
-              (pixel_data >> (8 - info_header_.bi_bit_count)) & mask;
+              (pixel_data >> (8 - info_header_.bit_count)) & mask;
           if (decoding_and_mask_) {
             // There's no way to accurately represent an AND + XOR
             // operation as an RGBA image, so where the AND values
@@ -795,16 +830,17 @@
             if (color_index) {
               SetRGBA(0, 0, 0, 0);
               buffer_->SetHasAlpha(true);
-            } else
+            } else {
               coord_.Move(1, 0);
+            }
           } else {
             // See comments near the end of ProcessRLEData().
-            if (color_index < info_header_.bi_clr_used)
+            if (color_index < info_header_.clr_used)
               SetI(color_index);
             else
               SetRGBA(0, 0, 0, 255);
           }
-          pixel_data <<= info_header_.bi_bit_count;
+          pixel_data <<= info_header_.bit_count;
         }
       }
     } else {
@@ -830,8 +866,9 @@
           if (seen_zero_alpha_pixel_) {
             buffer_->ZeroFillPixelData();
             seen_zero_alpha_pixel_ = false;
-          } else if (alpha != 255)
+          } else if (alpha != 255) {
             buffer_->SetHasAlpha(true);
+          }
         }
 
         SetRGBA(GetComponent(pixel, 0), GetComponent(pixel, 1),
diff --git a/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.h b/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.h
index 342d62e..bfeef79 100644
--- a/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.h
+++ b/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.h
@@ -96,6 +96,7 @@
     BITFIELDS = 3,
     JPEG = 4,
     PNG = 5,
+    ALPHABITFIELDS = 6,  // Windows CE only
     // OS/2 2.x-only
     HUFFMAN1D,  // Stored in file as 3
     RLE24,      // Stored in file as 4
@@ -106,16 +107,16 @@
     kInsufficientData,
   };
 
-  // These are based on the Windows BITMAPINFOHEADER and RGBTRIPLE
-  // structs, but with unnecessary entries removed.
+  // These are based on the Windows BITMAPINFOHEADER and RGBTRIPLE structs, but
+  // with unnecessary entries removed.
   struct BitmapInfoHeader {
     DISALLOW_NEW();
-    uint32_t bi_size;
-    int32_t bi_width;
-    int32_t bi_height;
-    uint16_t bi_bit_count;
-    CompressionType bi_compression;
-    uint32_t bi_clr_used;
+    uint32_t size;
+    int32_t width;
+    int32_t height;
+    uint16_t bit_count;
+    CompressionType compression;
+    uint32_t clr_used;
   };
   struct RGBTriple {
     DISALLOW_NEW();
@@ -154,16 +155,29 @@
   // of header values from the byte stream.  Returns false on error.
   bool ReadInfoHeader();
 
-  // Returns true if this is a Windows V4+ BMP.
-  inline bool IsWindowsV4Plus() const {
-    // Windows V4 info header is 108 bytes.  V5 is 124 bytes.
-    return (info_header_.bi_size == 108) || (info_header_.bi_size == 124);
+  // Returns true if this BMP has an alpha mask in the info header
+  // (BITMAPV3HEADER+).  See comments in ReadInfoHeader() for more.
+  inline bool HasAlphaMaskInHeader() const {
+    // BITMAPV3HEADER is 56 bytes; this is also a valid OS/2 2.x header size, so
+    // exclude that case.
+    return (info_header_.size == 56 && !is_os22x_) ||  // BITMAPV3HEADER
+           (info_header_.size == 108) ||               // BITMAPV4HEADER
+           (info_header_.size == 124);                 // BITMAPV5HEADER
+  }
+
+  // Returns true if this BMP has RGB masks in the info header
+  // (BITMAPV2HEADER+).  See comments in ReadInfoHeader() for more.
+  inline bool HasRGBMasksInHeader() const {
+    // BITMAPV2HEADER is 52 bytes; this is also a valid OS/2 2.x header size, so
+    // exclude that case.
+    return (info_header_.size == 52 && !is_os22x_) ||  // BITMAPV2HEADER
+           HasAlphaMaskInHeader();                     // BITMAPV3HEADER+
   }
 
   // Returns false if consistency errors are found in the info header.
   bool IsInfoHeaderValid() const;
 
-  // For BI_BITFIELDS images, initializes the bit_masks_[] and
+  // For BI_[ALPHA]BITFIELDS images, initializes the bit_masks_[] and
   // bit_offsets_[] arrays.  ProcessInfoHeader() will initialize these for
   // other compression types where needed.
   bool ProcessBitmasks();
diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
index c4ff566..be591272 100644
--- a/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
@@ -62,7 +62,7 @@
 }
 
 inline bool MatchesBMPSignature(const char* contents) {
-  return !memcmp(contents, "BM", 2);
+  return !memcmp(contents, "BM", 2) || !memcmp(contents, "BA", 2);
 }
 
 static constexpr size_t kLongestSignatureLength = sizeof("RIFF????WEBPVP") - 1;
diff --git a/third_party/blink/renderer/platform/instrumentation/histogram.cc b/third_party/blink/renderer/platform/instrumentation/histogram.cc
index f607bf9c..d5285d4 100644
--- a/third_party/blink/renderer/platform/instrumentation/histogram.cc
+++ b/third_party/blink/renderer/platform/instrumentation/histogram.cc
@@ -26,6 +26,11 @@
   histogram_->Add(sample);
 }
 
+void CustomCountHistogram::CountMany(base::HistogramBase::Sample sample,
+                                     int count) {
+  histogram_->AddCount(sample, count);
+}
+
 void CustomCountHistogram::CountMicroseconds(base::TimeDelta delta) {
   Count(base::saturated_cast<base::HistogramBase::Sample>(
       delta.InMicroseconds()));
diff --git a/third_party/blink/renderer/platform/instrumentation/histogram.h b/third_party/blink/renderer/platform/instrumentation/histogram.h
index deeb9ef6..134b73e 100644
--- a/third_party/blink/renderer/platform/instrumentation/histogram.h
+++ b/third_party/blink/renderer/platform/instrumentation/histogram.h
@@ -29,6 +29,7 @@
                        base::HistogramBase::Sample max,
                        int32_t bucket_count);
   void Count(base::HistogramBase::Sample);
+  void CountMany(base::HistogramBase::Sample, int count);
   void CountMicroseconds(base::TimeDelta);
   void CountMilliseconds(base::TimeDelta);
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index c35e5e7..d0f9ab8 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -2041,6 +2041,14 @@
     return;
   ResourceRequest resource_request(url);
   resource_request.SetRequestContext(request_context);
+  if (!resource_request.PriorityHasBeenSet()) {
+    resource_request.SetPriority(ComputeLoadPriority(
+        resource->GetType(), resource_request, ResourcePriority::kNotVisible,
+        FetchParameters::DeferOption::kNoDefer,
+        FetchParameters::SpeculativePreloadType::kNotSpeculative,
+        false /* is_link_preload */));
+  }
+
   ResourceLoaderOptions options = resource->Options();
   options.initiator_info.name = initiator_name;
   FetchParameters params(resource_request, options);
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index a4bfbb7..a817830 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1520,10 +1520,6 @@
       settable_from_internals: true,
     },
     {
-      name: "SmoothScrollJSIntervention",
-      status: "stable",
-    },
-    {
       name: "SmsReceiver",
       status: {"Android": "experimental", "default": "test"},
     },
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index ea10fa3..47abe17f 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3105,6 +3105,8 @@
 
 crbug.com/552085 external/wpt/css/css-cascade/important-prop.html [ Failure ]
 
+crbug.com/626703 external/wpt/css/css-animations/Element-getAnimations.tentative.html [ Pass Failure ]
+
 # TODO(chrishtr): rebaseline these
 crbug.com/921242 [ Mac ] paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer.html [ Failure ]
 crbug.com/921242 [ Mac ] paint/invalidation/scroll/fixed-under-composited-absolute-scrolled.html [ Failure ]
@@ -3250,6 +3252,7 @@
 crbug.com/994214 external/wpt/html/cross-origin-opener-policy/coop-navigated-popup.https.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Retina ] virtual/cascade/external/wpt/css/css-paint-api/one-custom-property-animation.https.html [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/css/css-scroll-anchoring/infinite-scroll-event.tentative.html [ Timeout ]
 crbug.com/626703 [ Mac ] external/wpt/css/css-scroll-anchoring/infinite-scroll-event.tentative.html [ Timeout ]
 crbug.com/626703 [ Win ] external/wpt/css/css-scroll-anchoring/infinite-scroll-event.tentative.html [ Timeout ]
@@ -3367,7 +3370,6 @@
 crbug.com/626703 [ Retina ] virtual/blink-cors/external/wpt/fetch/api/idl.any.serviceworker.html [ Timeout ]
 crbug.com/626703 [ Retina ] virtual/blink-cors/external/wpt/referrer-policy/no-referrer/meta-referrer/cross-origin/http-http/xhr-request/swap-origin-redirect/generic.http.html [ Timeout ]
 crbug.com/626703 [ Retina ] virtual/blink-cors/external/wpt/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/xhr-request/swap-origin-redirect/cross-origin.http.html [ Timeout ]
-crbug.com/626703 [ Retina ] virtual/blink-cors/external/wpt/fetch/api/basic/scheme-data.any.worker.html [ Timeout ]
 crbug.com/626703 [ Retina ] virtual/blink-cors/external/wpt/fetch/api/policies/referrer-unsafe-url.html [ Timeout ]
 crbug.com/626703 [ Retina ] virtual/blink-cors/external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/fetch-request/swap-origin-redirect/same-origin-insecure.http.html [ Timeout ]
 crbug.com/626703 [ Retina ] virtual/blink-cors/external/wpt/referrer-policy/no-referrer/http-rp/cross-origin/http-http/img-tag/no-redirect/generic.http.html [ Timeout ]
@@ -4728,6 +4730,7 @@
 crbug.com/785230 [ Win ] external/wpt/css/css-text-decor/text-decoration-skip-ink-vertical-001.html [ Failure ]
 
 crbug.com/912362 external/wpt/web-animations/timing-model/timelines/timelines.html [ Failure ]
+crbug.com/997149 external/wpt/web-animations/timing-model/animations/pausing-an-animation.html [ Pass Failure ]
 
 # Decoding test timeout on Win7. Marked flaky to get imported in case it's flaky timeout everywhere.
 crbug.com/862938 external/wpt/encoding/textdecoder-fatal-single-byte.any.worker.html [ Pass Timeout ]
@@ -4932,6 +4935,7 @@
 crbug.com/678499 virtual/blink-cors/http/tests/security/contentSecurityPolicy/require-sri-for/require-sri-for-script-preload-allowed.php [ Failure Pass ]
 
 crbug.com/747751 [ Win ] http/tests/devtools/application-panel/resources-panel-resource-preview.js [ Failure Pass ]
+crbug.com/747751 http/tests/devtools/application-panel/storage-view-reports-quota.js [ Pass Timeout ]
 
 crbug.com/689781 external/wpt/media-source/mediasource-duration.html [ Failure Pass ]
 crbug.com/689781 [ Win Mac ] http/tests/media/media-source/mediasource-duration.html [ Failure Pass ]
@@ -5866,6 +5870,7 @@
 crbug.com/923244 external/wpt/webrtc/RTCDTMFSender-ontonechange.https.html [ Failure Pass ]
 # WebRTC Plan B
 crbug.com/920979 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-remote-track-mute.https.html [ Timeout ]
+crbug.com/997201 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https.html [ Pass Failure ]
 crbug.com/920979 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCTrackEvent-fire.html [ Timeout ]
 # WebRTC codec tests - software H.264 not present on webkit bot family
 crbug.com/840659 external/wpt/webrtc/protocol/video-codecs.https.html [ Pass Failure ]
@@ -6479,7 +6484,6 @@
 
 #Sherrif 2019-08-16
 crbug.com/994692 [ Linux Win ] compositing/reflections/nested-reflection-anchor-point.html [ Pass Failure ]
-crbug.com/994746 animations/events/play-state-initially-paused-start-event.html [ Pass Timeout ]
 
 # Video flakily not being being rendered on Mac
 crbug.com/979298 [ Mac ] media/controls/captions-menu-always-visible.html [ Pass Failure ]
@@ -6518,15 +6522,17 @@
 crbug.com/994008 [ Linux Win ] http/tests/devtools/elements/styles-3/styles-computed-trace.js [ Pass Failure ]
 crbug.com/994034 [ Linux Win ] http/tests/devtools/elements/styles-3/styles-add-new-rule-colon.js [ Pass Failure ]
 crbug.com/994027 [ Linux Win ] http/tests/devtools/elements/styles-2/force-pseudo-state.js [ Pass Failure ]
-crbug.com/995142 crbug.com/900706 [ Mac ] fast/canvas/canvas-large-pattern.html [ Pass Failure ]
-crbug.com/995142 crbug.com/900706 [ Mac ] virtual/gpu/fast/canvas/canvas-large-pattern.html [ Pass Failure ]
 crbug.com/995142 crbug.com/900706 [ Mac ] virtual/gpu/fast/canvas/pixelated-canvas.html [ Pass Failure ]
 crbug.com/995142 crbug.com/900706 [ Mac ] virtual/gpu/fast/canvas/pixelated-resize.html [ Pass Failure ]
 crbug.com/995142 crbug.com/900706 [ Mac ] virtual/gpu/fast/canvas/pixelated.html [ Pass Failure ]
-crbug.com/997149 [ Linux ] external/wpt/web-animations/timing-model/animations/pausing-an-animation.html [ Pass Failure ]
 
 crbug.com/995669 [ Win ] http/tests/media/video-throttled-load-metadata.html [ Pass Failure Crash ]
 crbug.com/995669 [ Win ] virtual/audio-service/http/tests/media/video-throttled-load-metadata.html [ Pass Failure Crash ]
 
+# Sheriff 2019-08-26
+crbug.com/997669 [ Win ] http/tests/devtools/search/sources-search-scope-in-files.js [ Pass Crash ]
+crbug.com/997669 [ Win ] http/tests/devtools/search/sources-search-scope.js [ Pass Crash ]
+crbug.com/626703 external/wpt/css/css-paint-api/custom-property-animation-on-main-thread.https.html [ Pass Failure ]
+
 # Pending enabling scroll to text feature
 crbug.com/919204 external/wpt/scroll-to-text-fragment/scroll-to-text-fragment.html [ Skip ]
diff --git a/third_party/blink/web_tests/animations/interpolation/caret-color-interpolation.html b/third_party/blink/web_tests/animations/interpolation/caret-color-interpolation.html
index 3f0a8cfc..6513809c 100644
--- a/third_party/blink/web_tests/animations/interpolation/caret-color-interpolation.html
+++ b/third_party/blink/web_tests/animations/interpolation/caret-color-interpolation.html
@@ -17,83 +17,8 @@
 <template id="target-template">T</template>
 <script src="resources/interpolation-test.js"></script>
 <script>
-assertInterpolation({
-  property: 'caret-color',
-  from: neutralKeyframe,
-  to: 'green',
-}, [
-  {at: -0.3, is: 'rgb(255, 255, 0)'},
-  {at: 0, is: 'rgb(255, 255, 0)'},
-  {at: 0.3, is: 'rgb(179, 217, 0)'},
-  {at: 0.6, is: 'rgb(102, 179, 0)'},
-  {at: 1, is: 'rgb(0, 128, 0)'},
-  {at: 1.5, is: 'rgb(0, 65, 0)'},
-]);
-
-assertNoInterpolation({
-  property: 'caret-color',
-  from: 'initial',
-  to: 'green',
-});
-
-assertNoInterpolation({
-  property: 'caret-color',
-  from: 'auto',
-  to: 'green',
-});
-
-assertInterpolation({
-  property: 'caret-color',
-  from: 'currentColor',
-  to: 'green',
-}, [
-  {at: -0.3, is: 'rgb(0, 0, 0)'},
-  {at: 0, is: 'rgb(0, 0, 0)'},
-  {at: 0.3, is: 'rgb(0, 38, 0)'},
-  {at: 0.6, is: 'rgb(0, 77, 0)'},
-  {at: 1, is: 'rgb(0, 128, 0)'},
-  {at: 1.5, is: 'rgb(0, 192, 0)'},
-]);
-
-assertInterpolation({
-  property: 'caret-color',
-  from: 'inherit',
-  to: 'green',
-}, [
-  {at: -0.3, is: 'rgb(0, 0, 255)'},
-  {at: 0, is: 'rgb(0, 0, 255)'},
-  {at: 0.3, is: 'rgb(0, 38, 179)'},
-  {at: 0.6, is: 'rgb(0, 77, 102)'},
-  {at: 1, is: 'rgb(0, 128, 0)'},
-  {at: 1.5, is: 'rgb(0, 192, 0)'},
-]);
-
-assertInterpolation({
-  property: 'caret-color',
-  from: 'unset',
-  to: 'green',
-}, [
-  {at: -0.3, is: 'rgb(0, 0, 255)'},
-  {at: 0, is: 'rgb(0, 0, 255)'},
-  {at: 0.3, is: 'rgb(0, 38, 179)'},
-  {at: 0.6, is: 'rgb(0, 77, 102)'},
-  {at: 1, is: 'rgb(0, 128, 0)'},
-  {at: 1.5, is: 'rgb(0, 192, 0)'},
-]);
-
-assertInterpolation({
-  property: 'caret-color',
-  from: 'black',
-  to: 'orange',
-}, [
-  {at: -0.3, is: 'rgb(0, 0, 0)'},
-  {at: 0, is: 'rgb(0, 0, 0)'},
-  {at: 0.3, is: 'rgb(77, 50, 0)'},
-  {at: 0.6, is: 'rgb(153, 99, 0)'},
-  {at: 1, is: 'rgb(255, 165, 0)'},
-  {at: 1.5, is: 'rgb(255, 248, 0)'},
-]);
-
+// New tests should be added to external/wpt/css/css-ui/animation/caret-color-interpolation.html,
+// but these legacy tests are kept here as they rely on prefixed values.
 assertInterpolation({
   property: 'caret-color',
   from: '-webkit-activelink',
diff --git a/third_party/blink/web_tests/animations/interpolation/color-interpolation.html b/third_party/blink/web_tests/animations/interpolation/color-interpolation.html
index 55e15e52..5dc5e997 100644
--- a/third_party/blink/web_tests/animations/interpolation/color-interpolation.html
+++ b/third_party/blink/web_tests/animations/interpolation/color-interpolation.html
@@ -17,71 +17,8 @@
 <template id="target-template">T</template>
 <script src="resources/interpolation-test.js"></script>
 <script>
-assertInterpolation({
-  property: 'color',
-  from: neutralKeyframe,
-  to: 'green',
-}, [
-  {at: -0.3, is: 'rgb(255, 255, 0)'},
-  {at: 0, is: 'rgb(255, 255, 0)'},
-  {at: 0.3, is: 'rgb(179, 217, 0)'},
-  {at: 0.6, is: 'rgb(102, 179, 0)'},
-  {at: 1, is: 'rgb(0, 128, 0)'},
-  {at: 1.5, is: 'rgb(0, 65, 0)'},
-]);
-
-assertInterpolation({
-  property: 'color',
-  from: 'initial',
-  to: 'green',
-}, [
-  {at: -0.3, is: 'rgb(0, 0, 0)'},
-  {at: 0, is: 'rgb(0, 0, 0)'},
-  {at: 0.3, is: 'rgb(0, 38, 0)'},
-  {at: 0.6, is: 'rgb(0, 77, 0)'},
-  {at: 1, is: 'rgb(0, 128, 0)'},
-  {at: 1.5, is: 'rgb(0, 192, 0)'},
-]);
-
-assertInterpolation({
-  property: 'color',
-  from: 'inherit',
-  to: 'green',
-}, [
-  {at: -0.3, is: 'rgb(0, 0, 255)'},
-  {at: 0, is: 'rgb(0, 0, 255)'},
-  {at: 0.3, is: 'rgb(0, 38, 179)'},
-  {at: 0.6, is: 'rgb(0, 77, 102)'},
-  {at: 1, is: 'rgb(0, 128, 0)'},
-  {at: 1.5, is: 'rgb(0, 192, 0)'},
-]);
-
-assertInterpolation({
-  property: 'color',
-  from: 'unset',
-  to: 'green',
-}, [
-  {at: -0.3, is: 'rgb(0, 0, 255)'},
-  {at: 0, is: 'rgb(0, 0, 255)'},
-  {at: 0.3, is: 'rgb(0, 38, 179)'},
-  {at: 0.6, is: 'rgb(0, 77, 102)'},
-  {at: 1, is: 'rgb(0, 128, 0)'},
-  {at: 1.5, is: 'rgb(0, 192, 0)'},
-]);
-
-assertInterpolation({
-  property: 'color',
-  from: 'black',
-  to: 'orange',
-}, [
-  {at: -0.3, is: 'rgb(0, 0, 0)'},
-  {at: 0, is: 'rgb(0, 0, 0)'},
-  {at: 0.3, is: 'rgb(77, 50, 0)'},
-  {at: 0.6, is: 'rgb(153, 99, 0)'},
-  {at: 1, is: 'rgb(255, 165, 0)'},
-  {at: 1.5, is: 'rgb(255, 248, 0)'},
-]);
-
+// New tests should be added to external/wpt/css/css-color/animation/color-interpolation.html,
+// but these legacy tests are kept here as they rely on prefixed values.
 assertInterpolation({
   property: 'color',
   from: '-webkit-activelink',
diff --git a/third_party/blink/web_tests/animations/interpolation/flex-basis-interpolation.html b/third_party/blink/web_tests/animations/interpolation/flex-basis-interpolation.html
deleted file mode 100644
index 332c856..0000000
--- a/third_party/blink/web_tests/animations/interpolation/flex-basis-interpolation.html
+++ /dev/null
@@ -1,78 +0,0 @@
-<!DOCTYPE html>
-<meta charset="UTF-8">
-<style>
-.parent {
-  flex-basis: 3%;
-}
-.target {
-  flex-basis: 1%;
-}
-</style>
-<body>
-<script src="resources/interpolation-test.js"></script>
-<script>
-assertInterpolation({
-  property: 'flex-basis',
-  from: neutralKeyframe,
-  to: '2%',
-}, [
-  {at: -0.3, is: '0.7%'},
-  {at: 0, is: '1%'},
-  {at: 0.3, is: '1.3%'},
-  {at: 0.6, is: '1.6%'},
-  {at: 1, is: '2%'},
-  {at: 1.5, is: '2.5%'},
-]);
-
-assertNoInterpolation({
-  property: 'flex-basis',
-  from: 'initial',
-  to: '2%',
-});
-
-assertInterpolation({
-  property: 'flex-basis',
-  from: 'inherit',
-  to: '2%',
-}, [
-  {at: -0.3, is: '3.3%'},
-  {at: 0, is: '3%'},
-  {at: 0.3, is: '2.7%'},
-  {at: 0.6, is: '2.4%'},
-  {at: 1, is: '2%'},
-  {at: 1.5, is: '1.5%'},
-]);
-
-assertNoInterpolation({
-  property: 'flex-basis',
-  from: 'unset',
-  to: '2%',
-});
-
-assertInterpolation({
-  property: 'flex-basis',
-  from: '0px',
-  to: '100px'
-}, [
-  {at: -0.3, is: '0px'},
-  {at: 0, is: '0px'},
-  {at: 0.4, is: '40px'},
-  {at: 0.6, is: '60px'},
-  {at: 1, is: '100px'},
-  {at: 1.5, is: '150px'}
-]);
-
-assertInterpolation({
-  property: 'flex-basis',
-  from: '0%',
-  to: '100%'
-}, [
-  {at: -0.3, is: '0%'},
-  {at: 0, is: '0%'},
-  {at: 0.4, is: '40%'},
-  {at: 0.6, is: '60%'},
-  {at: 1, is: '100%'},
-  {at: 1.5, is: '150%'}
-]);
-</script>
-</body>
diff --git a/third_party/blink/web_tests/animations/interpolation/flex-grow-interpolation.html b/third_party/blink/web_tests/animations/interpolation/flex-grow-interpolation.html
deleted file mode 100644
index 0da58e2..0000000
--- a/third_party/blink/web_tests/animations/interpolation/flex-grow-interpolation.html
+++ /dev/null
@@ -1,110 +0,0 @@
-<!DOCTYPE html>
-<meta charset="UTF-8">
-<style>
-.parent {
-  display: flex;
-  flex-grow: 3;
-}
-.target {
-  height: 10px;
-  background: black;
-  flex-grow: 1;
-}
-.filler {
-  height: 10px;
-  flex-grow: 1;
-}
-.expected {
-  background: green;
-}
-</style>
-<body>
-<template id="target-template">
-  <div class="parent">
-    <div class="target"></div>
-    <div class="filler"></div>
-  </div>
-</template>
-<script src="resources/interpolation-test.js"></script>
-<script>
-assertInterpolation({
-  property: 'flex-grow',
-  from: neutralKeyframe,
-  to: '2',
-}, [
-  {at: -0.3, is: '0.7'},
-  {at: 0, is: '1'},
-  {at: 0.3, is: '1.3'},
-  {at: 0.6, is: '1.6'},
-  {at: 1, is: '2'},
-  {at: 1.5, is: '2.5'},
-]);
-
-assertInterpolation({
-  property: 'flex-grow',
-  from: 'initial',
-  to: '2',
-}, [
-  {at: -0.3, is: '0'},
-  {at: 0, is: '0'},
-  {at: 0.3, is: '0.6'},
-  {at: 0.6, is: '1.2'},
-  {at: 1, is: '2'},
-  {at: 1.5, is: '3'},
-]);
-
-assertInterpolation({
-  property: 'flex-grow',
-  from: 'inherit',
-  to: '2',
-}, [
-  {at: -0.3, is: '3.3'},
-  {at: 0, is: '3'},
-  {at: 0.3, is: '2.7'},
-  {at: 0.6, is: '2.4'},
-  {at: 1, is: '2'},
-  {at: 1.5, is: '1.5'},
-]);
-
-assertInterpolation({
-  property: 'flex-grow',
-  from: 'unset',
-  to: '2',
-}, [
-  {at: -0.3, is: '0'},
-  {at: 0, is: '0'},
-  {at: 0.3, is: '0.6'},
-  {at: 0.6, is: '1.2'},
-  {at: 1, is: '2'},
-  {at: 1.5, is: '3'},
-]);
-
-assertInterpolation({
-  property: 'flex-grow',
-  from: '1',
-  to: '2',
-}, [
-  {at: -5, is: '0'},
-  {at: -0.3, is: '0.7'},
-  {at: 0, is: '1'},
-  {at: 0.3, is: '1.3'},
-  {at: 0.6, is: '1.6'},
-  {at: 1, is: '2'},
-  {at: 1.5, is: '2.5'},
-]);
-
-assertInterpolation({
-  property: 'flex-grow',
-  from: '0',
-  to: '1',
-}, [
-  {at: -5, is: '0'},
-  {at: -0.3, is: '0'},
-  {at: 0, is: '0'},
-  {at: 0.3, is: '0.3'},
-  {at: 0.6, is: '0.6'},
-  {at: 1, is: '1'},
-  {at: 1.5, is: '1.5'},
-]);
-</script>
-</body>
diff --git a/third_party/blink/web_tests/animations/interpolation/flex-shrink-interpolation.html b/third_party/blink/web_tests/animations/interpolation/flex-shrink-interpolation.html
deleted file mode 100644
index a5645827..0000000
--- a/third_party/blink/web_tests/animations/interpolation/flex-shrink-interpolation.html
+++ /dev/null
@@ -1,94 +0,0 @@
-<!DOCTYPE html>
-<meta charset="UTF-8">
-<style>
-.parent {
-  flex-shrink: 3;
-}
-.target {
-  flex-shrink: 1.5;
-}
-</style>
-<body>
-<script src="resources/interpolation-test.js"></script>
-<script>
-assertInterpolation({
-  property: 'flex-shrink',
-  from: neutralKeyframe,
-  to: '2',
-}, [
-  {at: -0.3, is: '1.35'},
-  {at: 0, is: '1.5'},
-  {at: 0.3, is: '1.65'},
-  {at: 0.6, is: '1.8'},
-  {at: 1, is: '2'},
-  {at: 1.5, is: '2.25'},
-]);
-
-assertInterpolation({
-  property: 'flex-shrink',
-  from: 'initial',
-  to: '2',
-}, [
-  {at: -0.3, is: '0.7'},
-  {at: 0, is: '1'},
-  {at: 0.3, is: '1.3'},
-  {at: 0.6, is: '1.6'},
-  {at: 1, is: '2'},
-  {at: 1.5, is: '2.5'},
-]);
-
-assertInterpolation({
-  property: 'flex-shrink',
-  from: 'inherit',
-  to: '2',
-}, [
-  {at: -0.3, is: '3.3'},
-  {at: 0, is: '3'},
-  {at: 0.3, is: '2.7'},
-  {at: 0.6, is: '2.4'},
-  {at: 1, is: '2'},
-  {at: 1.5, is: '1.5'},
-]);
-
-assertInterpolation({
-  property: 'flex-shrink',
-  from: 'unset',
-  to: '2',
-}, [
-  {at: -0.3, is: '0.7'},
-  {at: 0, is: '1'},
-  {at: 0.3, is: '1.3'},
-  {at: 0.6, is: '1.6'},
-  {at: 1, is: '2'},
-  {at: 1.5, is: '2.5'},
-]);
-
-assertInterpolation({
-  property: 'flex-shrink',
-  from: '1',
-  to: '2',
-}, [
-  {at: -5, is: '0'},
-  {at: -0.3, is: '0.7'},
-  {at: 0, is: '1'},
-  {at: 0.3, is: '1.3'},
-  {at: 0.6, is: '1.6'},
-  {at: 1, is: '2'},
-  {at: 1.5, is: '2.5'},
-]);
-
-assertInterpolation({
-  property: 'flex-shrink',
-  from: '0',
-  to: '1',
-}, [
-  {at: -5, is: '0'},
-  {at: -0.3, is: '0'},
-  {at: 0, is: '0'},
-  {at: 0.3, is: '0.3'},
-  {at: 0.6, is: '0.6'},
-  {at: 1, is: '1'},
-  {at: 1.5, is: '1.5'},
-]);
-</script>
-</body>
diff --git a/third_party/blink/web_tests/animations/interpolation/outline-color-interpolation.html b/third_party/blink/web_tests/animations/interpolation/outline-color-interpolation.html
deleted file mode 100644
index a7873a4..0000000
--- a/third_party/blink/web_tests/animations/interpolation/outline-color-interpolation.html
+++ /dev/null
@@ -1,88 +0,0 @@
-<!DOCTYPE html>
-<meta charset="UTF-8">
-<style>
-.parent {
-  outline-color: yellow;
-}
-.target {
-  width: 50px;
-  height: 50px;
-  background-color: black;
-  display: inline-block;
-  outline: 12px solid white;
-  margin: 12px 12px;
-  outline-color: blue;
-}
-.expected {
-  background-color: lime;
-}
-</style>
-<body>
-<script src="resources/interpolation-test.js"></script>
-<script>
-assertInterpolation({
-  property: 'outline-color',
-  from: neutralKeyframe,
-  to: 'green',
-}, [
-  {at: -0.3, is: 'rgb(0, 0, 255)'},
-  {at: 0, is: 'rgb(0, 0, 255)'},
-  {at: 0.3, is: 'rgb(0, 38, 179)'},
-  {at: 0.6, is: 'rgb(0, 77, 102)'},
-  {at: 1, is: 'rgb(0, 128, 0)'},
-  {at: 1.5, is: 'rgb(0, 192, 0)'},
-]);
-
-assertInterpolation({
-  property: 'outline-color',
-  from: 'initial',
-  to: 'green',
-}, [
-  {at: -0.3, is: 'rgb(0, 0, 0)'},
-  {at: 0, is: 'rgb(0, 0, 0)'},
-  {at: 0.3, is: 'rgb(0, 38, 0)'},
-  {at: 0.6, is: 'rgb(0, 77, 0)'},
-  {at: 1, is: 'rgb(0, 128, 0)'},
-  {at: 1.5, is: 'rgb(0, 192, 0)'},
-]);
-
-assertInterpolation({
-  property: 'outline-color',
-  from: 'inherit',
-  to: 'green',
-}, [
-  {at: -0.3, is: 'rgb(255, 255, 0)'},
-  {at: 0, is: 'rgb(255, 255, 0)'},
-  {at: 0.3, is: 'rgb(179, 217, 0)'},
-  {at: 0.6, is: 'rgb(102, 179, 0)'},
-  {at: 1, is: 'rgb(0, 128, 0)'},
-  {at: 1.5, is: 'rgb(0, 65, 0)'},
-]);
-
-assertInterpolation({
-  property: 'outline-color',
-  from: 'unset',
-  to: 'green',
-}, [
-  {at: -0.3, is: 'rgb(0, 0, 0)'},
-  {at: 0, is: 'rgb(0, 0, 0)'},
-  {at: 0.3, is: 'rgb(0, 38, 0)'},
-  {at: 0.6, is: 'rgb(0, 77, 0)'},
-  {at: 1, is: 'rgb(0, 128, 0)'},
-  {at: 1.5, is: 'rgb(0, 192, 0)'},
-]);
-
-assertInterpolation({
-  property: 'outline-color',
-  from: 'white',
-  to: 'orange'
-}, [
-  {at: -0.3, is: 'white'},
-  {at: 0, is: 'white'},
-  {at: 0.3, is: 'rgb(255, 228, 179)'},
-  {at: 0.6, is: 'rgb(255, 201, 102)'},
-  {at: 1, is: 'orange'},
-  {at: 1.5, is: 'rgb(255, 120, 0)'},
-]);
-</script>
-</body>
diff --git a/third_party/blink/web_tests/animations/interpolation/outline-offset-interpolation.html b/third_party/blink/web_tests/animations/interpolation/outline-offset-interpolation.html
deleted file mode 100644
index bc96f7d9..0000000
--- a/third_party/blink/web_tests/animations/interpolation/outline-offset-interpolation.html
+++ /dev/null
@@ -1,90 +0,0 @@
-<!DOCTYPE html>
-<meta charset="UTF-8">
-<style>
-.parent {
-  outline: solid 0px;
-  outline-offset: 30px;
-}
-.target {
-  width: 50px;
-  height: 50px;
-  background-color: black;
-  display: inline-block;
-  margin: 40px 0px 0px 40px;
-  outline: 4px solid orange;
-  outline-offset: 10px;
-}
-.expected {
-  background-color: green;
-  margin-right: 18px;
-}
-</style>
-<body>
-<script src="resources/interpolation-test.js"></script>
-<script>
-assertInterpolation({
-  property: 'outline-offset',
-  from: neutralKeyframe,
-  to: '20px',
-}, [
-  {at: -0.3, is: '7px'},
-  {at: 0, is: '10px'},
-  {at: 0.3, is: '13px'},
-  {at: 0.6, is: '16px'},
-  {at: 1, is: '20px'},
-  {at: 1.5, is: '25px'},
-]);
-
-assertInterpolation({
-  property: 'outline-offset',
-  from: 'initial',
-  to: '20px',
-}, [
-  {at: -0.3, is: '-6px'},
-  {at: 0, is: '0px'},
-  {at: 0.3, is: '6px'},
-  {at: 0.6, is: '12px'},
-  {at: 1, is: '20px'},
-  {at: 1.5, is: '30px'},
-]);
-
-assertInterpolation({
-  property: 'outline-offset',
-  from: 'inherit',
-  to: '20px',
-}, [
-  {at: -0.3, is: '33px'},
-  {at: 0, is: '30px'},
-  {at: 0.3, is: '27px'},
-  {at: 0.6, is: '24px'},
-  {at: 1, is: '20px'},
-  {at: 1.5, is: '15px'},
-]);
-
-assertInterpolation({
-  property: 'outline-offset',
-  from: 'unset',
-  to: '20px',
-}, [
-  {at: -0.3, is: '-6px'},
-  {at: 0, is: '0px'},
-  {at: 0.3, is: '6px'},
-  {at: 0.6, is: '12px'},
-  {at: 1, is: '20px'},
-  {at: 1.5, is: '30px'},
-]);
-
-assertInterpolation({
-  property: 'outline-offset',
-  from: '-5px',
-  to: '5px',
-}, [
-  {at: -0.3, is: '-8px'},
-  {at: 0, is: '-5px'},
-  {at: 0.3, is: '-2px'},
-  {at: 0.6, is: '1px'},
-  {at: 1, is: '5px'},
-  {at: 1.5, is: '10px'},
-]);
-</script>
-</body>
diff --git a/third_party/blink/web_tests/animations/interpolation/outline-width-interpolation.html b/third_party/blink/web_tests/animations/interpolation/outline-width-interpolation.html
deleted file mode 100644
index ce201df..0000000
--- a/third_party/blink/web_tests/animations/interpolation/outline-width-interpolation.html
+++ /dev/null
@@ -1,104 +0,0 @@
-<!DOCTYPE html>
-<meta charset="UTF-8">
-<style>
-.parent {
-  outline: solid transparent;
-  outline-width: 30px;
-}
-.target {
-  width: 50px;
-  height: 50px;
-  background-color: black;
-  display: inline-block;
-  margin: 18px;
-  outline: solid orange;
-  outline-width: 10px;
-  opacity: 0.5;
-}
-.expected {
-  background-color: green;
-}
-</style>
-<body>
-<script src="resources/interpolation-test.js"></script>
-<script>
-assertInterpolation({
-  property: 'outline-width',
-  from: neutralKeyframe,
-  to: '20px',
-}, [
-  {at: -0.3, is: '7px'},
-  {at: 0, is: '10px'},
-  {at: 0.3, is: '13px'},
-  {at: 0.6, is: '16px'},
-  {at: 1, is: '20px'},
-  {at: 1.5, is: '25px'},
-]);
-
-assertInterpolation({
-  property: 'outline-width',
-  from: 'initial',
-  to: '20px',
-}, [
-  {at: -0.3, is: '0px'},
-  {at: 0, is: '3px'},
-  {at: 0.3, is: '8px'},
-  {at: 0.6, is: '13px'},
-  {at: 1, is: '20px'},
-  {at: 1.5, is: '28px'},
-]);
-
-assertInterpolation({
-  property: 'outline-width',
-  from: 'inherit',
-  to: '20px',
-}, [
-  {at: -0.3, is: '33px'},
-  {at: 0, is: '30px'},
-  {at: 0.3, is: '27px'},
-  {at: 0.6, is: '24px'},
-  {at: 1, is: '20px'},
-  {at: 1.5, is: '15px'},
-]);
-
-assertInterpolation({
-  property: 'outline-width',
-  from: 'unset',
-  to: '20px',
-}, [
-  {at: -0.3, is: '0px'},
-  {at: 0, is: '3px'},
-  {at: 0.3, is: '8px'},
-  {at: 0.6, is: '13px'},
-  {at: 1, is: '20px'},
-  {at: 1.5, is: '28px'},
-]);
-
-assertInterpolation({
-  property: 'outline-width',
-  from: '0px',
-  to: '10px',
-}, [
-  {at: -0.3, is: '0px'}, // CSS outline-width can't be negative.
-  {at: 0, is: '0px'},
-  {at: 0.3, is: '3px'},
-  {at: 0.6, is: '6px'},
-  {at: 1, is: '10px'},
-  {at: 1.5, is: '15px'}
-]);
-
-assertInterpolation({
-  property: 'outline-width',
-  from: 'thick',
-  to: '15px',
-}, [
-  {at: -2, is: '0px'}, // CSS outline-width can't be negative.
-  {at: -0.3, is: '2px'},
-  {at: 0, is: '5px'},
-  {at: 0.3, is: '8px'},
-  {at: 0.6, is: '11px'},
-  {at: 1, is: '15px'},
-  {at: 1.5, is: '20px'}
-]);
-</script>
-</body>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index 1952779..5a307939 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -154578,6 +154578,12 @@
    "fetch/api/basic/request-upload.any.worker-expected.txt": [
     []
    ],
+   "fetch/api/basic/scheme-data.any-expected.txt": [
+    []
+   ],
+   "fetch/api/basic/scheme-data.any.worker-expected.txt": [
+    []
+   ],
    "fetch/api/cors/cors-filtering-worker-expected.txt": [
     []
    ],
@@ -159921,6 +159927,12 @@
    "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-worker.js.headers": [
     []
    ],
+   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html": [
+    []
+   ],
+   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html.headers": [
+    []
+   ],
    "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe-domain.sub.html": [
     []
    ],
@@ -160011,6 +160023,12 @@
    "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-success.https.sub.html.headers": [
     []
    ],
+   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https-expected.txt": [
+    []
+   ],
+   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html.headers": [
+    []
+   ],
    "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel-success.https.html.headers": [
     []
    ],
@@ -164634,6 +164652,9 @@
    "media-source/idlharness.any.worker-expected.txt": [
     []
    ],
+   "media-source/idlharness.window-expected.txt": [
+    []
+   ],
    "media-source/import_tests.sh": [
     []
    ],
@@ -177345,6 +177366,9 @@
    "websockets/handlers/basic_auth_wsh.py": [
     []
    ],
+   "websockets/handlers/delayed-passive-close_wsh.py": [
+    []
+   ],
    "websockets/handlers/echo-cookie_wsh.py": [
     []
    ],
@@ -177444,6 +177468,12 @@
    "websockets/security/check.py": [
     []
    ],
+   "websockets/stream-tentative/README.md": [
+    []
+   ],
+   "websockets/stream-tentative/resources/url-constants.js": [
+    []
+   ],
    "websockets/unload-a-document/001-1.html": [
     []
    ],
@@ -205248,6 +205278,12 @@
      {}
     ]
    ],
+   "css/css-color/animation/color-interpolation.html": [
+    [
+     "css/css-color/animation/color-interpolation.html",
+     {}
+    ]
+   ],
    "css/css-color/color-function-parsing.html": [
     [
      "css/css-color/color-function-parsing.html",
@@ -205532,6 +205568,24 @@
      {}
     ]
    ],
+   "css/css-flexbox/animation/flex-basis-interpolation.html": [
+    [
+     "css/css-flexbox/animation/flex-basis-interpolation.html",
+     {}
+    ]
+   ],
+   "css/css-flexbox/animation/flex-grow-interpolation.html": [
+    [
+     "css/css-flexbox/animation/flex-grow-interpolation.html",
+     {}
+    ]
+   ],
+   "css/css-flexbox/animation/flex-shrink-interpolation.html": [
+    [
+     "css/css-flexbox/animation/flex-shrink-interpolation.html",
+     {}
+    ]
+   ],
    "css/css-flexbox/display_flex_exist.html": [
     [
      "css/css-flexbox/display_flex_exist.html",
@@ -208740,6 +208794,30 @@
      {}
     ]
    ],
+   "css/css-grid/parsing/grid-template-columns-invalid.html": [
+    [
+     "css/css-grid/parsing/grid-template-columns-invalid.html",
+     {}
+    ]
+   ],
+   "css/css-grid/parsing/grid-template-columns-valid.html": [
+    [
+     "css/css-grid/parsing/grid-template-columns-valid.html",
+     {}
+    ]
+   ],
+   "css/css-grid/parsing/grid-template-rows-invalid.html": [
+    [
+     "css/css-grid/parsing/grid-template-rows-invalid.html",
+     {}
+    ]
+   ],
+   "css/css-grid/parsing/grid-template-rows-valid.html": [
+    [
+     "css/css-grid/parsing/grid-template-rows-valid.html",
+     {}
+    ]
+   ],
    "css/css-grid/placement/grid-auto-placement-implicit-tracks-001.html": [
     [
      "css/css-grid/placement/grid-auto-placement-implicit-tracks-001.html",
@@ -217644,6 +217722,30 @@
      {}
     ]
    ],
+   "css/css-ui/animation/caret-color-interpolation.html": [
+    [
+     "css/css-ui/animation/caret-color-interpolation.html",
+     {}
+    ]
+   ],
+   "css/css-ui/animation/outline-color-interpolation.html": [
+    [
+     "css/css-ui/animation/outline-color-interpolation.html",
+     {}
+    ]
+   ],
+   "css/css-ui/animation/outline-offset-interpolation.html": [
+    [
+     "css/css-ui/animation/outline-offset-interpolation.html",
+     {}
+    ]
+   ],
+   "css/css-ui/animation/outline-width-interpolation.html": [
+    [
+     "css/css-ui/animation/outline-width-interpolation.html",
+     {}
+    ]
+   ],
    "css/css-ui/appearance-cssom-001.html": [
     [
      "css/css-ui/appearance-cssom-001.html",
@@ -242604,6 +242706,12 @@
      {}
     ]
    ],
+   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html": [
+    [
+     "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html",
+     {}
+    ]
+   ],
    "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel-success.https.html": [
     [
      "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel-success.https.html",
@@ -255573,6 +255681,12 @@
      {}
     ]
    ],
+   "mathml/relations/css-styling/display-contents.html": [
+    [
+     "mathml/relations/css-styling/display-contents.html",
+     {}
+    ]
+   ],
    "mathml/relations/css-styling/displaystyle-1.html": [
     [
      "mathml/relations/css-styling/displaystyle-1.html",
@@ -255840,24 +255954,9 @@
      {}
     ]
    ],
-   "media-source/idlharness.any.js": [
+   "media-source/idlharness.window.js": [
     [
-     "media-source/idlharness.any.html",
-     {
-      "script_metadata": [
-       [
-        "script",
-        "/resources/WebIDLParser.js"
-       ],
-       [
-        "script",
-        "/resources/idlharness.js"
-       ]
-      ]
-     }
-    ],
-    [
-     "media-source/idlharness.any.worker.html",
+     "media-source/idlharness.window.html",
      {
       "script_metadata": [
        [
@@ -305419,6 +305518,162 @@
      {}
     ]
    ],
+   "websockets/stream-tentative/close.any.js": [
+    [
+     "websockets/stream-tentative/close.any.html",
+     {
+      "script_metadata": [
+       [
+        "script",
+        "../websocket.sub.js"
+       ],
+       [
+        "script",
+        "resources/url-constants.js"
+       ],
+       [
+        "global",
+        "window,worker"
+       ]
+      ]
+     }
+    ],
+    [
+     "websockets/stream-tentative/close.any.serviceworker.html",
+     {
+      "script_metadata": [
+       [
+        "script",
+        "../websocket.sub.js"
+       ],
+       [
+        "script",
+        "resources/url-constants.js"
+       ],
+       [
+        "global",
+        "window,worker"
+       ]
+      ]
+     }
+    ],
+    [
+     "websockets/stream-tentative/close.any.sharedworker.html",
+     {
+      "script_metadata": [
+       [
+        "script",
+        "../websocket.sub.js"
+       ],
+       [
+        "script",
+        "resources/url-constants.js"
+       ],
+       [
+        "global",
+        "window,worker"
+       ]
+      ]
+     }
+    ],
+    [
+     "websockets/stream-tentative/close.any.worker.html",
+     {
+      "script_metadata": [
+       [
+        "script",
+        "../websocket.sub.js"
+       ],
+       [
+        "script",
+        "resources/url-constants.js"
+       ],
+       [
+        "global",
+        "window,worker"
+       ]
+      ]
+     }
+    ]
+   ],
+   "websockets/stream-tentative/constructor.any.js": [
+    [
+     "websockets/stream-tentative/constructor.any.html",
+     {
+      "script_metadata": [
+       [
+        "script",
+        "../websocket.sub.js"
+       ],
+       [
+        "script",
+        "resources/url-constants.js"
+       ],
+       [
+        "global",
+        "window,worker"
+       ]
+      ]
+     }
+    ],
+    [
+     "websockets/stream-tentative/constructor.any.serviceworker.html",
+     {
+      "script_metadata": [
+       [
+        "script",
+        "../websocket.sub.js"
+       ],
+       [
+        "script",
+        "resources/url-constants.js"
+       ],
+       [
+        "global",
+        "window,worker"
+       ]
+      ]
+     }
+    ],
+    [
+     "websockets/stream-tentative/constructor.any.sharedworker.html",
+     {
+      "script_metadata": [
+       [
+        "script",
+        "../websocket.sub.js"
+       ],
+       [
+        "script",
+        "resources/url-constants.js"
+       ],
+       [
+        "global",
+        "window,worker"
+       ]
+      ]
+     }
+    ],
+    [
+     "websockets/stream-tentative/constructor.any.worker.html",
+     {
+      "script_metadata": [
+       [
+        "script",
+        "../websocket.sub.js"
+       ],
+       [
+        "script",
+        "resources/url-constants.js"
+       ],
+       [
+        "global",
+        "window,worker"
+       ]
+      ]
+     }
+    ]
+   ],
    "websockets/unload-a-document/001.html": [
     [
      "websockets/unload-a-document/001.html",
@@ -353920,6 +354175,10 @@
    "e282c4fb98224c0827f24ed9ecdfe95abb1ffbcd",
    "support"
   ],
+  "css/css-color/animation/color-interpolation.html": [
+   "4b39fdcf796083ace917f0a021a697c6e4eb7214",
+   "testharness"
+  ],
   "css/css-color/blacktext-ref.html": [
    "ddfa9100cbef6094375df8b40eeb9673e690d4f7",
    "support"
@@ -356992,6 +357251,18 @@
    "f16d29835f7aef92f278fa5bc28317dc913f3544",
    "reftest"
   ],
+  "css/css-flexbox/animation/flex-basis-interpolation.html": [
+   "d66e81d4924f6b419a0e467119e5ce6dd27e2966",
+   "testharness"
+  ],
+  "css/css-flexbox/animation/flex-grow-interpolation.html": [
+   "bab5c8210574ab24ac8d8aaffb9474656f031eeb",
+   "testharness"
+  ],
+  "css/css-flexbox/animation/flex-shrink-interpolation.html": [
+   "da69a06891ee2d16f91db5d839e14aa2d876b604",
+   "testharness"
+  ],
   "css/css-flexbox/anonymous-flex-item-001.html": [
    "3e749d43f76981a5cc0b372c7ba0c364c350b6b2",
    "reftest"
@@ -370972,6 +371243,22 @@
    "9c9bd47ec894ccc0e389287120082fb4bf5c9905",
    "testharness"
   ],
+  "css/css-grid/parsing/grid-template-columns-invalid.html": [
+   "9d7aac48721a763ab778e22523b16b3f99b3f16a",
+   "testharness"
+  ],
+  "css/css-grid/parsing/grid-template-columns-valid.html": [
+   "85ac76a999aab5adf628a8e38f626fcae299d12f",
+   "testharness"
+  ],
+  "css/css-grid/parsing/grid-template-rows-invalid.html": [
+   "8a0fe9ddfa23e12985c99c1fe9196eebfc04c8cc",
+   "testharness"
+  ],
+  "css/css-grid/parsing/grid-template-rows-valid.html": [
+   "ec8d64f79bacc1d7831e415768c2a76bf05607e0",
+   "testharness"
+  ],
   "css/css-grid/placement/grid-auto-placement-implicit-tracks-001.html": [
    "546336a6ce8208d7c30afd66e20fbe33040cbd7c",
    "testharness"
@@ -398156,6 +398443,22 @@
    "5f43f9116bc8073cfadd3bf840519a3ad96c296f",
    "support"
   ],
+  "css/css-ui/animation/caret-color-interpolation.html": [
+   "b3a4e30130843163d76a0a24196c66853bd4160a",
+   "testharness"
+  ],
+  "css/css-ui/animation/outline-color-interpolation.html": [
+   "f49aa79a382c8e5a8f4c9d834f5f12aea551818f",
+   "testharness"
+  ],
+  "css/css-ui/animation/outline-offset-interpolation.html": [
+   "46c1c51c6eefaa490fc9d55e4cadfb0cb7804337",
+   "testharness"
+  ],
+  "css/css-ui/animation/outline-width-interpolation.html": [
+   "c024c7cf6a08e0f6e02ccb451ca04d0b4a8c9251",
+   "testharness"
+  ],
   "css/css-ui/appearance-auto-001.html": [
    "deef1c5d0b52fe7c91a319abbd1f6ccc2eeda08d",
    "reftest"
@@ -425932,10 +426235,18 @@
    "fb1357eaf294b8973f44af30c2fafc40100d8eaf",
    "testharness"
   ],
+  "fetch/api/basic/scheme-data.any-expected.txt": [
+   "012001f6e498a62417525e92ddc0d0698114d020",
+   "support"
+  ],
   "fetch/api/basic/scheme-data.any.js": [
-   "b1d67418120eb685ac2c52aa3c0a7624274b999d",
+   "2ff2545cc6463f855d6f8ad1657407c85343a7e7",
    "testharness"
   ],
+  "fetch/api/basic/scheme-data.any.worker-expected.txt": [
+   "012001f6e498a62417525e92ddc0d0698114d020",
+   "support"
+  ],
   "fetch/api/basic/scheme-others.sub.any.js": [
    "5f9848ff4c297d662a82aa9e847f548371c33d19",
    "testharness"
@@ -437056,6 +437367,14 @@
    "6604450991a122e3e241e40b1b9e0516c525389d",
    "support"
   ],
+  "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html": [
+   "2c33dba79a2fb9e6a81fc9ee5805b378e826f725",
+   "support"
+  ],
+  "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html.headers": [
+   "1528bf05e6368b00600b23c23042bf0d61985917",
+   "support"
+  ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe-domain.sub.html": [
    "a6dd70177584c9115c24beb281e7681110c07624",
    "support"
@@ -437192,6 +437511,18 @@
    "63b60e490f47f4db77d33d7a4ca2f5b9a4181de8",
    "support"
   ],
+  "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https-expected.txt": [
+   "68c6a6464d0a1fe6ec2e9af58ad5b534ead12271",
+   "support"
+  ],
+  "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html": [
+   "203a9f637fe61a2367fd4b58523af601401991be",
+   "testharness"
+  ],
+  "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html.headers": [
+   "63b60e490f47f4db77d33d7a4ca2f5b9a4181de8",
+   "support"
+  ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel-success.https.html": [
    "98145310f610f0ca88af938872e9ea2103de600c",
    "testharness"
@@ -452124,6 +452455,10 @@
    "f32a15ec01ac808d2e32ecb8cb232c12a5e0aa19",
    "reftest"
   ],
+  "mathml/relations/css-styling/display-contents.html": [
+   "aeaa28da908816ad39619d9a47bbb43a3116bbe9",
+   "testharness"
+  ],
   "mathml/relations/css-styling/displaystyle-011-ref.html": [
    "400c46a2456d0e1d4d48860e3ad557fa5d2990ee",
    "support"
@@ -452433,7 +452768,7 @@
    "reftest"
   ],
   "mathml/relations/css-styling/not-participating-to-parent-layout.html": [
-   "89389fc668234cc46bb58e33a099b2c04cc675b5",
+   "769449b97d4983efc19aa9c0999725cb5735c6dc",
    "testharness"
   ],
   "mathml/relations/css-styling/padding-border-margin/border-001.html": [
@@ -452441,7 +452776,7 @@
    "testharness"
   ],
   "mathml/relations/css-styling/padding-border-margin/border-002.html": [
-   "f78bc588c04cc3d7b7939a194b2b577bab8f1ae7",
+   "06a719de64071ce0c9ca388ccf98fc4c6559a983",
    "testharness"
   ],
   "mathml/relations/css-styling/padding-border-margin/margin-001.html": [
@@ -452449,7 +452784,7 @@
    "testharness"
   ],
   "mathml/relations/css-styling/padding-border-margin/margin-002.html": [
-   "69e1fd6b033c1a4be5e8f46bb699d08caf558eca",
+   "01c8f3e45f83e9f9ad5168de98dc60c63e213841",
    "testharness"
   ],
   "mathml/relations/css-styling/padding-border-margin/padding-001.html": [
@@ -452457,7 +452792,7 @@
    "testharness"
   ],
   "mathml/relations/css-styling/padding-border-margin/padding-002.html": [
-   "a2f167a34040e067e3f41db33034ecb891204d58",
+   "565dfc43b566b0a3c019437111ce21db7885c426",
    "testharness"
   ],
   "mathml/relations/css-styling/padding-border-margin/padding-border-margin-001-ref.html": [
@@ -452533,7 +452868,7 @@
    "testharness"
   ],
   "mathml/relations/html5-tree/clipboard-event-handlers.tentative.html": [
-   "d293b1500c77dd289381619d658ad5761316b9cf",
+   "0031004616886bc5933358d005d16d2299d52095",
    "testharness"
   ],
   "mathml/relations/html5-tree/color-attributes-1-ref.html": [
@@ -452545,7 +452880,7 @@
    "reftest"
   ],
   "mathml/relations/html5-tree/css-inline-style-interface.tentative.html": [
-   "708926413f2aff21c4972a3f0ab19c692b81bbc7",
+   "b82f98e28b70cbfbadf7f585c9a2c8a2612ba05f",
    "testharness"
   ],
   "mathml/relations/html5-tree/display-1.html": [
@@ -452581,7 +452916,7 @@
    "testharness"
   ],
   "mathml/relations/html5-tree/html-or-foreign-element-interfaces.tentative.html": [
-   "1fcaf6c40167a5eeb55dcc62de14e50224998994",
+   "bf4b99894da3251a394760f03d7b06b7e52b291f",
    "testharness"
   ],
   "mathml/relations/html5-tree/integration-point-1-ref.html": [
@@ -452613,7 +452948,7 @@
    "testharness"
   ],
   "mathml/relations/html5-tree/math-global-event-handlers.tentative.html": [
-   "92e18639dff0d10949143bba2c7c403e48fdbe1c",
+   "807a29ee315f2a0d4680b693bb9137a6cd50ca1c",
    "testharness"
   ],
   "mathml/relations/html5-tree/required-extensions-2-ref.html": [
@@ -452665,7 +453000,7 @@
    "support"
   ],
   "mathml/support/box-comparison.js": [
-   "c46808f7fee6f72ec9e7ffd4946239d9dcaad25b",
+   "a574b01706b5fecc232d62735ef7e89938594769",
    "support"
   ],
   "mathml/support/feature-detection.js": [
@@ -452860,14 +453195,18 @@
    "219b057a011f0472696e7ecdbca1d390012a4a26",
    "support"
   ],
-  "media-source/idlharness.any.js": [
-   "7992b1171310e7be933507a76923a0727c230b49",
-   "testharness"
-  ],
   "media-source/idlharness.any.worker-expected.txt": [
    "b86bc74b096aa3c76addb00566289250bfacd74d",
    "support"
   ],
+  "media-source/idlharness.window-expected.txt": [
+   "21a2eec7cff59b5ef3acf27c6feb4b7956e063f9",
+   "support"
+  ],
+  "media-source/idlharness.window.js": [
+   "28da56b32e692b0a43f9ab7e49cb395cba91ec6a",
+   "testharness"
+  ],
   "media-source/import_tests.sh": [
    "a87619c024858a8f0f72086d1839e895887ea83d",
    "support"
@@ -466309,7 +466648,7 @@
    "support"
   ],
   "referrer-policy/generic/referrer-policy-test-case.sub.js": [
-   "d1cb469b3dfba788b0042e41427d637dfb14fad4",
+   "d10a03212bd23dd31e568685c68c73067047a3cb",
    "support"
   ],
   "referrer-policy/generic/sandboxed-iframe-with-opaque-origin.html": [
@@ -496580,6 +496919,10 @@
    "7ace8f4edb79e4d7dae94349ff256567a17c219b",
    "support"
   ],
+  "websockets/handlers/delayed-passive-close_wsh.py": [
+   "7d55b88ecc6db7fd9d3fb7a9722e030d926c807d",
+   "support"
+  ],
   "websockets/handlers/echo-cookie_wsh.py": [
    "367d03190cfef898b092663a34891c721216f823",
    "support"
@@ -497048,6 +497391,22 @@
    "f1414376dcf165ab1c7da18a6f5856796dfec2c2",
    "support"
   ],
+  "websockets/stream-tentative/README.md": [
+   "6c5158877417a3271ff0179449eace740cf157a1",
+   "support"
+  ],
+  "websockets/stream-tentative/close.any.js": [
+   "ddcdc2b2c8f62682f395551be1272b624876ce32",
+   "testharness"
+  ],
+  "websockets/stream-tentative/constructor.any.js": [
+   "12a24f25f2cc50c9437c7e59c4997774d023a101",
+   "testharness"
+  ],
+  "websockets/stream-tentative/resources/url-constants.js": [
+   "9bc77aa3264a157f3da9e37504febfe8563d40bb",
+   "support"
+  ],
   "websockets/unload-a-document/001-1.html": [
    "bd0c36686954938a1d2bf70918bc506da431e296",
    "support"
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color/animation/color-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-color/animation/color-interpolation.html
new file mode 100644
index 0000000..4b39fdcf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-color/animation/color-interpolation.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>color interpolation</title>
+<link rel="help" href="https://drafts.csswg.org/css-color/#the-color-property">
+<meta name="assert" content="color supports animation by computed value type">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<style>
+.parent {
+  color: blue;
+}
+.target {
+  display: inline-block;
+  font-size: 60pt;
+  color: yellow;
+}
+.expected {
+  margin-right: 15px;
+}
+</style>
+
+<body>
+  <template id="target-template">T</template>
+</body>
+
+<script>
+test_interpolation({
+  property: 'color',
+  from: neutralKeyframe,
+  to: 'green',
+}, [
+  {at: -0.3, expect: 'rgb(255, 255, 0)'},
+  {at: 0, expect: 'rgb(255, 255, 0)'},
+  {at: 0.3, expect: 'rgb(179, 217, 0)'},
+  {at: 0.6, expect: 'rgb(102, 179, 0)'},
+  {at: 1, expect: 'rgb(0, 128, 0)'},
+  {at: 1.5, expect: 'rgb(0, 65, 0)'},
+]);
+
+test_interpolation({
+  property: 'color',
+  from: 'initial',
+  to: 'green',
+}, [
+  {at: -0.3, expect: 'rgb(0, 0, 0)'},
+  {at: 0, expect: 'rgb(0, 0, 0)'},
+  {at: 0.3, expect: 'rgb(0, 38, 0)'},
+  {at: 0.6, expect: 'rgb(0, 77, 0)'},
+  {at: 1, expect: 'rgb(0, 128, 0)'},
+  {at: 1.5, expect: 'rgb(0, 192, 0)'},
+]);
+
+test_interpolation({
+  property: 'color',
+  from: 'inherit',
+  to: 'green',
+}, [
+  {at: -0.3, expect: 'rgb(0, 0, 255)'},
+  {at: 0, expect: 'rgb(0, 0, 255)'},
+  {at: 0.3, expect: 'rgb(0, 38, 179)'},
+  {at: 0.6, expect: 'rgb(0, 77, 102)'},
+  {at: 1, expect: 'rgb(0, 128, 0)'},
+  {at: 1.5, expect: 'rgb(0, 192, 0)'},
+]);
+
+test_interpolation({
+  property: 'color',
+  from: 'unset',
+  to: 'green',
+}, [
+  {at: -0.3, expect: 'rgb(0, 0, 255)'},
+  {at: 0, expect: 'rgb(0, 0, 255)'},
+  {at: 0.3, expect: 'rgb(0, 38, 179)'},
+  {at: 0.6, expect: 'rgb(0, 77, 102)'},
+  {at: 1, expect: 'rgb(0, 128, 0)'},
+  {at: 1.5, expect: 'rgb(0, 192, 0)'},
+]);
+
+test_interpolation({
+  property: 'color',
+  from: 'black',
+  to: 'orange',
+}, [
+  {at: -0.3, expect: 'rgb(0, 0, 0)'},
+  {at: 0, expect: 'rgb(0, 0, 0)'},
+  {at: 0.3, expect: 'rgb(77, 50, 0)'},
+  {at: 0.6, expect: 'rgb(153, 99, 0)'},
+  {at: 1, expect: 'rgb(255, 165, 0)'},
+  {at: 1.5, expect: 'rgb(255, 248, 0)'},
+]);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/animation/flex-basis-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/animation/flex-basis-interpolation.html
new file mode 100644
index 0000000..d66e81d4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/animation/flex-basis-interpolation.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>flex-basis interpolation</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#flex-basis-property">
+<meta name="assert" content="flex-basis supports animation by computed value type">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<style>
+.parent {
+  flex-basis: 3%;
+}
+.target {
+  flex-basis: 1%;
+}
+</style>
+
+<body></body>
+
+<script>
+test_interpolation({
+  property: 'flex-basis',
+  from: neutralKeyframe,
+  to: '2%',
+}, [
+  {at: -0.3, expect: '0.7%'},
+  {at: 0, expect: '1%'},
+  {at: 0.3, expect: '1.3%'},
+  {at: 0.6, expect: '1.6%'},
+  {at: 1, expect: '2%'},
+  {at: 1.5, expect: '2.5%'},
+]);
+
+test_no_interpolation({
+  property: 'flex-basis',
+  from: 'initial',
+  to: '2%',
+});
+
+test_interpolation({
+  property: 'flex-basis',
+  from: 'inherit',
+  to: '2%',
+}, [
+  {at: -0.3, expect: '3.3%'},
+  {at: 0, expect: '3%'},
+  {at: 0.3, expect: '2.7%'},
+  {at: 0.6, expect: '2.4%'},
+  {at: 1, expect: '2%'},
+  {at: 1.5, expect: '1.5%'},
+]);
+
+test_no_interpolation({
+  property: 'flex-basis',
+  from: 'unset',
+  to: '2%',
+});
+
+test_interpolation({
+  property: 'flex-basis',
+  from: '0px',
+  to: '100px'
+}, [
+  {at: -0.3, expect: '0px'},
+  {at: 0, expect: '0px'},
+  {at: 0.4, expect: '40px'},
+  {at: 0.6, expect: '60px'},
+  {at: 1, expect: '100px'},
+  {at: 1.5, expect: '150px'}
+]);
+
+test_interpolation({
+  property: 'flex-basis',
+  from: '0%',
+  to: '100%'
+}, [
+  {at: -0.3, expect: '0%'},
+  {at: 0, expect: '0%'},
+  {at: 0.4, expect: '40%'},
+  {at: 0.6, expect: '60%'},
+  {at: 1, expect: '100%'},
+  {at: 1.5, expect: '150%'}
+]);
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/animation/flex-grow-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/animation/flex-grow-interpolation.html
new file mode 100644
index 0000000..bab5c821
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/animation/flex-grow-interpolation.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>flex-grow interpolation</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#flex-grow-property">
+<meta name="assert" content="flex-grow supports animation by computed value type">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<style>
+.parent {
+  display: flex;
+  flex-grow: 3;
+}
+.target {
+  height: 10px;
+  background: black;
+  flex-grow: 1;
+}
+.filler {
+  height: 10px;
+  flex-grow: 1;
+}
+.expected {
+  background: green;
+}
+</style>
+
+<body>
+<template id="target-template">
+  <div class="parent">
+    <div class="target"></div>
+    <div class="filler"></div>
+  </div>
+</template>
+</body>
+
+<script>
+test_interpolation({
+  property: 'flex-grow',
+  from: neutralKeyframe,
+  to: '2',
+}, [
+  {at: -0.3, expect: '0.7'},
+  {at: 0, expect: '1'},
+  {at: 0.3, expect: '1.3'},
+  {at: 0.6, expect: '1.6'},
+  {at: 1, expect: '2'},
+  {at: 1.5, expect: '2.5'},
+]);
+
+test_interpolation({
+  property: 'flex-grow',
+  from: 'initial',
+  to: '2',
+}, [
+  {at: -0.3, expect: '0'},
+  {at: 0, expect: '0'},
+  {at: 0.3, expect: '0.6'},
+  {at: 0.6, expect: '1.2'},
+  {at: 1, expect: '2'},
+  {at: 1.5, expect: '3'},
+]);
+
+test_interpolation({
+  property: 'flex-grow',
+  from: 'inherit',
+  to: '2',
+}, [
+  {at: -0.3, expect: '3.3'},
+  {at: 0, expect: '3'},
+  {at: 0.3, expect: '2.7'},
+  {at: 0.6, expect: '2.4'},
+  {at: 1, expect: '2'},
+  {at: 1.5, expect: '1.5'},
+]);
+
+test_interpolation({
+  property: 'flex-grow',
+  from: 'unset',
+  to: '2',
+}, [
+  {at: -0.3, expect: '0'},
+  {at: 0, expect: '0'},
+  {at: 0.3, expect: '0.6'},
+  {at: 0.6, expect: '1.2'},
+  {at: 1, expect: '2'},
+  {at: 1.5, expect: '3'},
+]);
+
+test_interpolation({
+  property: 'flex-grow',
+  from: '1',
+  to: '2',
+}, [
+  {at: -5, expect: '0'},
+  {at: -0.3, expect: '0.7'},
+  {at: 0, expect: '1'},
+  {at: 0.3, expect: '1.3'},
+  {at: 0.6, expect: '1.6'},
+  {at: 1, expect: '2'},
+  {at: 1.5, expect: '2.5'},
+]);
+
+test_interpolation({
+  property: 'flex-grow',
+  from: '0',
+  to: '1',
+}, [
+  {at: -5, expect: '0'},
+  {at: -0.3, expect: '0'},
+  {at: 0, expect: '0'},
+  {at: 0.3, expect: '0.3'},
+  {at: 0.6, expect: '0.6'},
+  {at: 1, expect: '1'},
+  {at: 1.5, expect: '1.5'},
+]);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/animation/flex-shrink-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/animation/flex-shrink-interpolation.html
new file mode 100644
index 0000000..da69a068
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/animation/flex-shrink-interpolation.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>flex-shrink interpolation</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#flex-shrink-property">
+<meta name="assert" content="flex-shrink supports animation as a number">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<style>
+.parent {
+  flex-shrink: 3;
+}
+.target {
+  flex-shrink: 1.5;
+}
+</style>
+
+<body></body>
+
+<script>
+test_interpolation({
+  property: 'flex-shrink',
+  from: neutralKeyframe,
+  to: '2',
+}, [
+  {at: -0.3, expect: '1.35'},
+  {at: 0, expect: '1.5'},
+  {at: 0.3, expect: '1.65'},
+  {at: 0.6, expect: '1.8'},
+  {at: 1, expect: '2'},
+  {at: 1.5, expect: '2.25'},
+]);
+
+test_interpolation({
+  property: 'flex-shrink',
+  from: 'initial',
+  to: '2',
+}, [
+  {at: -0.3, expect: '0.7'},
+  {at: 0, expect: '1'},
+  {at: 0.3, expect: '1.3'},
+  {at: 0.6, expect: '1.6'},
+  {at: 1, expect: '2'},
+  {at: 1.5, expect: '2.5'},
+]);
+
+test_interpolation({
+  property: 'flex-shrink',
+  from: 'inherit',
+  to: '2',
+}, [
+  {at: -0.3, expect: '3.3'},
+  {at: 0, expect: '3'},
+  {at: 0.3, expect: '2.7'},
+  {at: 0.6, expect: '2.4'},
+  {at: 1, expect: '2'},
+  {at: 1.5, expect: '1.5'},
+]);
+
+test_interpolation({
+  property: 'flex-shrink',
+  from: 'unset',
+  to: '2',
+}, [
+  {at: -0.3, expect: '0.7'},
+  {at: 0, expect: '1'},
+  {at: 0.3, expect: '1.3'},
+  {at: 0.6, expect: '1.6'},
+  {at: 1, expect: '2'},
+  {at: 1.5, expect: '2.5'},
+]);
+
+test_interpolation({
+  property: 'flex-shrink',
+  from: '1',
+  to: '2',
+}, [
+  {at: -5, expect: '0'},
+  {at: -0.3, expect: '0.7'},
+  {at: 0, expect: '1'},
+  {at: 0.3, expect: '1.3'},
+  {at: 0.6, expect: '1.6'},
+  {at: 1, expect: '2'},
+  {at: 1.5, expect: '2.5'},
+]);
+
+test_interpolation({
+  property: 'flex-shrink',
+  from: '0',
+  to: '1',
+}, [
+  {at: -5, expect: '0'},
+  {at: -0.3, expect: '0'},
+  {at: 0, expect: '0'},
+  {at: 0.3, expect: '0.3'},
+  {at: 0.6, expect: '0.6'},
+  {at: 1, expect: '1'},
+  {at: 1.5, expect: '1.5'},
+]);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-invalid.html
new file mode 100644
index 0000000..9d7aac48
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-invalid.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: parsing grid-template-columns with invalid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-grid-1/#propdef-grid-template-columns">
+<meta name="assert" content="grid-template-columns supports only the grammar 'none | <track-list> | <auto-track-list>'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_invalid_value("grid-template-columns", '-10px');
+test_invalid_value("grid-template-columns", '-20%');
+test_invalid_value("grid-template-columns", '-5fr');
+test_invalid_value("grid-template-columns", 'minmax(5fr, calc(0.5em + 10px))');
+test_invalid_value("grid-template-columns", 'minmax(-10px, auto)');
+test_invalid_value("grid-template-columns", 'minmax(-20%, max-content)');
+test_invalid_value("grid-template-columns", 'minmax(min-content, -20%)');
+test_invalid_value("grid-template-columns", 'fit-content(-10px)');
+test_invalid_value("grid-template-columns", 'fit-content(-20%)');
+test_invalid_value("grid-template-columns", '[one] 10px [two three] repeat(20%) [four five six] 3fr [seven]');
+test_invalid_value("grid-template-columns", '[one]');
+test_invalid_value("grid-template-columns", '[one] 10px [two] [three]');
+test_invalid_value("grid-template-columns", 'repeat(auto-fill, -10px)');
+test_invalid_value("grid-template-columns", 'repeat(auto-fill, 10px) repeat(auto-fit, 20%)');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-valid.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-valid.html
new file mode 100644
index 0000000..85ac76a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-valid.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: parsing grid-template-columns with valid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-grid-1/#propdef-grid-template-columns">
+<meta name="assert" content="grid-template-columns supports the full grammar 'none | <track-list> | <auto-track-list>'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_valid_value("grid-template-columns", "none");
+
+// track-size <fixed-breadth> = <length-percentage> | <flex> | min-content | max-content | auto
+test_valid_value("grid-template-columns", '10px');
+test_valid_value("grid-template-columns", '20%');
+test_valid_value("grid-template-columns", 'calc(-0.5em + 10px)');
+test_valid_value("grid-template-columns", 'calc(0.5em + 10px)');
+test_valid_value("grid-template-columns", 'calc(30% + 40vw)');
+test_valid_value("grid-template-columns", '5fr');
+test_valid_value("grid-template-columns", 'min-content');
+test_valid_value("grid-template-columns", 'max-content');
+test_valid_value("grid-template-columns", 'auto');
+
+// track-size minmax( <inflexible-breadth> , <track-breadth> )
+test_valid_value("grid-template-columns", 'minmax(10px, auto)');
+test_valid_value("grid-template-columns", 'minmax(20%, max-content)');
+test_valid_value("grid-template-columns", 'minmax(calc(-0.5em + 10px), min-content)');
+test_valid_value("grid-template-columns", 'minmax(calc(0.5em + 10px), 5fr)');
+test_valid_value("grid-template-columns", 'minmax(calc(30% + 40vw), 10px)');
+test_valid_value("grid-template-columns", 'minmax(min-content, 20%)');
+test_valid_value("grid-template-columns", 'minmax(max-content, calc(-0.5em + 10px))');
+test_valid_value("grid-template-columns", 'minmax(auto, calc(0.5em + 10px))');
+
+// track-size fit-content( <length-percentage> )
+test_valid_value("grid-template-columns", 'fit-content(10px)');
+test_valid_value("grid-template-columns", 'fit-content(20%)');
+test_valid_value("grid-template-columns", 'fit-content(calc(-0.5em + 10px))');
+test_valid_value("grid-template-columns", 'fit-content(calc(0.5em + 10px))');
+test_valid_value("grid-template-columns", 'fit-content(calc(30% + 40vw))');
+
+// <track-repeat> = repeat( [ <positive-integer> ] , [ <line-names>? <track-size> ]+ <line-names>? )
+
+// 'repeat(1, [] 10px)' in Blink
+// 'repeat(1, 10px)' in Firefox
+// '[] 10px' in Safari
+// '10px' in Edge 18
+test_valid_value("grid-template-columns", 'repeat(1, [] 10px)', ['repeat(1, 10px)', 'repeat(1, [] 10px)']);
+
+// 'repeat(1, [one two] 20%)' in Blink, Firefox
+// '[one two] 20%' in Safari, Edge 18
+test_valid_value("grid-template-columns", 'repeat(1, [one two] 20%)');
+
+// 'repeat(2, minmax(10px, auto))' in Blink, Firefox, Edge 18
+// 'minmax(10px, auto) minmax(10px, auto)' in Safari
+test_valid_value("grid-template-columns", 'repeat(2, minmax(10px, auto))');
+
+test_valid_value("grid-template-columns", 'repeat(2, fit-content(20%) [three four] 30px 40px [five six])');
+
+// <track-list> = [ <line-names>? [ <track-size> | <track-repeat> ] ]+ <line-names>?
+test_valid_value("grid-template-columns", 'min-content repeat(5, minmax(10px, auto))');
+
+// <auto-repeat> = repeat( [ auto-fill | auto-fit ] , [ <line-names>? <fixed-size> ]+ <line-names>? )
+test_valid_value("grid-template-columns", 'repeat(auto-fill, 10px)');
+test_valid_value("grid-template-columns", 'repeat(auto-fit, [one] 20%)');
+test_valid_value("grid-template-columns", 'repeat(auto-fill, minmax(30px, 5fr) [two])');
+test_valid_value("grid-template-columns", 'repeat(auto-fit, [three] minmax(max-content, 6em) [four])');
+
+// <auto-track-list> =
+// [ <line-names>? [ <fixed-size> | <fixed-repeat> ] ]* <line-names>?
+// <auto-repeat>
+// [ <line-names>? [ <fixed-size> | <fixed-repeat> ] ]* <line-names>?
+
+test_valid_value("grid-template-columns", '[one] repeat(2, minmax(10px, auto)) [two] 30px [three] repeat(auto-fill, 10px) 40px [four five] repeat(2, minmax(10px, auto)) [six]');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-invalid.html
new file mode 100644
index 0000000..8a0fe9d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-invalid.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: parsing grid-template-rows with invalid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-grid-1/#propdef-grid-template-rows">
+<meta name="assert" content="grid-template-rows supports only the grammar 'none | <track-list> | <auto-track-list>'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_invalid_value("grid-template-rows", '-10px');
+test_invalid_value("grid-template-rows", '-20%');
+test_invalid_value("grid-template-rows", '-5fr');
+test_invalid_value("grid-template-rows", 'minmax(5fr, calc(0.5em + 10px))');
+test_invalid_value("grid-template-rows", 'minmax(-10px, auto)');
+test_invalid_value("grid-template-rows", 'minmax(-20%, max-content)');
+test_invalid_value("grid-template-rows", 'minmax(min-content, -20%)');
+test_invalid_value("grid-template-rows", 'fit-content(-10px)');
+test_invalid_value("grid-template-rows", 'fit-content(-20%)');
+test_invalid_value("grid-template-rows", '[one] 10px [two three] repeat(20%) [four five six] 3fr [seven]');
+test_invalid_value("grid-template-rows", '[one]');
+test_invalid_value("grid-template-rows", '[one] 10px [two] [three]');
+test_invalid_value("grid-template-rows", 'repeat(auto-fill, -10px)');
+test_invalid_value("grid-template-rows", 'repeat(auto-fill, 10px) repeat(auto-fit, 20%)');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-valid.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-valid.html
new file mode 100644
index 0000000..ec8d64f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-valid.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: parsing grid-template-rows with valid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-grid-1/#propdef-grid-template-rows">
+<meta name="assert" content="grid-template-rows supports the full grammar 'none | <track-list> | <auto-track-list>'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_valid_value("grid-template-rows", "none");
+
+// track-size <fixed-breadth> = <length-percentage> | <flex> | min-content | max-content | auto
+test_valid_value("grid-template-rows", '10px');
+test_valid_value("grid-template-rows", '20%');
+test_valid_value("grid-template-rows", 'calc(-0.5em + 10px)');
+test_valid_value("grid-template-rows", 'calc(0.5em + 10px)');
+test_valid_value("grid-template-rows", 'calc(30% + 40vw)');
+test_valid_value("grid-template-rows", '5fr');
+test_valid_value("grid-template-rows", 'min-content');
+test_valid_value("grid-template-rows", 'max-content');
+test_valid_value("grid-template-rows", 'auto');
+
+// track-size minmax( <inflexible-breadth> , <track-breadth> )
+test_valid_value("grid-template-rows", 'minmax(10px, auto)');
+test_valid_value("grid-template-rows", 'minmax(20%, max-content)');
+test_valid_value("grid-template-rows", 'minmax(calc(-0.5em + 10px), min-content)');
+test_valid_value("grid-template-rows", 'minmax(calc(0.5em + 10px), 5fr)');
+test_valid_value("grid-template-rows", 'minmax(calc(30% + 40vw), 10px)');
+test_valid_value("grid-template-rows", 'minmax(min-content, 20%)');
+test_valid_value("grid-template-rows", 'minmax(max-content, calc(-0.5em + 10px))');
+test_valid_value("grid-template-rows", 'minmax(auto, calc(0.5em + 10px))');
+
+// track-size fit-content( <length-percentage> )
+test_valid_value("grid-template-rows", 'fit-content(10px)');
+test_valid_value("grid-template-rows", 'fit-content(20%)');
+test_valid_value("grid-template-rows", 'fit-content(calc(-0.5em + 10px))');
+test_valid_value("grid-template-rows", 'fit-content(calc(0.5em + 10px))');
+test_valid_value("grid-template-rows", 'fit-content(calc(30% + 40vw))');
+
+// <track-repeat> = repeat( [ <positive-integer> ] , [ <line-names>? <track-size> ]+ <line-names>? )
+
+// 'repeat(1, [] 10px)' in Blink
+// 'repeat(1, 10px)' in Firefox
+// '[] 10px' in Safari
+// '10px' in Edge 18
+test_valid_value("grid-template-rows", 'repeat(1, [] 10px)', ['repeat(1, 10px)', 'repeat(1, [] 10px)']);
+
+// 'repeat(1, [one two] 20%)' in Blink, Firefox
+// '[one two] 20%' in Safari, Edge 18
+test_valid_value("grid-template-rows", 'repeat(1, [one two] 20%)');
+
+// 'repeat(2, minmax(10px, auto))' in Blink, Firefox, Edge 18
+// 'minmax(10px, auto) minmax(10px, auto)' in Safari
+test_valid_value("grid-template-rows", 'repeat(2, minmax(10px, auto))');
+
+test_valid_value("grid-template-rows", 'repeat(2, fit-content(20%) [three four] 30px 40px [five six])');
+
+// <track-list> = [ <line-names>? [ <track-size> | <track-repeat> ] ]+ <line-names>?
+test_valid_value("grid-template-rows", 'min-content repeat(5, minmax(10px, auto))');
+
+// <auto-repeat> = repeat( [ auto-fill | auto-fit ] , [ <line-names>? <fixed-size> ]+ <line-names>? )
+test_valid_value("grid-template-rows", 'repeat(auto-fill, 10px)');
+test_valid_value("grid-template-rows", 'repeat(auto-fit, [one] 20%)');
+test_valid_value("grid-template-rows", 'repeat(auto-fill, minmax(30px, 5fr) [two])');
+test_valid_value("grid-template-rows", 'repeat(auto-fit, [three] minmax(max-content, 6em) [four])');
+
+// <auto-track-list> =
+// [ <line-names>? [ <fixed-size> | <fixed-repeat> ] ]* <line-names>?
+// <auto-repeat>
+// [ <line-names>? [ <fixed-size> | <fixed-repeat> ] ]* <line-names>?
+
+test_valid_value("grid-template-rows", '[one] repeat(2, minmax(10px, auto)) [two] 30px [three] repeat(auto-fill, 10px) 40px [four five] repeat(2, minmax(10px, auto)) [six]');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-012.html b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-012.html
new file mode 100644
index 0000000..f012ff5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-012.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<link rel="help" href="https://crbug.com/996085">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>test(()=>{})</script>
+<style>
+  #container {
+    width: 600px;
+    position: relative;
+  }
+  #target {
+    position: absolute;
+    left: 0px;
+    right: 33554000px;
+    width: 10px;
+    margin-left: 33554432px;
+    margin-right: 33554432px;
+    height: 20px;
+  }
+</style>
+<body>
+  <div id="container">
+    <div id="target"></div>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/animation/caret-color-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-ui/animation/caret-color-interpolation.html
new file mode 100644
index 0000000..b3a4e30
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/animation/caret-color-interpolation.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>caret-color interpolation</title>
+<link rel="help" href="https://drafts.csswg.org/css-ui-3/#propdef-caret-color">
+<meta name="assert" content="caret-color supports animation by computed value">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<style>
+.parent {
+  caret-color: blue;
+}
+.target {
+  display: inline-block;
+  font-size: 60pt;
+  caret-color: yellow;
+}
+.expected {
+  margin-right: 15px;
+}
+</style>
+
+<body contenteditable>
+  <template id="target-template">T</template>
+</body>
+
+<script>
+test_interpolation({
+  property: 'caret-color',
+  from: neutralKeyframe,
+  to: 'green',
+}, [
+  {at: -0.3, expect: 'rgb(255, 255, 0)'},
+  {at: 0, expect: 'rgb(255, 255, 0)'},
+  {at: 0.3, expect: 'rgb(179, 217, 0)'},
+  {at: 0.6, expect: 'rgb(102, 179, 0)'},
+  {at: 1, expect: 'rgb(0, 128, 0)'},
+  {at: 1.5, expect: 'rgb(0, 65, 0)'},
+]);
+
+test_no_interpolation({
+  property: 'caret-color',
+  from: 'initial',
+  to: 'green',
+});
+
+test_no_interpolation({
+  property: 'caret-color',
+  from: 'auto',
+  to: 'green',
+});
+
+test_interpolation({
+  property: 'caret-color',
+  from: 'currentColor',
+  to: 'green',
+}, [
+  {at: -0.3, expect: 'rgb(0, 0, 0)'},
+  {at: 0, expect: 'rgb(0, 0, 0)'},
+  {at: 0.3, expect: 'rgb(0, 38, 0)'},
+  {at: 0.6, expect: 'rgb(0, 77, 0)'},
+  {at: 1, expect: 'rgb(0, 128, 0)'},
+  {at: 1.5, expect: 'rgb(0, 192, 0)'},
+]);
+
+test_interpolation({
+  property: 'caret-color',
+  from: 'inherit',
+  to: 'green',
+}, [
+  {at: -0.3, expect: 'rgb(0, 0, 255)'},
+  {at: 0, expect: 'rgb(0, 0, 255)'},
+  {at: 0.3, expect: 'rgb(0, 38, 179)'},
+  {at: 0.6, expect: 'rgb(0, 77, 102)'},
+  {at: 1, expect: 'rgb(0, 128, 0)'},
+  {at: 1.5, expect: 'rgb(0, 192, 0)'},
+]);
+
+test_interpolation({
+  property: 'caret-color',
+  from: 'unset',
+  to: 'green',
+}, [
+  {at: -0.3, expect: 'rgb(0, 0, 255)'},
+  {at: 0, expect: 'rgb(0, 0, 255)'},
+  {at: 0.3, expect: 'rgb(0, 38, 179)'},
+  {at: 0.6, expect: 'rgb(0, 77, 102)'},
+  {at: 1, expect: 'rgb(0, 128, 0)'},
+  {at: 1.5, expect: 'rgb(0, 192, 0)'},
+]);
+
+test_interpolation({
+  property: 'caret-color',
+  from: 'black',
+  to: 'orange',
+}, [
+  {at: -0.3, expect: 'rgb(0, 0, 0)'},
+  {at: 0, expect: 'rgb(0, 0, 0)'},
+  {at: 0.3, expect: 'rgb(77, 50, 0)'},
+  {at: 0.6, expect: 'rgb(153, 99, 0)'},
+  {at: 1, expect: 'rgb(255, 165, 0)'},
+  {at: 1.5, expect: 'rgb(255, 248, 0)'},
+]);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/animation/outline-color-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-ui/animation/outline-color-interpolation.html
new file mode 100644
index 0000000..f49aa79
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/animation/outline-color-interpolation.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>outline-color interpolation</title>
+<link rel="help" href="https://drafts.csswg.org/css-ui-3/#outline-color">
+<meta name="assert" content="outline-color supports animation by computed value">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<style>
+.parent {
+  outline-color: yellow;
+}
+.target {
+  width: 50px;
+  height: 50px;
+  background-color: black;
+  display: inline-block;
+  outline: 12px solid white;
+  margin: 12px 12px;
+  outline-color: blue;
+}
+.expected {
+  background-color: lime;
+}
+</style>
+
+<body></body>
+
+<script>
+test_interpolation({
+  property: 'outline-color',
+  from: neutralKeyframe,
+  to: 'green',
+}, [
+  {at: -0.3, expect: 'rgb(0, 0, 255)'},
+  {at: 0, expect: 'rgb(0, 0, 255)'},
+  {at: 0.3, expect: 'rgb(0, 38, 179)'},
+  {at: 0.6, expect: 'rgb(0, 77, 102)'},
+  {at: 1, expect: 'rgb(0, 128, 0)'},
+  {at: 1.5, expect: 'rgb(0, 192, 0)'},
+]);
+
+test_interpolation({
+  property: 'outline-color',
+  from: 'initial',
+  to: 'green',
+}, [
+  {at: -0.3, expect: 'rgb(0, 0, 0)'},
+  {at: 0, expect: 'rgb(0, 0, 0)'},
+  {at: 0.3, expect: 'rgb(0, 38, 0)'},
+  {at: 0.6, expect: 'rgb(0, 77, 0)'},
+  {at: 1, expect: 'rgb(0, 128, 0)'},
+  {at: 1.5, expect: 'rgb(0, 192, 0)'},
+]);
+
+test_interpolation({
+  property: 'outline-color',
+  from: 'inherit',
+  to: 'green',
+}, [
+  {at: -0.3, expect: 'rgb(255, 255, 0)'},
+  {at: 0, expect: 'rgb(255, 255, 0)'},
+  {at: 0.3, expect: 'rgb(179, 217, 0)'},
+  {at: 0.6, expect: 'rgb(102, 179, 0)'},
+  {at: 1, expect: 'rgb(0, 128, 0)'},
+  {at: 1.5, expect: 'rgb(0, 65, 0)'},
+]);
+
+test_interpolation({
+  property: 'outline-color',
+  from: 'unset',
+  to: 'green',
+}, [
+  {at: -0.3, expect: 'rgb(0, 0, 0)'},
+  {at: 0, expect: 'rgb(0, 0, 0)'},
+  {at: 0.3, expect: 'rgb(0, 38, 0)'},
+  {at: 0.6, expect: 'rgb(0, 77, 0)'},
+  {at: 1, expect: 'rgb(0, 128, 0)'},
+  {at: 1.5, expect: 'rgb(0, 192, 0)'},
+]);
+
+test_interpolation({
+  property: 'outline-color',
+  from: 'white',
+  to: 'orange'
+}, [
+  {at: -0.3, expect: 'white'},
+  {at: 0, expect: 'white'},
+  {at: 0.3, expect: 'rgb(255, 228, 179)'},
+  {at: 0.6, expect: 'rgb(255, 201, 102)'},
+  {at: 1, expect: 'orange'},
+  {at: 1.5, expect: 'rgb(255, 120, 0)'},
+]);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/animation/outline-offset-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-ui/animation/outline-offset-interpolation.html
new file mode 100644
index 0000000..46c1c51
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/animation/outline-offset-interpolation.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>outline-offset interpolation</title>
+<link rel="help" href="https://drafts.csswg.org/css-ui-3/#outline-offset">
+<meta name="assert" content="outline-offset supports animation by computed value">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<style>
+.parent {
+  outline: solid 0px;
+  outline-offset: 30px;
+}
+.target {
+  width: 50px;
+  height: 50px;
+  background-color: black;
+  display: inline-block;
+  margin: 40px 0px 0px 40px;
+  outline: 4px solid orange;
+  outline-offset: 10px;
+}
+.expected {
+  background-color: green;
+  margin-right: 18px;
+}
+</style>
+
+<body></body>
+
+<script>
+test_interpolation({
+  property: 'outline-offset',
+  from: neutralKeyframe,
+  to: '20px',
+}, [
+  {at: -0.3, expect: '7px'},
+  {at: 0, expect: '10px'},
+  {at: 0.3, expect: '13px'},
+  {at: 0.6, expect: '16px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '25px'},
+]);
+
+test_interpolation({
+  property: 'outline-offset',
+  from: 'initial',
+  to: '20px',
+}, [
+  {at: -0.3, expect: '-6px'},
+  {at: 0, expect: '0px'},
+  {at: 0.3, expect: '6px'},
+  {at: 0.6, expect: '12px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '30px'},
+]);
+
+test_interpolation({
+  property: 'outline-offset',
+  from: 'inherit',
+  to: '20px',
+}, [
+  {at: -0.3, expect: '33px'},
+  {at: 0, expect: '30px'},
+  {at: 0.3, expect: '27px'},
+  {at: 0.6, expect: '24px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '15px'},
+]);
+
+test_interpolation({
+  property: 'outline-offset',
+  from: 'unset',
+  to: '20px',
+}, [
+  {at: -0.3, expect: '-6px'},
+  {at: 0, expect: '0px'},
+  {at: 0.3, expect: '6px'},
+  {at: 0.6, expect: '12px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '30px'},
+]);
+
+test_interpolation({
+  property: 'outline-offset',
+  from: '-5px',
+  to: '5px',
+}, [
+  {at: -0.3, expect: '-8px'},
+  {at: 0, expect: '-5px'},
+  {at: 0.3, expect: '-2px'},
+  {at: 0.6, expect: '1px'},
+  {at: 1, expect: '5px'},
+  {at: 1.5, expect: '10px'},
+]);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/animation/outline-width-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-ui/animation/outline-width-interpolation.html
new file mode 100644
index 0000000..c024c7c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/animation/outline-width-interpolation.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>outline-width interpolation</title>
+<link rel="help" href="https://drafts.csswg.org/css-ui-3/#outline-width">
+<meta name="assert" content="outline-width supports animation by computed value">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<style>
+.parent {
+  outline: solid transparent;
+  outline-width: 30px;
+}
+.target {
+  width: 50px;
+  height: 50px;
+  background-color: black;
+  display: inline-block;
+  margin: 18px;
+  outline: solid orange;
+  outline-width: 10px;
+  opacity: 0.5;
+}
+.expected {
+  background-color: green;
+}
+</style>
+
+<body></body>
+
+<script>
+// NOTE: The below tests make assumptions about the values of medium (for unset
+// and initial) and thick, namely that:
+//   * medium=3px
+//   * thick=3px
+//
+// A better version of these tests would dynamically generate the expected values
+// by querying the computed style from the UA.
+
+test_interpolation({
+  property: 'outline-width',
+  from: neutralKeyframe,
+  to: '20px',
+}, [
+  {at: -0.3, expect: '7px'},
+  {at: 0, expect: '10px'},
+  {at: 0.3, expect: '13px'},
+  {at: 0.6, expect: '16px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '25px'},
+]);
+
+test_interpolation({
+  property: 'outline-width',
+  from: 'initial',
+  to: '20px',
+}, [
+  {at: -0.3, expect: '0px'},
+  {at: 0, expect: '3px'},
+  {at: 0.3, expect: '8px'},
+  {at: 0.6, expect: '13px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '28px'},
+]);
+
+test_interpolation({
+  property: 'outline-width',
+  from: 'inherit',
+  to: '20px',
+}, [
+  {at: -0.3, expect: '33px'},
+  {at: 0, expect: '30px'},
+  {at: 0.3, expect: '27px'},
+  {at: 0.6, expect: '24px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '15px'},
+]);
+
+test_interpolation({
+  property: 'outline-width',
+  from: 'unset',
+  to: '20px',
+}, [
+  {at: -0.3, expect: '0px'},
+  {at: 0, expect: '3px'},
+  {at: 0.3, expect: '8px'},
+  {at: 0.6, expect: '13px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '28px'},
+]);
+
+test_interpolation({
+  property: 'outline-width',
+  from: '0px',
+  to: '10px',
+}, [
+  {at: -0.3, expect: '0px'}, // CSS outline-width can't be negative.
+  {at: 0, expect: '0px'},
+  {at: 0.3, expect: '3px'},
+  {at: 0.6, expect: '6px'},
+  {at: 1, expect: '10px'},
+  {at: 1.5, expect: '15px'}
+]);
+
+test_interpolation({
+  property: 'outline-width',
+  from: 'thick',
+  to: '15px',
+}, [
+  {at: -2, expect: '0px'}, // CSS outline-width can't be negative.
+  {at: -0.3, expect: '2px'},
+  {at: 0, expect: '5px'},
+  {at: 0.3, expect: '8px'},
+  {at: 0.6, expect: '11px'},
+  {at: 1, expect: '15px'},
+  {at: 1.5, expect: '20px'}
+]);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/fetch/api/basic/scheme-data.any-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/api/basic/scheme-data.any-expected.txt
new file mode 100644
index 0000000..012001f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/api/basic/scheme-data.any-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+PASS Fetching data:,response%27s%20body is OK
+PASS Fetching data:,response%27s%20body is OK (same-origin)
+PASS Fetching data:,response%27s%20body is OK (cors)
+PASS Fetching data:text/plain;base64,cmVzcG9uc2UncyBib[...] is OK
+PASS Fetching data:image/png;base64,cmVzcG9uc2UncyBib2[...] is OK
+PASS Fetching [POST] data:,response%27s%20body is OK
+FAIL Fetching [HEAD] data:,response%27s%20body is OK assert_equals: Response's body is correct expected "" but got "response's body"
+PASS Fetching [GET] data:notAdataUrl.com is KO
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fetch/api/basic/scheme-data.any.js b/third_party/blink/web_tests/external/wpt/fetch/api/basic/scheme-data.any.js
index b1d6741..2ff2545 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/api/basic/scheme-data.any.js
+++ b/third_party/blink/web_tests/external/wpt/fetch/api/basic/scheme-data.any.js
@@ -29,7 +29,7 @@
                    "response's body",
                    "image/png");
 checkFetchResponse("data:,response%27s%20body", "response's body", "text/plain;charset=US-ASCII", null, "POST");
-checkFetchResponse("data:,response%27s%20body", "response's body", "text/plain;charset=US-ASCII", null, "HEAD");
+checkFetchResponse("data:,response%27s%20body", "", "text/plain;charset=US-ASCII", null, "HEAD");
 
 function checkKoUrl(url, method, desc) {
   var cut = (url.length >= 40) ? "[...]" : "";
diff --git a/third_party/blink/web_tests/external/wpt/fetch/api/basic/scheme-data.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/api/basic/scheme-data.any.worker-expected.txt
new file mode 100644
index 0000000..012001f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/api/basic/scheme-data.any.worker-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+PASS Fetching data:,response%27s%20body is OK
+PASS Fetching data:,response%27s%20body is OK (same-origin)
+PASS Fetching data:,response%27s%20body is OK (cors)
+PASS Fetching data:text/plain;base64,cmVzcG9uc2UncyBib[...] is OK
+PASS Fetching data:image/png;base64,cmVzcG9uc2UncyBib2[...] is OK
+PASS Fetching [POST] data:,response%27s%20body is OK
+FAIL Fetching [HEAD] data:,response%27s%20body is OK assert_equals: Response's body is correct expected "" but got "response's body"
+PASS Fetching [GET] data:notAdataUrl.com is KO
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html
new file mode 100644
index 0000000..2c33dba
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html
@@ -0,0 +1,3 @@
+<script>
+parent.postMessage(new SharedArrayBuffer(10), "*");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html.headers b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html.headers
new file mode 100644
index 0000000..1528bf0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: cross-site
diff --git a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https-expected.txt b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https-expected.txt
new file mode 100644
index 0000000..68c6a646
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL SharedArrayBuffer and a cross-site <iframe> assert_unreached: Got a message event, expected a messageerror event Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html
new file mode 100644
index 0000000..203a9f6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<div id=log></div>
+<script>
+async_test(t => {
+  const frame = document.createElement("iframe");
+  t.add_cleanup(() => frame.remove());
+  frame.src = get_host_info().HTTPS_NOTSAMESITE_ORIGIN + new URL("resources/iframe-failure.html", location).pathname;
+  window.onmessage = t.unreached_func("Got a message event, expected a messageerror event");
+  window.onmessageerror = t.step_func_done();
+  document.body.append(frame);
+}, "SharedArrayBuffer and a cross-site <iframe>");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html.headers b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html.headers
new file mode 100644
index 0000000..63b60e4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/display-contents.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/display-contents.html
new file mode 100644
index 0000000..aeaa28d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/display-contents.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>display: contents</title>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<meta name="assert" content="Verify that display: contents computes to display: none">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/mathml-fragments.js"></script>
+<script>
+  setup({ explicit_done: true });
+  window.addEventListener("load", runTests);
+  function runTests() {
+      var container = document.getElementById("container");
+      for (tag in MathMLFragments) {
+          container.insertAdjacentHTML("beforeend", `<math>${MathMLFragments[tag]}</math>`);
+      }
+      test(function() {
+          Array.from(document.getElementsByClassName("element")).forEach(element => {
+              var style = window.getComputedStyle(element);
+              element.setAttribute("style", "display: contents");
+              assert_equals(style.getPropertyValue("display"), "none", `${tag}`);
+          });
+      }, "display: contents computes to display: none");
+      done();
+  }
+</script>
+</head>
+<body>
+  <div id="log"></div>
+  <div id="container">
+    <math class="element"></math>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/not-participating-to-parent-layout.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/not-participating-to-parent-layout.html
index 89389fc..769449b 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/not-participating-to-parent-layout.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/not-participating-to-parent-layout.html
@@ -24,6 +24,7 @@
             continue;
         // TODO: Add floats too?
         ["display: none",
+         "display: contents",
          "position: absolute",
          "position: fixed"
         ].forEach(style => {
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/border-002.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/border-002.html
index f78bc58..06a719de 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/border-002.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/border-002.html
@@ -28,6 +28,7 @@
                 var s = compareSizeWithAndWithoutStyle(tag, style);
                 assert_approx_equals(s.element_width_delta, 30 + 40, epsilon, "left/right border");
                 assert_approx_equals(s.element_height_delta, 50 + 60, epsilon, "top/bottom border");
+                assert_approx_equals(s.preferred_width_delta, 30 + 40, epsilon, "preferred width");
             }, `Border properties on ${tag}`);
             continue;
         }
@@ -40,6 +41,7 @@
             assert_approx_equals(s.bottom_delta, 60, epsilon, "bottom border");
             assert_approx_equals(s.element_width_delta, 30 + 40, epsilon, "element width");
             assert_approx_equals(s.element_height_delta, 50 + 60, epsilon, "element height");
+            assert_approx_equals(s.preferred_width_delta, 30 + 40, epsilon, "element preferred width");
         }, `Border properties on ${tag}`);
 
         test(function() {
@@ -50,6 +52,7 @@
             assert_approx_equals(s.bottom_delta, 60, epsilon, "bottom border");
             assert_approx_equals(s.element_width_delta, 30 + 40, epsilon, "element width");
             assert_approx_equals(s.element_height_delta, 50 + 60, epsilon, "element height");
+            assert_approx_equals(s.preferred_width_delta, 30 + 40, epsilon, "element preferred width");
         }, `Border properties on ${tag} (rtl)`);
     }
 
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/margin-002.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/margin-002.html
index 69e1fd6..01c8f3e 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/margin-002.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/margin-002.html
@@ -30,6 +30,7 @@
                 assert_approx_equals(s.height_delta, 50 + 60, epsilon, "top/bottom margin");
                 assert_approx_equals(s.element_width_delta, 0, epsilon, "element width");
                 assert_approx_equals(s.element_height_delta, 0, epsilon, "element height");
+                assert_approx_equals(s.preferred_width_delta, 30 + 40, epsilon, "preferred width");
             }, `Margin properties on ${tag}`);
             continue;
         }
@@ -42,6 +43,7 @@
             assert_approx_equals(s.bottom_delta, 60, epsilon, "bottom margin");
             assert_approx_equals(s.element_width_delta, 0, epsilon, "element width");
             assert_approx_equals(s.element_height_delta, 0, epsilon, "element height");
+            assert_approx_equals(s.preferred_width_delta, 30 + 40, epsilon, "preferred width");
         }, `Margin properties on ${tag}`);
 
         test(function() {
@@ -52,6 +54,7 @@
             assert_approx_equals(s.bottom_delta, 60, epsilon, "bottom margin");
             assert_approx_equals(s.element_width_delta, 0, epsilon, "element width");
             assert_approx_equals(s.element_height_delta, 0, epsilon, "element height");
+            assert_approx_equals(s.preferred_width_delta, 30 + 40, epsilon, "preferred width");
         }, `Margin properties on ${tag} (rtl)`);
 
         test(function() {
@@ -64,6 +67,7 @@
             assert_approx_equals(s.bottom_delta, 60 * 2, epsilon, "bottom margin");
             assert_approx_equals(s.element_width_delta, 0, epsilon, "element width");
             assert_approx_equals(s.element_height_delta, 0, epsilon, "element height");
+            assert_approx_equals(s.preferred_width_delta, (30 + 40) * 2, epsilon, "preferred width");
         }, `Margin properties on ${tag} (no margin-collapsing)`);
     }
 
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/padding-002.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/padding-002.html
index a2f167a..565dfc43 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/padding-002.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/padding-002.html
@@ -28,6 +28,7 @@
                 var s = compareSizeWithAndWithoutStyle(tag, style);
                 assert_approx_equals(s.element_width_delta, 30 + 40, epsilon, "left/right padding");
                 assert_approx_equals(s.element_height_delta, 50 + 60, epsilon, "top/bottom padding");
+                assert_approx_equals(s.preferred_width_delta, 30 + 40, epsilon, "preferred width");
             }, `Padding properties on ${tag}`);
             continue;
         }
@@ -40,6 +41,7 @@
             assert_approx_equals(s.bottom_delta, 60, epsilon, "bottom padding");
             assert_approx_equals(s.element_width_delta, 30 + 40, epsilon, "element width");
             assert_approx_equals(s.element_height_delta, 50 + 60, epsilon, "element height");
+            assert_approx_equals(s.preferred_width_delta, 30 + 40, epsilon, "preferred width");
         }, `Padding properties on ${tag}`);
 
         test(function() {
@@ -50,6 +52,7 @@
             assert_approx_equals(s.bottom_delta, 60, epsilon, "bottom padding");
             assert_approx_equals(s.element_width_delta, 30 + 40, epsilon, "element width");
             assert_approx_equals(s.element_height_delta, 50 + 60, epsilon, "element height");
+            assert_approx_equals(s.preferred_width_delta, 30 + 40, epsilon, "preferred width");
         }, `Padding properties on ${tag} (rtl)`);
     }
 
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/clipboard-event-handlers.tentative.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/clipboard-event-handlers.tentative.html
index d293b15..00310046 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/clipboard-event-handlers.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/clipboard-event-handlers.tentative.html
@@ -1,25 +1,10 @@
 <!DOCTYPE html>
 <title>DocumentAndElementEventHandlers / clipboard events for MathML</title>
-<link
-  rel="help"
-  href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript"
-/>
-<link
-  rel="help"
-  href="https://html.spec.whatwg.org/multipage/webappapis.html#documentandelementeventhandlers"
-/>
-<link
-  rel="help"
-  href="https://w3c.github.io/clipboard-apis/#clipboard-event-copy"
-/>
-<link
-  rel="help"
-  href="https://w3c.github.io/clipboard-apis/#clipboard-event-cut"
-/>
-<link
-  rel="help"
-  href="https://w3c.github.io/clipboard-apis/#clipboard-event-paste"
-/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#documentandelementeventhandlers"/>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#clipboard-event-copy"/>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#clipboard-event-cut"/>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#clipboard-event-paste"/>
 <meta
   name="assert"
   content="MathMLElements incorporate a functional DocumentAndElementEventHandlers interface"
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/css-inline-style-interface.tentative.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/css-inline-style-interface.tentative.html
index 7089264..b82f98e 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/css-inline-style-interface.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/css-inline-style-interface.tentative.html
@@ -3,10 +3,7 @@
   <head>
     <meta charset="utf-8" />
     <title>MathML 'ElementCSSInlineStyle` Mixin Tests</title>
-    <link
-      rel="help"
-      href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript"
-    />
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript"/>
     <style>
       math * {
         background-color: red;
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/html-or-foreign-element-interfaces.tentative.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/html-or-foreign-element-interfaces.tentative.html
index 1fcaf6c..bf4b998 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/html-or-foreign-element-interfaces.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/html-or-foreign-element-interfaces.tentative.html
@@ -3,10 +3,7 @@
   <head>
     <meta charset="utf-8" />
     <title>MathML 'HTMLOrForeignElement` Mixin Tests</title>
-    <link
-      rel="help"
-      href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript"
-    />
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript"/>
     <style>
       mi {
         background-color: red;
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/math-global-event-handlers.tentative.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/math-global-event-handlers.tentative.html
index 92e1863..807a29e 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/math-global-event-handlers.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/math-global-event-handlers.tentative.html
@@ -1,18 +1,9 @@
 <!DOCTYPE html>
 <title>MathMLElement GlobalEventHandlers</title>
 <link rel="author" title="Brian Kardell" href="mailto:bkardell@igalia.com" />
-<link
-  rel="help"
-  href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript"
-/>
-<link
-  rel="help"
-  href="https://html.spec.whatwg.org/multipage/#event-handler-idl-attributes"
-/>
-<link
-  rel="help"
-  href="https://html.spec.whatwg.org/multipage/#event-handler-content-attributes"
-/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#event-handler-idl-attributes"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#event-handler-content-attributes"/>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/WebIDLParser.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/support/box-comparison.js b/third_party/blink/web_tests/external/wpt/mathml/support/box-comparison.js
index c46808f..a574b01 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/support/box-comparison.js
+++ b/third_party/blink/web_tests/external/wpt/mathml/support/box-comparison.js
@@ -23,12 +23,13 @@
     if (!direction)
       direction = "ltr";
     document.body.insertAdjacentHTML("beforeend", `<div style="position: absolute;">\
-<math><mrow dir="${direction}">${MathMLFragments[tag]}</mrow></math>\
-<math><mrow dir="${direction}">${MathMLFragments[tag]}</mrow></math>\
+<div style="display: inline-block"><math><mrow dir="${direction}">${MathMLFragments[tag]}</mrow></math></div>\
+<div style="display: inline-block"><math><mrow dir="${direction}">${MathMLFragments[tag]}</mrow></math></div>\
 </div>`);
     var div = document.body.lastElementChild;
 
-    var styleMath = div.firstElementChild;
+    var styleDiv = div.firstElementChild;
+    var styleMath = styleDiv.firstElementChild;
     var styleParent = styleMath.firstElementChild;
     if (parentStyle)
         styleParent.setAttribute("style", parentStyle);
@@ -40,7 +41,8 @@
     var styleChildBox = styleChild.getBoundingClientRect();
     var styleSpace = spaceBetween(styleChildBox, styleMathBox);
 
-    var noStyleMath = div.lastElementChild;
+    var noStyleDiv = div.lastElementChild;
+    var noStyleMath = noStyleDiv.firstElementChild;
     var noStyleElement = FragmentHelper.element(noStyleMath);
     var noStyleChild = FragmentHelper.forceNonEmptyElement(noStyleElement);
     var noStyleMathBox = noStyleMath.getBoundingClientRect();
@@ -48,9 +50,14 @@
     var noStyleChildBox = noStyleChild.getBoundingClientRect();
     var noStyleSpace = spaceBetween(noStyleChildBox, noStyleMathBox);
 
+    var preferredWidthDelta =
+        styleDiv.getBoundingClientRect().width -
+        noStyleDiv.getBoundingClientRect().width;
+
     div.style = "display: none;"; // Hide the div after measurement.
 
     return {
+        preferred_width_delta: preferredWidthDelta,
         left_delta: styleSpace.left - noStyleSpace.left,
         right_delta: styleSpace.right - noStyleSpace.right,
         top_delta: styleSpace.top - noStyleSpace.top,
@@ -65,25 +72,32 @@
         throw `Invalid argument: ${tag}`;
 
     document.body.insertAdjacentHTML("beforeend", `<div style="position: absolute;">\
-<math>${MathMLFragments[tag]}</math>\
-<math>${MathMLFragments[tag]}</math>\
+<div style="display: inline-block"><math>${MathMLFragments[tag]}</math></div>\
+<div style="display: inline-block"><math>${MathMLFragments[tag]}</math></div>\
 </div>`);
     var div = document.body.lastElementChild;
 
-    var styleMath = div.firstElementChild;
+    var styleDiv = div.firstElementChild;
+    var styleMath = styleDiv.firstElementChild;
     var styleElement = FragmentHelper.element(styleMath);
     styleElement.setAttribute("style", style);
     var styleMathBox = styleMath.getBoundingClientRect();
     var styleElementBox = styleElement.getBoundingClientRect();
 
-    var noStyleMath = div.lastElementChild;
+    var noStyleDiv = div.lastElementChild;
+    var noStyleMath = noStyleDiv.firstElementChild;
     var noStyleElement = FragmentHelper.element(noStyleMath);
     var noStyleMathBox = noStyleMath.getBoundingClientRect();
     var noStyleElementBox = noStyleElement.getBoundingClientRect();
 
+    var preferredWidthDelta =
+        styleDiv.getBoundingClientRect().width -
+        noStyleDiv.getBoundingClientRect().width;
+
     div.style = "display: none;"; // Hide the div after measurement.
 
     return {
+        preferred_width_delta: preferredWidthDelta,
         width_delta: styleMathBox.width - noStyleMathBox.width,
         height_delta: styleMathBox.height - noStyleMathBox.height,
         element_width_delta: styleElementBox.width - noStyleElementBox.width,
diff --git a/third_party/blink/web_tests/external/wpt/media-source/idlharness.any.js b/third_party/blink/web_tests/external/wpt/media-source/idlharness.any.js
deleted file mode 100644
index 7992b11..0000000
--- a/third_party/blink/web_tests/external/wpt/media-source/idlharness.any.js
+++ /dev/null
@@ -1,59 +0,0 @@
-// META: script=/resources/WebIDLParser.js
-// META: script=/resources/idlharness.js
-
-// https://w3c.github.io/media-source/
-
-'use strict';
-
-var mediaSource;
-var sourceBuffer;
-var video = document.createElement("video");
-
-promise_test(async t => {
-  const srcs = ['media-source', 'dom', 'html', 'url'];
-  const [idl, dom, html, url] = await Promise.all(
-      srcs.map(i => fetch(`/interfaces/${i}.idl`).then(r => r.text())));
-
-  var idlArray = new IdlArray();
-  idlArray.add_idls(idl);
-  idlArray.add_dependency_idls(dom);
-  idlArray.add_dependency_idls(html);
-  idlArray.add_dependency_idls(url);
-
-  const testIdls = new Promise(resolve => {
-    try {
-      mediaSource = new MediaSource();
-      video.src = URL.createObjectURL(mediaSource);
-      mediaSource.addEventListener("sourceopen", function () {
-        var defaultType ='video/webm;codecs="vp8,vorbis"';
-        if (MediaSource.isTypeSupported(defaultType)) {
-          sourceBuffer = mediaSource.addSourceBuffer(defaultType);
-        } else {
-          sourceBuffer = mediaSource.addSourceBuffer('video/mp4');
-        }
-        sourceBuffer.addEventListener("updateend", function (e) {
-          mediaSource.endOfStream();
-          resolve();
-        });
-        sourceBuffer.appendBuffer(new ArrayBuffer());
-      });
-    } catch (e) {
-      // Will be surfaced in idlharness.js's test_object below.
-    }
-  })
-
-  idlArray.add_objects({
-    MediaSource: ['mediaSource'],
-    SourceBuffer: ['sourceBuffer'],
-    SourceBufferList: ['mediaSource.sourceBuffers']
-  });
-
-  const timeout = new Promise((_, reject) => t.step_timeout(reject, 3000));
-  return Promise
-      .race([testIdls, timeout])
-      .then(() => { idlArray.test(); })
-      .catch(() => {
-        idlArray.test();
-        return Promise.reject('Failed to create media-source objects')
-      });
-}, 'media-source interfaces');
diff --git a/third_party/blink/web_tests/external/wpt/media-source/idlharness.window-expected.txt b/third_party/blink/web_tests/external/wpt/media-source/idlharness.window-expected.txt
new file mode 100644
index 0000000..21a2eec7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/media-source/idlharness.window-expected.txt
@@ -0,0 +1,111 @@
+This is a testharness.js-based test.
+Found 107 tests; 104 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS idl_test setup
+PASS Partial interface URL: original interface defined
+PASS Partial interface URL: valid exposure set
+PASS Partial interface AudioTrack: original interface defined
+PASS Partial interface VideoTrack: original interface defined
+PASS Partial interface TextTrack: original interface defined
+PASS MediaSource interface: existence and properties of interface object
+PASS MediaSource interface object length
+PASS MediaSource interface object name
+PASS MediaSource interface: existence and properties of interface prototype object
+PASS MediaSource interface: existence and properties of interface prototype object's "constructor" property
+PASS MediaSource interface: existence and properties of interface prototype object's @@unscopables property
+PASS MediaSource interface: attribute sourceBuffers
+PASS MediaSource interface: attribute activeSourceBuffers
+PASS MediaSource interface: attribute readyState
+PASS MediaSource interface: attribute duration
+PASS MediaSource interface: attribute onsourceopen
+PASS MediaSource interface: attribute onsourceended
+PASS MediaSource interface: attribute onsourceclose
+PASS MediaSource interface: operation addSourceBuffer(DOMString)
+PASS MediaSource interface: operation removeSourceBuffer(SourceBuffer)
+PASS MediaSource interface: operation endOfStream(EndOfStreamError)
+PASS MediaSource interface: operation setLiveSeekableRange(double, double)
+PASS MediaSource interface: operation clearLiveSeekableRange()
+PASS MediaSource interface: operation isTypeSupported(DOMString)
+PASS MediaSource must be primary interface of mediaSource
+PASS Stringification of mediaSource
+PASS MediaSource interface: mediaSource must inherit property "sourceBuffers" with the proper type
+PASS MediaSource interface: mediaSource must inherit property "activeSourceBuffers" with the proper type
+PASS MediaSource interface: mediaSource must inherit property "readyState" with the proper type
+PASS MediaSource interface: mediaSource must inherit property "duration" with the proper type
+PASS MediaSource interface: mediaSource must inherit property "onsourceopen" with the proper type
+PASS MediaSource interface: mediaSource must inherit property "onsourceended" with the proper type
+PASS MediaSource interface: mediaSource must inherit property "onsourceclose" with the proper type
+PASS MediaSource interface: mediaSource must inherit property "addSourceBuffer(DOMString)" with the proper type
+PASS MediaSource interface: calling addSourceBuffer(DOMString) on mediaSource with too few arguments must throw TypeError
+PASS MediaSource interface: mediaSource must inherit property "removeSourceBuffer(SourceBuffer)" with the proper type
+PASS MediaSource interface: calling removeSourceBuffer(SourceBuffer) on mediaSource with too few arguments must throw TypeError
+PASS MediaSource interface: mediaSource must inherit property "endOfStream(EndOfStreamError)" with the proper type
+PASS MediaSource interface: calling endOfStream(EndOfStreamError) on mediaSource with too few arguments must throw TypeError
+PASS MediaSource interface: mediaSource must inherit property "setLiveSeekableRange(double, double)" with the proper type
+PASS MediaSource interface: calling setLiveSeekableRange(double, double) on mediaSource with too few arguments must throw TypeError
+PASS MediaSource interface: mediaSource must inherit property "clearLiveSeekableRange()" with the proper type
+PASS MediaSource interface: mediaSource must inherit property "isTypeSupported(DOMString)" with the proper type
+PASS MediaSource interface: calling isTypeSupported(DOMString) on mediaSource with too few arguments must throw TypeError
+PASS SourceBuffer interface: existence and properties of interface object
+PASS SourceBuffer interface object length
+PASS SourceBuffer interface object name
+PASS SourceBuffer interface: existence and properties of interface prototype object
+PASS SourceBuffer interface: existence and properties of interface prototype object's "constructor" property
+PASS SourceBuffer interface: existence and properties of interface prototype object's @@unscopables property
+PASS SourceBuffer interface: attribute mode
+PASS SourceBuffer interface: attribute updating
+PASS SourceBuffer interface: attribute buffered
+PASS SourceBuffer interface: attribute timestampOffset
+PASS SourceBuffer interface: attribute audioTracks
+PASS SourceBuffer interface: attribute videoTracks
+FAIL SourceBuffer interface: attribute textTracks assert_true: The prototype object must have a property "textTracks" expected true got false
+PASS SourceBuffer interface: attribute appendWindowStart
+PASS SourceBuffer interface: attribute appendWindowEnd
+PASS SourceBuffer interface: attribute onupdatestart
+PASS SourceBuffer interface: attribute onupdate
+PASS SourceBuffer interface: attribute onupdateend
+PASS SourceBuffer interface: attribute onerror
+PASS SourceBuffer interface: attribute onabort
+PASS SourceBuffer interface: operation appendBuffer(BufferSource)
+PASS SourceBuffer interface: operation abort()
+PASS SourceBuffer interface: operation remove(double, unrestricted double)
+PASS SourceBuffer must be primary interface of sourceBuffer
+PASS Stringification of sourceBuffer
+PASS SourceBuffer interface: sourceBuffer must inherit property "mode" with the proper type
+PASS SourceBuffer interface: sourceBuffer must inherit property "updating" with the proper type
+PASS SourceBuffer interface: sourceBuffer must inherit property "buffered" with the proper type
+PASS SourceBuffer interface: sourceBuffer must inherit property "timestampOffset" with the proper type
+PASS SourceBuffer interface: sourceBuffer must inherit property "audioTracks" with the proper type
+PASS SourceBuffer interface: sourceBuffer must inherit property "videoTracks" with the proper type
+FAIL SourceBuffer interface: sourceBuffer must inherit property "textTracks" with the proper type assert_inherits: property "textTracks" not found in prototype chain
+PASS SourceBuffer interface: sourceBuffer must inherit property "appendWindowStart" with the proper type
+PASS SourceBuffer interface: sourceBuffer must inherit property "appendWindowEnd" with the proper type
+PASS SourceBuffer interface: sourceBuffer must inherit property "onupdatestart" with the proper type
+PASS SourceBuffer interface: sourceBuffer must inherit property "onupdate" with the proper type
+PASS SourceBuffer interface: sourceBuffer must inherit property "onupdateend" with the proper type
+PASS SourceBuffer interface: sourceBuffer must inherit property "onerror" with the proper type
+PASS SourceBuffer interface: sourceBuffer must inherit property "onabort" with the proper type
+PASS SourceBuffer interface: sourceBuffer must inherit property "appendBuffer(BufferSource)" with the proper type
+PASS SourceBuffer interface: calling appendBuffer(BufferSource) on sourceBuffer with too few arguments must throw TypeError
+PASS SourceBuffer interface: sourceBuffer must inherit property "abort()" with the proper type
+PASS SourceBuffer interface: sourceBuffer must inherit property "remove(double, unrestricted double)" with the proper type
+PASS SourceBuffer interface: calling remove(double, unrestricted double) on sourceBuffer with too few arguments must throw TypeError
+PASS SourceBufferList interface: existence and properties of interface object
+PASS SourceBufferList interface object length
+PASS SourceBufferList interface object name
+PASS SourceBufferList interface: existence and properties of interface prototype object
+PASS SourceBufferList interface: existence and properties of interface prototype object's "constructor" property
+PASS SourceBufferList interface: existence and properties of interface prototype object's @@unscopables property
+PASS SourceBufferList interface: attribute length
+PASS SourceBufferList interface: attribute onaddsourcebuffer
+PASS SourceBufferList interface: attribute onremovesourcebuffer
+PASS SourceBufferList must be primary interface of mediaSource.sourceBuffers
+PASS Stringification of mediaSource.sourceBuffers
+PASS SourceBufferList interface: mediaSource.sourceBuffers must inherit property "length" with the proper type
+PASS SourceBufferList interface: mediaSource.sourceBuffers must inherit property "onaddsourcebuffer" with the proper type
+PASS SourceBufferList interface: mediaSource.sourceBuffers must inherit property "onremovesourcebuffer" with the proper type
+PASS AudioTrack interface: attribute sourceBuffer
+PASS VideoTrack interface: attribute sourceBuffer
+FAIL TextTrack interface: attribute sourceBuffer assert_true: The prototype object must have a property "sourceBuffer" expected true got false
+PASS URL interface: operation createObjectURL(MediaSource)
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/media-source/idlharness.window.js b/third_party/blink/web_tests/external/wpt/media-source/idlharness.window.js
new file mode 100644
index 0000000..28da56b3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/media-source/idlharness.window.js
@@ -0,0 +1,36 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+
+// https://w3c.github.io/media-source/
+
+'use strict';
+
+idl_test(
+  ['media-source'],
+  ['dom', 'html', 'url'],
+  async idl_array => {
+    self.audio = document.createElement('audio');
+    self.video = document.createElement('video');
+    idl_array.add_objects({
+      MediaSource: ['mediaSource'],
+      SourceBuffer: ['sourceBuffer'],
+      SourceBufferList: ['mediaSource.sourceBuffers'],
+    });
+
+    const video = document.createElement('video');
+    self.mediaSource = new MediaSource();
+    video.src = URL.createObjectURL(mediaSource);
+
+    self.sourceBuffer = await new Promise((resolve, reject) => {
+      mediaSource.addEventListener('sourceopen', () => {
+        var defaultType = 'video/webm;codecs="vp8,vorbis"';
+        if (MediaSource.isTypeSupported(defaultType)) {
+          resolve(mediaSource.addSourceBuffer(defaultType));
+        } else {
+          resolve(mediaSource.addSourceBuffer('video/mp4'));
+        }
+      });
+      step_timeout(() => reject(new Error('sourceopen event not fired')), 3000);
+    });
+  }
+);
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-tab-navigation-expected.txt b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-tab-navigation-expected.txt
new file mode 100644
index 0000000..2b6a1199
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-tab-navigation-expected.txt
@@ -0,0 +1,2 @@
+Popup did not open.
+TEST COMPLETE
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-tab-navigation.html b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-tab-navigation.html
new file mode 100644
index 0000000..974123f
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-tab-navigation.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+testRunner.dumpAsText();
+testRunner.waitUntilDone();
+</script>
+<script src='../../../forms/resources/picker-common.js'></script>
+</head>
+<body>
+<input type='color' id='color'>
+
+<p id='description' style='opacity: 0'></p>
+<div id='console' style='opacity: 0'></div>
+
+<script>
+let descriptionContainer = document.getElementById('description');
+openPicker(document.getElementById('color'), openPickerCallback, openPickerCallback);
+
+function openPickerCallback() {
+  if (internals.pagePopupWindow) {
+    descriptionContainer.append('Popup opened.', document.createElement('br'));
+    popupWindow.focus();
+    const popupDocument = popupWindow.document;
+    const focusableElements = popupDocument
+        .querySelectorAll('color-value-container:not(.hidden-color-value-container) > input, ' +
+                          '[tabindex]:not([tabindex=\'-1\'])');
+    for(let i = 0; i < focusableElements.length; i++) {
+      eventSender.keyDown('Tab');
+      if (popupDocument.activeElement.hasAttribute('id')) {
+        descriptionContainer.append(popupDocument.activeElement.getAttribute('id').toUpperCase());
+      } else {
+        descriptionContainer.append(popupDocument.activeElement.tagName);
+      }
+      descriptionContainer.append(document.createElement('br'));
+    }
+  } else {
+    descriptionContainer.append('Popup did not open.', document.createElement('br'));
+  }
+  descriptionContainer.append('TEST COMPLETE');
+  testRunner.notifyDone();
+}
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/web-animations/timing-model/animations/pausing-an-animation-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/web-animations/timing-model/animations/pausing-an-animation-expected.txt
new file mode 100644
index 0000000..04baedd8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/web-animations/timing-model/animations/pausing-an-animation-expected.txt
@@ -0,0 +1,8 @@
+This is a testharness.js-based test.
+PASS Pausing clears the start time
+PASS Aborting a pause preserves the start time
+PASS A pending ready promise should be resolved and not replaced when the animation is paused
+FAIL A pause-pending animation maintains the current time when applying a pending playback rate assert_greater_than: expected a number greater than 50000 but got 50000
+PASS The animation's current time remains fixed after pausing
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
new file mode 100644
index 0000000..64dbbdf
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
@@ -0,0 +1,14 @@
+This is a testharness.js-based test.
+PASS Initial iceConnectionState should be new
+PASS Closing the connection should set iceConnectionState to closed
+PASS connection with one data channel should eventually have connected or completed connection state
+FAIL connection with one data channel should eventually have connected connection state Cannot read property 'transport' of null
+PASS connection with audio track should eventually have connected connection state
+PASS connection with audio and video tracks should eventually have connected connection state
+FAIL ICE can connect in a recvonly usecase promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL iceConnectionState changes at the right time, with bundle policy balanced promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined"
+FAIL iceConnectionState changes at the right time, with bundle policy max-bundle promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined"
+FAIL iceConnectionState changes at the right time, with bundle policy max-compat promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined"
+PASS Responder ICE connection state behaves as expected
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/external/wpt/web-animations/timing-model/animations/pausing-an-animation-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/external/wpt/web-animations/timing-model/animations/pausing-an-animation-expected.txt
new file mode 100644
index 0000000..04baedd8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/external/wpt/web-animations/timing-model/animations/pausing-an-animation-expected.txt
@@ -0,0 +1,8 @@
+This is a testharness.js-based test.
+PASS Pausing clears the start time
+PASS Aborting a pause preserves the start time
+PASS A pending ready promise should be resolved and not replaced when the animation is paused
+FAIL A pause-pending animation maintains the current time when applying a pending playback rate assert_greater_than: expected a number greater than 50000 but got 50000
+PASS The animation's current time remains fixed after pausing
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
similarity index 100%
copy from third_party/blink/web_tests/platform/mac-retina/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
copy to third_party/blink/web_tests/platform/mac-mac10.11/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
new file mode 100644
index 0000000..64dbbdf
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
@@ -0,0 +1,14 @@
+This is a testharness.js-based test.
+PASS Initial iceConnectionState should be new
+PASS Closing the connection should set iceConnectionState to closed
+PASS connection with one data channel should eventually have connected or completed connection state
+FAIL connection with one data channel should eventually have connected connection state Cannot read property 'transport' of null
+PASS connection with audio track should eventually have connected connection state
+PASS connection with audio and video tracks should eventually have connected connection state
+FAIL ICE can connect in a recvonly usecase promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL iceConnectionState changes at the right time, with bundle policy balanced promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined"
+FAIL iceConnectionState changes at the right time, with bundle policy max-bundle promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined"
+FAIL iceConnectionState changes at the right time, with bundle policy max-compat promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined"
+PASS Responder ICE connection state behaves as expected
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/platform/mac-retina/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
rename to third_party/blink/web_tests/platform/mac/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
diff --git a/third_party/blink/web_tests/presentation/resources/presentation-service-mock.js b/third_party/blink/web_tests/presentation/resources/presentation-service-mock.js
index f38376d..16b5bfa 100644
--- a/third_party/blink/web_tests/presentation/resources/presentation-service-mock.js
+++ b/third_party/blink/web_tests/presentation/resources/presentation-service-mock.js
@@ -55,8 +55,8 @@
     return {
       result: {
         presentationInfo: {url: urls[0], id: 'fakePresentationId'},
-        connectionPtr: receiver_ptr,
-        connectionRequest: mojo.makeRequest(controller_ptr),
+        connectionRemote: receiver_ptr,
+        connectionReceiver: mojo.makeRequest(controller_ptr),
       },
       error: null,
     };
@@ -70,8 +70,8 @@
     return {
       result: {
         presentationInfo: {url: urls[0], id: 'fakePresentationId'},
-        connectionPtr: receiver_ptr,
-        connectionRequest: mojo.makeRequest(controller_ptr),
+        connectionRemote: receiver_ptr,
+        connectionReceiver: mojo.makeRequest(controller_ptr),
       },
       error: null,
     };
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-tab-navigation-expected.txt b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-tab-navigation-expected.txt
new file mode 100644
index 0000000..f8190be
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-tab-navigation-expected.txt
@@ -0,0 +1,10 @@
+Popup opened.
+COLOR-SELECTION-RING
+COLOR-SELECTION-RING
+RVALUECONTAINER
+GVALUECONTAINER
+BVALUECONTAINER
+FORMAT-TOGGLER
+SUBMISSION-BUTTON
+SUBMISSION-BUTTON
+TEST COMPLETE
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 22a5996..6e10ff95 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-10-1-8-g734d60f63
-Revision: 734d60f63cfa27f9b337ddbb80adb9edd60475bf
+Version: VER-2-10-1-9-g7d1d3b9a0
+Revision: 7d1d3b9a0e9310376a559ad2eac8a9dc4c60ce59
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
 License File: src/docs/FTL.TXT
diff --git a/third_party/google_android_play_core/README.chromium b/third_party/google_android_play_core/README.chromium
index 90876940..02e2221 100644
--- a/third_party/google_android_play_core/README.chromium
+++ b/third_party/google_android_play_core/README.chromium
@@ -7,7 +7,7 @@
 Security Critical: yes
 
 Description:
-
+- Built from @264816199
 
 Local Modifications:
 - Unproguarded verification API.
diff --git a/third_party/google_android_play_core/com_google_android_play_core.info b/third_party/google_android_play_core/com_google_android_play_core.info
index 2307336..0f4c57f 100644
--- a/third_party/google_android_play_core/com_google_android_play_core.info
+++ b/third_party/google_android_play_core/com_google_android_play_core.info
@@ -8,6 +8,6 @@
 has_proguard_flags = false
 has_r_text_file = false
 is_manifest_empty = false
-resources = [  ]
+resources = [ "res/values/styles.xml" ]
 subjar_tuples = [  ]
 subjars = [  ]
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index 28fe189..38e360d 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -1084,7 +1084,6 @@
     is_cros = ('target_os="chromeos"' in vals['gn_args'] or
                vals.get('cros_passthrough', False))
     is_mac = self.platform == 'darwin'
-    is_win = self.platform == 'win32' or 'target_os="win"' in vals['gn_args']
     is_msan = 'is_msan=true' in vals['gn_args']
 
     err = ''
@@ -1092,7 +1091,7 @@
       # Skip a few configs that need extra cleanup for now.
       # TODO(https://crbug.com/912946): Fix everything on all platforms and
       # enable check everywhere.
-      if is_android or is_cros or is_mac or is_win or is_msan:
+      if is_android or is_cros or is_mac or is_msan:
         break
 
       # Skip a few existing violations that need to be cleaned up. Each of
@@ -1100,27 +1099,24 @@
       # contents change. Do not add to this list.
       # TODO(https://crbug.com/912946): Remove this if statement.
       if (f == 'angledata/gl_cts/' or  # http://anglebug.com/3827
-          f == 'gen/chrome/browser/resources/media_router/extension/' or
-          f == 'gen/components/subresource_filter/tools/' or
           f == 'locales/' or
           f.startswith('nacl_test_data/') or
           f.startswith('ppapi_nacl_tests_libs/') or
           f == 'test_url_loader_data/'):
         continue
 
-      path = os.path.normpath(self.PathJoin(build_dir, f))
-      # This runs before the build, so we can't use isdir(path). But
+      # This runs before the build, so we can't use isdir(f). But
       # isolate.py luckily requires data directories to end with '/', so we
-      # can check for that. (Check f instead of path because normpath() removes
-      # trailing slashes.)
-      if path.startswith(build_dir) and f.endswith('/'):
-        # See https://crbug.com/912946
-        err += '\n' + path
+      # can check for that.
+      # TODO(thakis): Use '../../' once crbug.com/997673 is fixed.
+      if not f.startswith('../') and f.endswith('/'):
+        # Don't use self.PathJoin() -- all involved paths consistently use
+        # forward slashes, so don't add one single backslash on Windows.
+        err += '\n' + build_dir + '/' +  f
 
     if err:
       self.Print('error: gn `data` items may not list generated directories; '
-                 'list files in directory instead for:' + err,
-                 file=sys.stderr)
+                 'list files in directory instead for:' + err)
       return 1
 
     self.WriteFile(isolate_path,
diff --git a/tools/mb/mb_unittest.py b/tools/mb/mb_unittest.py
index 98e4ea4..35957e8e 100755
--- a/tools/mb/mb_unittest.py
+++ b/tools/mb/mb_unittest.py
@@ -654,9 +654,11 @@
     mbw.cmds.append((0, 'base_unitests\ntest_data/\n', ''))
     expected_err = ('error: gn `data` items may not list generated directories;'
                     ' list files in directory instead for:\n'
-                    '//out/Default/test_data\n')
+                    '//out/Default/test_data/\n')
     self.check(['isolate', '-c', 'debug_goma', '//out/Default',
-                'base_unittests'], mbw=mbw, ret=1, err=expected_err)
+                'base_unittests'], mbw=mbw, ret=1)
+    self.assertEqual(mbw.out[-len(expected_err):], expected_err)
+
 
   def test_run(self):
     files = {
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 1cbfeab..217e071 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -9600,6 +9600,17 @@
   <int value="1" label="Contact(s) selected"/>
 </enum>
 
+<enum name="ContactsPickerProperties">
+  <int value="0" label="No properties requested"/>
+  <int value="1" label="Telephone numbers requested"/>
+  <int value="2" label="Emails requested"/>
+  <int value="3" label="Emails and Telephone numbers requested"/>
+  <int value="4" label="Names requested"/>
+  <int value="5" label="Names and Telephone numbers requested"/>
+  <int value="6" label="Names and Emails requested"/>
+  <int value="7" label="Names, Emails, and Telephone numbers requested"/>
+</enum>
+
 <enum name="ContentIndexCategory">
   <summary>
     The type of a Content Index entry. Corresponds to
@@ -35116,7 +35127,6 @@
 
   <int value="-1086728979" label="kids-management-url-classification:enabled"/>
   <int value="-1085492638" label="FetchKeepaliveTimeoutSetting:enabled"/>
-  <int value="-1084855234" label="UnfilteredBluetoothDevices:disabled"/>
   <int value="-1084055006" label="disable-web-notification-custom-layouts"/>
   <int value="-1083547717" label="NotificationExpansionAnimation:disabled"/>
   <int value="-1082302549" label="scan-cards-in-web-payments"/>
@@ -35707,7 +35717,6 @@
   <int value="-268357961" label="enable-feature-policy"/>
   <int value="-263150202" label="BundledConnectionHelp:disabled"/>
   <int value="-262122630" label="ArcEnableDocumentsProviderInFilesApp:enabled"/>
-  <int value="-260355779" label="UnfilteredBluetoothDevices:enabled"/>
   <int value="-258081634" label="AutofillAssistantDirectActions:disabled"/>
   <int value="-255264176" label="OmniboxSearchEngineLogo:disabled"/>
   <int value="-254887599" label="google-profile-info"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 75a2fe9f..f182b110 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -1984,6 +1984,16 @@
   </summary>
 </histogram>
 
+<histogram name="Android.ContactsPicker.PropertiesRequested"
+    units="ContactsPickerProperties" expires_after="2020-09-01">
+  <owner>finnur@chromium.org</owner>
+  <owner>twellington@chromium.org</owner>
+  <summary>
+    The contact properties requested by the website (names, emails, telephone
+    numbers). Measured once when the Contacts Picker dialog is dismissed.
+  </summary>
+</histogram>
+
 <histogram name="Android.ContactsPicker.SelectCount" units="Contacts"
     expires_after="2020-09-01">
   <owner>finnur@chromium.org</owner>
@@ -13804,7 +13814,10 @@
 </histogram>
 
 <histogram name="Blink.DecodedImage.JpegDensity.1000px"
-    units="0.01 bits per pixel" expires_after="2020-02-16">
+    units="0.01 bits per pixel" expires_after="M78">
+  <obsolete>
+    Deprecated in M79. Replaced by Blink.DecodedImage.JpegDensity.KiBWeighted.
+  </obsolete>
   <owner>deymo@google.com</owner>
   <owner>compression-dev@google.com</owner>
   <summary>
@@ -13815,7 +13828,10 @@
 </histogram>
 
 <histogram name="Blink.DecodedImage.JpegDensity.100px"
-    units="0.01 bits per pixel" expires_after="2020-02-02">
+    units="0.01 bits per pixel" expires_after="M78">
+  <obsolete>
+    Deprecated in M79. Replaced by Blink.DecodedImage.JpegDensity.KiBWeighted.
+  </obsolete>
   <owner>deymo@google.com</owner>
   <owner>compression-dev@google.com</owner>
   <summary>
@@ -13827,7 +13843,10 @@
 </histogram>
 
 <histogram name="Blink.DecodedImage.JpegDensity.400px"
-    units="0.01 bits per pixel" expires_after="2020-01-26">
+    units="0.01 bits per pixel" expires_after="M78">
+  <obsolete>
+    Deprecated in M79. Replaced by Blink.DecodedImage.JpegDensity.KiBWeighted.
+  </obsolete>
   <owner>deymo@google.com</owner>
   <owner>compression-dev@google.com</owner>
   <summary>
@@ -13838,6 +13857,19 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.DecodedImage.JpegDensity.KiBWeighted"
+    units="0.01 bits per pixel" expires_after="2020-08-08">
+  <owner>deymo@google.com</owner>
+  <owner>compression-dev@google.com</owner>
+  <summary>
+    The compressed image size in KiB per image density measured in 0.01 bits per
+    pixel. This is logged once per image load after the whole image is loaded
+    and only for JPEGs with at least 100 pixels on the smallest dimension (width
+    or height). The reported count for a sample represents the image size
+    rounded to the nearest KiB.
+  </summary>
+</histogram>
+
 <histogram name="Blink.DecodedImage.Orientation" enum="DecodedImageOrientation">
   <owner>andrescj@chromium.org</owner>
   <owner>rob.buis@samsung.org</owner>
@@ -98200,6 +98232,16 @@
   <summary>The time it took to analyze a single leak lookup response.</summary>
 </histogram>
 
+<histogram name="PasswordManager.LeakDetection.Enabled" enum="BooleanEnabled"
+    expires_after="2020-01-31">
+  <owner>jdoerrie@chromium.org</owner>
+  <owner>vasilii@chromium.org</owner>
+  <summary>
+    Indicates whether the password manager leak detection feature is enabled.
+    Recorded for each profile on browser start-up.
+  </summary>
+</histogram>
+
 <histogram name="PasswordManager.LeakDetection.HttpResponseCode"
     enum="HttpResponseCode" expires_after="2020-01-31">
   <owner>jdoerrie@chromium.org</owner>
@@ -112031,47 +112073,27 @@
   </summary>
 </histogram>
 
-<histogram name="Renderer4.ImageDecodeTaskDurationUs" units="microseconds">
+<histogram base="true" name="Renderer4.ImageDecodeTaskDurationUs"
+    units="microseconds">
+<!-- Name completed by histogram_suffixes name="DecodedImageType" -->
+
+<!-- Name completed by histogram_suffixes name="OutOfRaster" -->
+
+<!-- Name completed by histogram_suffixes name="RasterTaskType" -->
+
   <owner>vmpstr@chromium.org</owner>
+  <owner>sashamcintosh@chromium.org</owner>
   <summary>
     This metric records the duration of an image decode for the raster path in
     the compositor. It is recorded every time we decode an image. It is suffixed
-    by the type of rasterization we're in (either Gpu or Software).
+    by the type of rasterization we're in (either Gpu or Software). The
+    rasterization suffix is optionally prefixed by the image type (WebP, Jpeg,
+    etc.) or OutOfRaster.
 
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution. When considering
-    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
-    solution.
-  </summary>
-</histogram>
-
-<histogram name="Renderer4.ImageDecodeTaskDurationUs.OutOfRaster"
-    units="microseconds">
-  <owner>vmpstr@chromium.org</owner>
-  <owner>khushalsagar@chromium.org</owner>
-  <summary>
-    This metric records the duration of an image decode for out of raster path
-    in the compositor. It is recorded every time we decode an image. It is
-    suffixed by the type of rasterization we're in (either Gpu or Software).
-
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution. When considering
-    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
-    solution.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Renderer4.ImageDecodeTaskDurationUs.WebP"
-    units="microseconds">
-  <owner>gildekel@chromium.org</owner>
-  <owner>andrescj@chromium.org</owner>
-  <summary>
-    This metric records the duration of a WebP image decode for both raster and
-    out-of-raster paths in the compositor. It is recorded every time we decode
-    an image. It is suffixed by the type of rasterization we're in (either Gpu
-    or Software).
+    Warning: Before version M78 this metric may include reports from clients
+    with low-resolution clocks (i.e. on Windows, ref.
+    |TimeTicks::IsHighResolution()|). Such reports will cause this metric to
+    have an abnormal distribution.
   </summary>
 </histogram>
 
@@ -161529,6 +161551,18 @@
   <affected-histogram name="DataUse.BackgroundToFirstDownstream"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="DecodedImageType" separator=".">
+<!-- Name completed by histogram_suffixes name="RasterTaskType" -->
+
+  <suffix base="true" name="Jpeg"
+      label="This metric is for only Jpeg image types."/>
+  <suffix base="true" name="Other"
+      label="This metric is for non-WebP and non-Jpeg image types."/>
+  <suffix base="true" name="WebP"
+      label="This metric is for only WebP image types."/>
+  <affected-histogram name="Renderer4.ImageDecodeTaskDurationUs"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="DefaultAppsExperiment" separator="_">
   <suffix name="NoDefaultApps" label="User's without default apps installed"/>
   <suffix name="WithDefaultApps" label="User's with default apps installed"/>
@@ -167509,6 +167543,7 @@
   <suffix name="OutOfRaster"
       label="Task was not performed as part of a raster task."/>
   <affected-histogram name="Renderer4.GpuImageDecodeState.FirstLockWasted"/>
+  <affected-histogram name="Renderer4.ImageDecodeTaskDurationUs"/>
   <affected-histogram
       name="Renderer4.SoftwareImageDecodeState.FirstLockWasted"/>
 </histogram_suffixes>
@@ -169956,6 +169991,8 @@
       name="Compositing.Renderer.RasterTask.RasterPixelsPerMs2"/>
   <affected-histogram name="Compositing.Renderer.RasterTask.RasterUs"/>
   <affected-histogram name="Renderer4.ImageDecodeTaskDurationUs"/>
+  <affected-histogram name="Renderer4.ImageDecodeTaskDurationUs.Jpeg"/>
+  <affected-histogram name="Renderer4.ImageDecodeTaskDurationUs.Other"/>
   <affected-histogram name="Renderer4.ImageDecodeTaskDurationUs.OutOfRaster"/>
   <affected-histogram name="Renderer4.ImageDecodeTaskDurationUs.WebP"/>
 </histogram_suffixes>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index b6c833a..b836efcf 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -2459,6 +2459,13 @@
     Measures statistics about the contacts shared in the contacts picker (when
     the Contacts Picker dialog is dismissed.)
   </summary>
+  <metric name="PropertiesRequested">
+    <summary>
+      Measures the contact properties requested by the website (names, emails,
+      telephone numbers). Bit 1 - telephone numbers, bit 2 - emails, bit 3 -
+      names.
+    </summary>
+  </metric>
   <metric name="SelectCount">
     <summary>
       Measures the total number of contacts selected in the contacts picker.
diff --git a/tools/perf/BUILD.gn b/tools/perf/BUILD.gn
index c9ddc30..eea77d11 100644
--- a/tools/perf/BUILD.gn
+++ b/tools/perf/BUILD.gn
@@ -30,9 +30,6 @@
     # For image_decoding.measurement
     "//chrome/test/data/image_decoding/",
 
-    # For ad tagging ruleset
-    "$root_gen_dir/components/subresource_filter/tools/",
-
     # For Pylib used by VR tests
     "//build/android/pylib/",
   ]
@@ -83,9 +80,6 @@
     # For image_decoding.measurement
     "//chrome/test/data/image_decoding/",
 
-    # For ad tagging ruleset
-    "$root_gen_dir/components/subresource_filter/tools/",
-
     # For Pylib used by VR tests
     "//build/android/pylib/",
   ]
diff --git a/tools/perf/contrib/media_router_benchmarks/media_router_pages.py b/tools/perf/contrib/media_router_benchmarks/media_router_pages.py
index e976a75..d0b47f24 100644
--- a/tools/perf/contrib/media_router_benchmarks/media_router_pages.py
+++ b/tools/perf/contrib/media_router_benchmarks/media_router_pages.py
@@ -2,8 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import time
-
 from telemetry import story
 from telemetry.page import shared_page_state
 from telemetry.util import js_template
@@ -79,11 +77,12 @@
 
       # Start session
       action_runner.TapElement(selector='#start_session_button')
+      action_runner.Wait(WAIT_TIME_SEC)
       self._WaitForResult(
         action_runner,
         lambda: action_runner.EvaluateJavaScript('currentSession'),
          'Failed to start session',
-         timeout=10)
+         timeout=WAIT_TIME_SEC)
 
       # Load Media
       self.ExecuteAsyncJavaScript(
@@ -133,6 +132,7 @@
               sink_name, str(action_runner.tab.GetCastSinks())))
 
       # Start session
+      action_runner.Wait(WAIT_TIME_SEC)
       action_runner.tab.StartTabMirroring(sink_name)
 
       # Make sure the route is created.
@@ -150,9 +150,7 @@
     super(MediaRouterCPUMemoryPageSet, self).__init__(
         cloud_storage_bucket=story.PARTNER_BUCKET)
     self.AddStory(CastIdlePage(self))
-    time.sleep(WAIT_TIME_SEC)
     self.AddStory(CastFlingingPage(self))
-    time.sleep(WAIT_TIME_SEC)
     self.AddStory(CastMirroringPage(self))
 
 
diff --git a/tools/perf/process_perf_results.py b/tools/perf/process_perf_results.py
index 0e32cca..a341249c 100755
--- a/tools/perf/process_perf_results.py
+++ b/tools/perf/process_perf_results.py
@@ -53,6 +53,7 @@
 # problems. So, only perform the conversion on tests that are whitelisted and
 # are okay with potentially encountering issues.
 GTEST_CONVERSION_WHITELIST = [
+  'angle_perftests',
   'xr.vr.common_perftests',
 ]
 
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc b/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
index 8eb0699c..200ee16 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
+++ b/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
@@ -129,7 +129,7 @@
                              file_str.length() - base_str.length() - 1);
     }
   }
-  return converted_file_path.MaybeAsASCII();
+  return file_path;
 }
 
 }  // namespace
diff --git a/tools/traffic_annotation/bin/linux64/traffic_annotation_auditor.sha1 b/tools/traffic_annotation/bin/linux64/traffic_annotation_auditor.sha1
index 3ff4035..74681b9 100644
--- a/tools/traffic_annotation/bin/linux64/traffic_annotation_auditor.sha1
+++ b/tools/traffic_annotation/bin/linux64/traffic_annotation_auditor.sha1
@@ -1 +1 @@
-e45bc93e4e7f4e853a557ce9a074e72b490f3f23
\ No newline at end of file
+62dbe9751c9d8d03810f9928e29859e5af43db8d
\ No newline at end of file
diff --git a/tools/traffic_annotation/bin/win32/traffic_annotation_auditor.exe.sha1 b/tools/traffic_annotation/bin/win32/traffic_annotation_auditor.exe.sha1
index 4b0e974..bde6d84f 100644
--- a/tools/traffic_annotation/bin/win32/traffic_annotation_auditor.exe.sha1
+++ b/tools/traffic_annotation/bin/win32/traffic_annotation_auditor.exe.sha1
@@ -1 +1 @@
-2025e4edae5aa037547cdbe111ed36e38ad9c7f9
\ No newline at end of file
+135da9329dc7bfe8e8056ed3976a704c777d49f3
\ No newline at end of file
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 152a99f..f555288 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -227,7 +227,7 @@
  <item id="resource_prefetch" hash_code="110815970" type="0" deprecated="2018-02-28" content_hash_code="39251261" file_path=""/>
  <item id="rlz_ping" hash_code="99279418" type="0" content_hash_code="102108802" os_list="windows" file_path="rlz/lib/financial_ping.cc"/>
  <item id="safe_browsing_backup_request" hash_code="106980485" type="0" deprecated="2018-08-14" content_hash_code="101760679" file_path=""/>
- <item id="safe_browsing_binary_upload" hash_code="71663319" type="0" content_hash_code="88524040" os_list="linux,windows" file_path="chrome/browser/safe_browsing/download_protection/binary_upload_service.cc"/>
+ <item id="safe_browsing_binary_upload" hash_code="71663319" type="0" content_hash_code="105913171" os_list="linux,windows" file_path="chrome/browser/safe_browsing/download_protection/binary_upload_service.cc"/>
  <item id="safe_browsing_cache_collector" hash_code="115907811" type="0" content_hash_code="36392362" os_list="linux,windows" file_path="components/safe_browsing/browser/threat_details_cache.cc"/>
  <item id="safe_browsing_certificate_error_reporting" hash_code="66590631" type="0" content_hash_code="50197576" os_list="linux,windows" file_path="chrome/browser/ssl/certificate_error_reporter.cc"/>
  <item id="safe_browsing_chunk_backup_request" hash_code="79957943" type="0" deprecated="2018-08-14" content_hash_code="133850277" file_path=""/>
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index ce3ee09..a0e8812d 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -5408,7 +5408,7 @@
       return L"group";
 
     case ax::mojom::Role::kFigcaption:
-      return L"group";
+      return L"description";
 
     case ax::mojom::Role::kFigure:
       return L"group";
@@ -6049,7 +6049,7 @@
       return UIA_GroupControlTypeId;
 
     case ax::mojom::Role::kFigcaption:
-      return UIA_GroupControlTypeId;
+      return UIA_TextControlTypeId;
 
     case ax::mojom::Role::kFigure:
       return UIA_GroupControlTypeId;
diff --git a/ui/android/java/src/org/chromium/ui/ContactsPickerListener.java b/ui/android/java/src/org/chromium/ui/ContactsPickerListener.java
index 6a463ef1..8237027 100644
--- a/ui/android/java/src/org/chromium/ui/ContactsPickerListener.java
+++ b/ui/android/java/src/org/chromium/ui/ContactsPickerListener.java
@@ -50,7 +50,9 @@
      * @param contacts The list of contacts selected.
      * @param percentageShared How big a percentage of the full contact list was shared (for metrics
      *         purposes).
+     * @param propertiesRequested The properties requested by the website (names, emails,
+     *         telephones).
      */
-    void onContactsPickerUserAction(
-            @ContactsPickerAction int action, List<Contact> contacts, int percentageShared);
+    void onContactsPickerUserAction(@ContactsPickerAction int action, List<Contact> contacts,
+            int percentageShared, int propertiesRequested);
 }
diff --git a/ui/base/clipboard/clipboard_format_type_win.cc b/ui/base/clipboard/clipboard_format_type_win.cc
index e5210b9e3..1e1c427 100644
--- a/ui/base/clipboard/clipboard_format_type_win.cc
+++ b/ui/base/clipboard/clipboard_format_type_win.cc
@@ -6,9 +6,9 @@
 
 #include <shlobj.h>
 
-#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 
@@ -66,101 +66,95 @@
 }
 
 // The following formats can be referenced by ClipboardUtilWin::GetPlainText.
-// For reasons (COM), they must be initialized in a thread-safe manner.
+// Clipboard formats are initialized in a thread-safe manner, using static
+// initialization. COM requires this thread-safe initialization.
 // TODO(dcheng): We probably need to make static initialization of "known"
 // ClipboardFormatTypes thread-safe on all platforms.
-#define CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(name, ...)                        \
-  struct ClipboardFormatTypeArgumentForwarder : public ClipboardFormatType { \
-    ClipboardFormatTypeArgumentForwarder()                                   \
-        : ClipboardFormatType(__VA_ARGS__) {}                                \
-  };                                                                         \
-  static base::LazyInstance<ClipboardFormatTypeArgumentForwarder>::Leaky     \
-      name = LAZY_INSTANCE_INITIALIZER
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetUrlType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(type,
-                                     ::RegisterClipboardFormat(CFSTR_INETURLA));
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(
+      ::RegisterClipboardFormat(CFSTR_INETURLA));
+  return *format;
 }
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetUrlWType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(type,
-                                     ::RegisterClipboardFormat(CFSTR_INETURLW));
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(
+      ::RegisterClipboardFormat(CFSTR_INETURLW));
+  return *format;
 }
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetMozUrlType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
-      type, ::RegisterClipboardFormat(L"text/x-moz-url"));
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(
+      ::RegisterClipboardFormat(L"text/x-moz-url"));
+  return *format;
 }
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetPlainTextType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(type, CF_TEXT);
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(CF_TEXT);
+  return *format;
 }
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetPlainTextWType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(type, CF_UNICODETEXT);
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(CF_UNICODETEXT);
+  return *format;
 }
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetFilenameType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
-      type, ::RegisterClipboardFormat(CFSTR_FILENAMEA));
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(
+      ::RegisterClipboardFormat(CFSTR_FILENAMEA));
+  return *format;
 }
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetFilenameWType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
-      type, ::RegisterClipboardFormat(CFSTR_FILENAMEW));
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(
+      ::RegisterClipboardFormat(CFSTR_FILENAMEW));
+  return *format;
 }
 
 // MS HTML Format
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetHtmlType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(type,
-                                     ::RegisterClipboardFormat(L"HTML Format"));
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(
+      ::RegisterClipboardFormat(L"HTML Format"));
+  return *format;
 }
 
 // MS RTF Format
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetRtfType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
-      type, ::RegisterClipboardFormat(L"Rich Text Format"));
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(
+      ::RegisterClipboardFormat(L"Rich Text Format"));
+  return *format;
 }
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetBitmapType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(type, CF_BITMAP);
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(CF_BITMAP);
+  return *format;
 }
 
 // Firefox text/html
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetTextHtmlType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(type,
-                                     ::RegisterClipboardFormat(L"text/html"));
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(
+      ::RegisterClipboardFormat(L"text/html"));
+  return *format;
 }
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetCFHDropType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(type, CF_HDROP);
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(CF_HDROP);
+  return *format;
 }
 
 // Nothing prevents the drag source app from using the CFSTR_FILEDESCRIPTORA
@@ -168,16 +162,16 @@
 // register both the ANSI and Unicode file group descriptors.
 // static
 const ClipboardFormatType& ClipboardFormatType::GetFileDescriptorType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
-      type, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA));
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(
+      ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA));
+  return *format;
 }
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetFileDescriptorWType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
-      type, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW));
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(
+      ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW));
+  return *format;
 }
 
 // static
@@ -189,9 +183,9 @@
   // TODO(https://crbug.com/950756): Should TYMED_ISTREAM / TYMED_ISTORAGE be
   // used instead of TYMED_HGLOBAL in
   // OSExchangeDataProviderWin::SetFileContents.
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
-      type, ::RegisterClipboardFormat(CFSTR_FILECONTENTS), 0);
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(
+      ::RegisterClipboardFormat(CFSTR_FILECONTENTS));
+  return *format;
 }
 
 // static
@@ -216,32 +210,31 @@
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetIDListType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
-      type, ::RegisterClipboardFormat(CFSTR_SHELLIDLIST));
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(
+      ::RegisterClipboardFormat(CFSTR_SHELLIDLIST));
+  return *format;
 }
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetWebKitSmartPasteType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
-      type, ::RegisterClipboardFormat(L"WebKit Smart Paste Format"));
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(
+      ::RegisterClipboardFormat(L"WebKit Smart Paste Format"));
+  return *format;
 }
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetWebCustomDataType() {
-  // TODO(dcheng): This name is temporary. See http://crbug.com/106449.
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
-      type, ::RegisterClipboardFormat(L"Chromium Web Custom MIME Data Format"));
-  return type.Get();
+  // TODO(http://crbug.com/106449): Standardize this name.
+  static base::NoDestructor<ClipboardFormatType> format(
+      ::RegisterClipboardFormat(L"Chromium Web Custom MIME Data Format"));
+  return *format;
 }
 
 // static
 const ClipboardFormatType& ClipboardFormatType::GetPepperCustomDataType() {
-  CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
-      type, ::RegisterClipboardFormat(L"Chromium Pepper MIME Data Format"));
-  return type.Get();
+  static base::NoDestructor<ClipboardFormatType> format(
+      ::RegisterClipboardFormat(L"Chromium Pepper MIME Data Format"));
+  return *format;
 }
-#undef CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE
 
 }  // namespace ui
diff --git a/ui/base/dragdrop/os_exchange_data_win_unittest.cc b/ui/base/dragdrop/os_exchange_data_win_unittest.cc
index d2f1fd76..3a595ce 100644
--- a/ui/base/dragdrop/os_exchange_data_win_unittest.cc
+++ b/ui/base/dragdrop/os_exchange_data_win_unittest.cc
@@ -312,8 +312,8 @@
   {
     CLIPFORMAT cfstr_file_contents =
         RegisterClipboardFormat(CFSTR_FILECONTENTS);
-    FORMATETC format_etc =
-        { cfstr_file_contents, NULL, DVASPECT_CONTENT, 0, TYMED_HGLOBAL };
+    FORMATETC format_etc = {cfstr_file_contents, nullptr, DVASPECT_CONTENT, -1,
+                            TYMED_HGLOBAL};
     EXPECT_EQ(S_OK, com_data->QueryGetData(&format_etc));
 
     STGMEDIUM medium;
diff --git a/ui/base/win/window_event_target.h b/ui/base/win/window_event_target.h
index 0d3e9d3..b31cae0 100644
--- a/ui/base/win/window_event_target.h
+++ b/ui/base/win/window_event_target.h
@@ -114,8 +114,11 @@
   // to ApplyPanGestureScroll(), ApplyPanGestureScrollEnd(),
   // ApplyPanGestureFlingBegin(), any number of calls to ApplyPanGestureFling(),
   // and finally ApplyPanGestureFlingEnd().
+  // |transition_to_pinch| is a hint to know if the scroll end will be followed
+  // by a pinch begin or not, so that momentum_phase can be set to Blocked if
+  // a momentum scroll/fling will not be happening next.
   virtual void ApplyPanGestureScrollBegin(int scroll_x, int scroll_y) = 0;
-  virtual void ApplyPanGestureScrollEnd() = 0;
+  virtual void ApplyPanGestureScrollEnd(bool transition_to_pinch) = 0;
   virtual void ApplyPanGestureFlingBegin() = 0;
   virtual void ApplyPanGestureFlingEnd() = 0;
 
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn
index e04a07b..291ff3c 100644
--- a/ui/events/BUILD.gn
+++ b/ui/events/BUILD.gn
@@ -145,6 +145,8 @@
     "keycodes/keyboard_codes.h",
     "mobile_scroller.cc",
     "mobile_scroller.h",
+    "pointer_details.cc",
+    "pointer_details.h",
   ]
 
   defines = [ "EVENTS_BASE_IMPLEMENTATION" ]
diff --git a/ui/events/blink/web_input_event.cc b/ui/events/blink/web_input_event.cc
index 4b1a948..c43e4dc5 100644
--- a/ui/events/blink/web_input_event.cc
+++ b/ui/events/blink/web_input_event.cc
@@ -174,6 +174,9 @@
     case ui::EventMomentumPhase::END:
       webkit_event.momentum_phase = blink::WebMouseWheelEvent::kPhaseEnded;
       break;
+    case ui::EventMomentumPhase::BLOCKED:
+      webkit_event.momentum_phase = blink::WebMouseWheelEvent::kPhaseBlocked;
+      break;
     default:
       NOTREACHED();
   }
diff --git a/ui/events/event.cc b/ui/events/event.cc
index 15252f61..9f1bb86c 100644
--- a/ui/events/event.cc
+++ b/ui/events/event.cc
@@ -134,6 +134,8 @@
       return "INERTIAL_UPDATE";
     case EventMomentumPhase::END:
       return "END";
+    case EventMomentumPhase::BLOCKED:
+      return "BLOCKED";
   }
 }
 
@@ -462,57 +464,6 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// PointerDetails
-
-PointerDetails::PointerDetails() {}
-
-PointerDetails::PointerDetails(EventPointerType pointer_type,
-                               PointerId pointer_id)
-    : PointerDetails(pointer_type,
-                     pointer_id,
-                     /* radius_x */ 0.0f,
-                     /* radius_y */ 0.0f,
-                     /* force */ std::numeric_limits<float>::quiet_NaN()) {}
-
-PointerDetails::PointerDetails(EventPointerType pointer_type,
-                               PointerId pointer_id,
-                               float radius_x,
-                               float radius_y,
-                               float force,
-                               float twist,
-                               float tilt_x,
-                               float tilt_y,
-                               float tangential_pressure)
-    : pointer_type(pointer_type),
-      // If we aren't provided with a radius on one axis, use the
-      // information from the other axis.
-      radius_x(radius_x > 0 ? radius_x : radius_y),
-      radius_y(radius_y > 0 ? radius_y : radius_x),
-      force(force),
-      tilt_x(tilt_x),
-      tilt_y(tilt_y),
-      tangential_pressure(tangential_pressure),
-      twist(twist),
-      id(pointer_id) {
-  if (pointer_id == PointerDetails::kUnknownPointerId) {
-    id = pointer_type == EventPointerType::POINTER_TYPE_MOUSE
-             ? MouseEvent::kMousePointerId
-             : 0;
-  }
-}
-
-PointerDetails::PointerDetails(EventPointerType pointer_type,
-                               const gfx::Vector2d& pointer_offset,
-                               PointerId pointer_id)
-    : PointerDetails(pointer_type, pointer_id) {
-  offset = pointer_offset;
-}
-
-PointerDetails::PointerDetails(const PointerDetails& other) = default;
-
-const PointerId PointerDetails::kUnknownPointerId = -1;
-
-////////////////////////////////////////////////////////////////////////////////
 // MouseEvent
 
 MouseEvent::MouseEvent(const PlatformEvent& native_event)
@@ -676,9 +627,6 @@
   set_flags(f);
 }
 
-const PointerId MouseEvent::kMousePointerId =
-    std::numeric_limits<PointerId>::max();
-
 ////////////////////////////////////////////////////////////////////////////////
 // MouseWheelEvent
 
diff --git a/ui/events/event.h b/ui/events/event.h
index 7e54838..0e260406 100644
--- a/ui/events/event.h
+++ b/ui/events/event.h
@@ -24,6 +24,7 @@
 #include "ui/events/keycodes/dom/dom_key.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/events/platform_event.h"
+#include "ui/events/pointer_details.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/latency/latency_info.h"
@@ -45,8 +46,6 @@
 
 enum class DomCode;
 
-using PointerId = int32_t;
-
 class EVENTS_EXPORT Event {
  public:
   using Properties = base::flat_map<std::string, std::vector<uint8_t>>;
@@ -432,85 +431,8 @@
   gfx::PointF root_location_;
 };
 
-// Structure for handling common fields between touch and mouse to support
-// PointerEvents API.
-struct EVENTS_EXPORT PointerDetails {
- public:
-  PointerDetails();
-  explicit PointerDetails(EventPointerType pointer_type,
-                          PointerId pointer_id = kUnknownPointerId);
-  PointerDetails(EventPointerType pointer_type,
-                 PointerId pointer_id,
-                 float radius_x,
-                 float radius_y,
-                 float force,
-                 float twist = 0.0f,
-                 float tilt_x = 0.0f,
-                 float tilt_y = 0.0f,
-                 float tangential_pressure = 0.0f);
-  PointerDetails(EventPointerType pointer_type,
-                 const gfx::Vector2d& pointer_offset,
-                 PointerId pointer_id = kUnknownPointerId);
-  PointerDetails(const PointerDetails& other);
-
-  bool operator==(const PointerDetails& other) const {
-    return pointer_type == other.pointer_type && radius_x == other.radius_x &&
-           radius_y == other.radius_y &&
-           (force == other.force ||
-            (std::isnan(force) && std::isnan(other.force))) &&
-           tilt_x == other.tilt_x && tilt_y == other.tilt_y &&
-           tangential_pressure == other.tangential_pressure &&
-           twist == other.twist && id == other.id && offset == other.offset;
-  }
-
-  // A value for pointer id which means it needs to be initialized for all
-  // pointer types.
-  static const PointerId kUnknownPointerId;
-
-  // The type of pointer device.
-  EventPointerType pointer_type = EventPointerType::POINTER_TYPE_UNKNOWN;
-
-  // Radius of the X (major) axis of the touch ellipse. 0.0 if unknown.
-  float radius_x = 0.0;
-
-  // Radius of the Y (minor) axis of the touch ellipse. 0.0 if unknown.
-  float radius_y = 0.0;
-
-  // Force (pressure) of the touch. Normalized to be [0, 1] except NaN means
-  // pressure is not supported by the input device.
-  float force = 0.0;
-
-  // Tilt of a pen/stylus from surface normal as plane angle in degrees, values
-  // lie in [-90,90]. A positive tilt_x is to the right and a positive tilt_y
-  // is towards the user. 0.0 if unknown.
-  float tilt_x = 0.0;
-  float tilt_y = 0.0;
-
-  // The normalized tangential pressure (or barrel pressure), typically set by
-  // an additional control of the stylus, which has a range of [-1,1], where 0
-  // is the neutral position of the control. Always 0 if the device does not
-  // support it.
-  float tangential_pressure = 0.0;
-
-  // The clockwise rotation of a pen stylus around its own major axis, in
-  // degrees in the range [0,359]. Always 0 if the device does not support it.
-  float twist = 0;
-
-  // An identifier that uniquely identifies a pointer during its lifetime.
-  PointerId id = 0;
-
-  // Only used by mouse wheel events. The amount to scroll. This is in multiples
-  // of kWheelDelta.
-  // Note: offset_.x() > 0/offset_.y() > 0 means scroll left/up.
-  gfx::Vector2d offset;
-
-  // If you add fields please update ui/events/mojom/event.mojom.
-};
-
 class EVENTS_EXPORT MouseEvent : public LocatedEvent {
  public:
-  static const PointerId kMousePointerId;
-
   // NOTE: On some platforms this will allow an event to be constructed from a
   // void*, see PlatformEvent.
   explicit MouseEvent(const PlatformEvent& native_event);
@@ -547,7 +469,7 @@
              int changed_button_flags,
              const PointerDetails& pointer_details =
                  PointerDetails(EventPointerType::POINTER_TYPE_MOUSE,
-                                kMousePointerId));
+                                kPointerIdMouse));
 
   // DEPRECATED: Prefer constructor that takes gfx::PointF.
   MouseEvent(EventType type,
@@ -558,7 +480,7 @@
              int changed_button_flags,
              const PointerDetails& pointer_details =
                  PointerDetails(EventPointerType::POINTER_TYPE_MOUSE,
-                                kMousePointerId));
+                                kPointerIdMouse));
 
   MouseEvent(const MouseEvent& copy);
   ~MouseEvent() override;
diff --git a/ui/events/event_constants.h b/ui/events/event_constants.h
index cab62faf..09eab5f 100644
--- a/ui/events/event_constants.h
+++ b/ui/events/event_constants.h
@@ -220,6 +220,13 @@
   // for events that are not a "stream", but indicate both the start and end of
   // the event (e.g. a mouse wheel tick).
   END,
+
+  // EventMomentumPhase can only be BLOCKED when ScrollEventPhase is kEnd. Event
+  // marks the end of the current event stream, when there will be no inertia
+  // scrolling after the user gesture. ScrollEventPhase must simultaneously be
+  // kEnd because that is when it is determined if an event stream that results
+  // in momentum will begin or not. This phase is only used on Windows.
+  BLOCKED,
 };
 
 // Device ID for Touch and Key Events.
diff --git a/ui/events/events_default.cc b/ui/events/events_default.cc
index f293ed6..0f52e53 100644
--- a/ui/events/events_default.cc
+++ b/ui/events/events_default.cc
@@ -52,7 +52,7 @@
       static_cast<const ui::MouseEvent*>(native_event);
   DCHECK(event->IsMouseEvent() || event->IsScrollEvent());
   PointerDetails pointer_detail = event->pointer_details();
-  pointer_detail.id = MouseEvent::kMousePointerId;
+  pointer_detail.id = kPointerIdMouse;
   return pointer_detail;
 }
 
diff --git a/ui/events/mojom/event_constants.mojom b/ui/events/mojom/event_constants.mojom
index 103d9b34..4470dfe 100644
--- a/ui/events/mojom/event_constants.mojom
+++ b/ui/events/mojom/event_constants.mojom
@@ -93,6 +93,7 @@
   MAY_BEGIN,
   INERTIAL_UPDATE,
   END,
+  BLOCKED,
 };
 
 // Device type for gesture events.
diff --git a/ui/events/mojom/event_mojom_traits.h b/ui/events/mojom/event_mojom_traits.h
index bca5fe1..63db803 100644
--- a/ui/events/mojom/event_mojom_traits.h
+++ b/ui/events/mojom/event_mojom_traits.h
@@ -65,6 +65,8 @@
         return ui::mojom::EventMomentumPhase::INERTIAL_UPDATE;
       case ui::EventMomentumPhase::END:
         return ui::mojom::EventMomentumPhase::END;
+      case ui::EventMomentumPhase::BLOCKED:
+        return ui::mojom::EventMomentumPhase::BLOCKED;
     }
     NOTREACHED();
     return ui::mojom::EventMomentumPhase::NONE;
@@ -88,6 +90,9 @@
       case ui::mojom::EventMomentumPhase::END:
         *out = ui::EventMomentumPhase::END;
         return true;
+      case ui::mojom::EventMomentumPhase::BLOCKED:
+        *out = ui::EventMomentumPhase::BLOCKED;
+        return true;
     }
     NOTREACHED();
     return false;
diff --git a/ui/events/mojom/mojom_traits_unittest.cc b/ui/events/mojom/mojom_traits_unittest.cc
index f83d07d0..adab8ca79 100644
--- a/ui/events/mojom/mojom_traits_unittest.cc
+++ b/ui/events/mojom/mojom_traits_unittest.cc
@@ -122,37 +122,30 @@
   const MouseEvent kTestData[] = {
       {ET_MOUSE_PRESSED, gfx::Point(10, 10), gfx::Point(20, 30),
        base::TimeTicks() + base::TimeDelta::FromMicroseconds(201), EF_NONE, 0,
-       PointerDetails(EventPointerType::POINTER_TYPE_MOUSE,
-                      MouseEvent::kMousePointerId)},
+       PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, kPointerIdMouse)},
       {ET_MOUSE_DRAGGED, gfx::Point(1, 5), gfx::Point(5, 1),
        base::TimeTicks() + base::TimeDelta::FromMicroseconds(202),
        EF_LEFT_MOUSE_BUTTON, EF_LEFT_MOUSE_BUTTON,
-       PointerDetails(EventPointerType::POINTER_TYPE_MOUSE,
-                      MouseEvent::kMousePointerId)},
+       PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, kPointerIdMouse)},
       {ET_MOUSE_RELEASED, gfx::Point(411, 130), gfx::Point(20, 30),
        base::TimeTicks() + base::TimeDelta::FromMicroseconds(203),
        EF_MIDDLE_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON, EF_RIGHT_MOUSE_BUTTON,
-       PointerDetails(EventPointerType::POINTER_TYPE_MOUSE,
-                      MouseEvent::kMousePointerId)},
+       PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, kPointerIdMouse)},
       {ET_MOUSE_MOVED, gfx::Point(0, 1), gfx::Point(2, 3),
        base::TimeTicks() + base::TimeDelta::FromMicroseconds(204), EF_ALT_DOWN,
        0,
-       PointerDetails(EventPointerType::POINTER_TYPE_MOUSE,
-                      MouseEvent::kMousePointerId)},
+       PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, kPointerIdMouse)},
       {ET_MOUSE_ENTERED, gfx::Point(6, 7), gfx::Point(8, 9),
        base::TimeTicks() + base::TimeDelta::FromMicroseconds(205), EF_NONE, 0,
-       PointerDetails(EventPointerType::POINTER_TYPE_MOUSE,
-                      MouseEvent::kMousePointerId)},
+       PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, kPointerIdMouse)},
       {ET_MOUSE_EXITED, gfx::Point(10, 10), gfx::Point(20, 30),
        base::TimeTicks() + base::TimeDelta::FromMicroseconds(206),
        EF_BACK_MOUSE_BUTTON, 0,
-       PointerDetails(EventPointerType::POINTER_TYPE_MOUSE,
-                      MouseEvent::kMousePointerId)},
+       PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, kPointerIdMouse)},
       {ET_MOUSE_CAPTURE_CHANGED, gfx::Point(99, 99), gfx::Point(99, 99),
        base::TimeTicks() + base::TimeDelta::FromMicroseconds(207),
        EF_CONTROL_DOWN, 0,
-       PointerDetails(EventPointerType::POINTER_TYPE_MOUSE,
-                      MouseEvent::kMousePointerId)},
+       PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, kPointerIdMouse)},
   };
 
   for (size_t i = 0; i < base::size(kTestData); i++) {
diff --git a/ui/events/platform/x11/x11_event_source_default.cc b/ui/events/platform/x11/x11_event_source_default.cc
index ce3c7a9..7e8aa51c 100644
--- a/ui/events/platform/x11/x11_event_source_default.cc
+++ b/ui/events/platform/x11/x11_event_source_default.cc
@@ -26,10 +26,7 @@
                                              const XEvent& xev) {
   std::unique_ptr<TouchEvent> event = std::make_unique<TouchEvent>(
       event_type, EventLocationFromXEvent(xev), EventTimeFromXEvent(xev),
-      ui::PointerDetails(
-          GetTouchPointerTypeFromXEvent(xev), GetTouchIdFromXEvent(xev),
-          GetTouchRadiusXFromXEvent(xev), GetTouchRadiusYFromXEvent(xev),
-          GetTouchForceFromXEvent(xev)));
+      GetTouchPointerDetailsFromXEvent(xev));
 
   // Touch events don't usually have |root_location| set differently than
   // |location|, since there is a touch device to display association, but this
diff --git a/ui/events/pointer_details.cc b/ui/events/pointer_details.cc
new file mode 100644
index 0000000..e2dedfd
--- /dev/null
+++ b/ui/events/pointer_details.cc
@@ -0,0 +1,60 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/events/pointer_details.h"
+
+#include <cmath>
+
+namespace ui {
+
+PointerDetails::PointerDetails() {}
+
+PointerDetails::PointerDetails(EventPointerType pointer_type,
+                               PointerId pointer_id)
+    : PointerDetails(pointer_type,
+                     pointer_id,
+                     /* radius_x */ 0.0f,
+                     /* radius_y */ 0.0f,
+                     /* force */ std::numeric_limits<float>::quiet_NaN()) {}
+
+PointerDetails::PointerDetails(EventPointerType pointer_type,
+                               PointerId pointer_id,
+                               float radius_x,
+                               float radius_y,
+                               float force,
+                               float twist,
+                               float tilt_x,
+                               float tilt_y,
+                               float tangential_pressure)
+    : pointer_type(pointer_type),
+      // If we aren't provided with a radius on one axis, use the
+      // information from the other axis.
+      radius_x(radius_x > 0 ? radius_x : radius_y),
+      radius_y(radius_y > 0 ? radius_y : radius_x),
+      force(force),
+      tilt_x(tilt_x),
+      tilt_y(tilt_y),
+      tangential_pressure(tangential_pressure),
+      twist(twist),
+      id(pointer_id) {
+  if (pointer_id == kPointerIdUnknown) {
+    id = (pointer_type == EventPointerType::POINTER_TYPE_MOUSE)
+             ? kPointerIdMouse
+             : 0;
+  }
+}
+
+PointerDetails::PointerDetails(const PointerDetails& other) = default;
+
+bool PointerDetails::operator==(const PointerDetails& other) const {
+  return pointer_type == other.pointer_type && radius_x == other.radius_x &&
+         radius_y == other.radius_y &&
+         (force == other.force ||
+          (std::isnan(force) && std::isnan(other.force))) &&
+         tilt_x == other.tilt_x && tilt_y == other.tilt_y &&
+         tangential_pressure == other.tangential_pressure &&
+         twist == other.twist && id == other.id && offset == other.offset;
+}
+
+}  // namespace ui
diff --git a/ui/events/pointer_details.h b/ui/events/pointer_details.h
new file mode 100644
index 0000000..f274a81
--- /dev/null
+++ b/ui/events/pointer_details.h
@@ -0,0 +1,89 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_EVENTS_POINTER_DETAILS_H_
+#define UI_EVENTS_POINTER_DETAILS_H_
+
+#include <stdint.h>
+
+#include <limits>
+
+#include "ui/events/event_constants.h"
+#include "ui/events/events_base_export.h"
+#include "ui/gfx/geometry/vector2d.h"
+
+namespace ui {
+
+// Pointer ID type.
+using PointerId = int32_t;
+
+// Pointer id which means it needs to be initialized for all pointer types.
+static constexpr PointerId kPointerIdUnknown = -1;
+// Pointer id to be used by Mouse events.
+static constexpr PointerId kPointerIdMouse =
+    std::numeric_limits<int32_t>::max();
+
+// Structure for handling common fields between touch and mouse to support
+// PointerEvents API.
+struct EVENTS_BASE_EXPORT PointerDetails {
+ public:
+  PointerDetails();
+  explicit PointerDetails(EventPointerType pointer_type,
+                          PointerId pointer_id = kPointerIdUnknown);
+  PointerDetails(EventPointerType pointer_type,
+                 PointerId pointer_id,
+                 float radius_x,
+                 float radius_y,
+                 float force,
+                 float twist = 0.0f,
+                 float tilt_x = 0.0f,
+                 float tilt_y = 0.0f,
+                 float tangential_pressure = 0.0f);
+  PointerDetails(const PointerDetails& other);
+
+  bool operator==(const PointerDetails& other) const;
+
+  // The type of pointer device.
+  EventPointerType pointer_type = EventPointerType::POINTER_TYPE_UNKNOWN;
+
+  // Radius of the X (major) axis of the touch ellipse. 0.0 if unknown.
+  float radius_x = 0.0;
+
+  // Radius of the Y (minor) axis of the touch ellipse. 0.0 if unknown.
+  float radius_y = 0.0;
+
+  // Force (pressure) of the touch. Normalized to be [0, 1] except NaN means
+  // pressure is not supported by the input device.
+  float force = 0.0;
+
+  // Tilt of a pen/stylus from surface normal as plane angle in degrees, values
+  // lie in [-90,90]. A positive tilt_x is to the right and a positive tilt_y
+  // is towards the user. 0.0 if unknown.
+  float tilt_x = 0.0;
+  float tilt_y = 0.0;
+
+  // The normalized tangential pressure (or barrel pressure), typically set by
+  // an additional control of the stylus, which has a range of [-1,1], where 0
+  // is the neutral position of the control. Always 0 if the device does not
+  // support it.
+  float tangential_pressure = 0.0;
+
+  // The clockwise rotation of a pen stylus around its own major axis, in
+  // degrees in the range [0,359]. Always 0 if the device does not support it.
+  float twist = 0;
+
+  // An identifier that uniquely identifies a pointer during its lifetime.
+  PointerId id = 0;
+
+  // Only used by mouse wheel events. The amount to scroll. This is in multiples
+  // of kWheelDelta.
+  // Note: offset_.x() > 0/offset_.y() > 0 means scroll left/up.
+  gfx::Vector2d offset;
+
+  // If you add fields please update ui/events/mojom/event.mojom.
+};
+
+}  // namespace ui
+
+#endif  // UI_EVENTS_POINTER_DETAILS_H_
diff --git a/ui/events/x/events_x.cc b/ui/events/x/events_x.cc
index 36ed8f1..0f79570 100644
--- a/ui/events/x/events_x.cc
+++ b/ui/events/x/events_x.cc
@@ -145,12 +145,7 @@
 
 PointerDetails GetTouchPointerDetailsFromNative(
     const PlatformEvent& native_event) {
-  return PointerDetails(EventPointerType::POINTER_TYPE_TOUCH,
-                        GetTouchIdFromXEvent(*native_event),
-                        GetTouchRadiusXFromXEvent(*native_event),
-                        GetTouchRadiusYFromXEvent(*native_event),
-                        GetTouchForceFromXEvent(*native_event),
-                        GetTouchAngleFromXEvent(*native_event));
+  return GetTouchPointerDetailsFromXEvent(*native_event);
 }
 
 bool GetScrollOffsets(const PlatformEvent& native_event,
diff --git a/ui/events/x/events_x_utils.cc b/ui/events/x/events_x_utils.cc
index 86fbd19..7137c10 100644
--- a/ui/events/x/events_x_utils.cc
+++ b/ui/events/x/events_x_utils.cc
@@ -750,6 +750,13 @@
       event->sourceid);
 }
 
+PointerDetails GetTouchPointerDetailsFromXEvent(const XEvent& xev) {
+  return PointerDetails(
+      EventPointerType::POINTER_TYPE_TOUCH, GetTouchIdFromXEvent(xev),
+      GetTouchRadiusXFromXEvent(xev), GetTouchRadiusYFromXEvent(xev),
+      GetTouchForceFromXEvent(xev), GetTouchAngleFromXEvent(xev));
+}
+
 bool GetScrollOffsetsFromXEvent(const XEvent& xev,
                                 float* x_offset,
                                 float* y_offset,
diff --git a/ui/events/x/events_x_utils.h b/ui/events/x/events_x_utils.h
index 64814e87..0cdc3a3c 100644
--- a/ui/events/x/events_x_utils.h
+++ b/ui/events/x/events_x_utils.h
@@ -12,6 +12,7 @@
 #include "base/time/tick_clock.h"
 #include "base/time/time.h"
 #include "ui/events/event_constants.h"
+#include "ui/events/pointer_details.h"
 #include "ui/events/x/events_x_export.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/x/x11_types.h"
@@ -68,6 +69,10 @@
 EVENTS_X_EXPORT EventPointerType
 GetTouchPointerTypeFromXEvent(const XEvent& xev);
 
+// Gets the pointer details from an XEvent.
+EVENTS_X_EXPORT PointerDetails
+GetTouchPointerDetailsFromXEvent(const XEvent& xev);
+
 // Returns whether this is a scroll event and optionally gets the amount to be
 // scrolled. |x_offset|, |y_offset| and |finger_count| can be NULL.
 EVENTS_X_EXPORT bool GetScrollOffsetsFromXEvent(const XEvent& xev,
diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc
index 2370d15..929e339 100644
--- a/ui/native_theme/native_theme.cc
+++ b/ui/native_theme/native_theme.cc
@@ -102,7 +102,9 @@
   set_use_dark_colors(is_dark_mode);
   set_high_contrast(is_high_contrast);
   set_preferred_color_scheme(preferred_color_scheme);
-  system_colors_.insert(colors.begin(), colors.end());
+  for (const auto& color : colors) {
+    system_colors_[color.first] = color.second;
+  }
 }
 
 NativeTheme::ColorSchemeNativeThemeObserver::ColorSchemeNativeThemeObserver(
diff --git a/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc b/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
index fbabdaf..7ce490e 100644
--- a/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
+++ b/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
@@ -66,17 +66,7 @@
   std::vector<const char*> required_layers = {
       "VK_LAYER_FUCHSIA_imagepipe_swapchain",
   };
-  if (!vulkan_instance_.Initialize(required_extensions, required_layers))
-    return false;
-
-  vkCreateImagePipeSurfaceFUCHSIA_ =
-      reinterpret_cast<PFN_vkCreateImagePipeSurfaceFUCHSIA>(
-          vkGetInstanceProcAddr(vulkan_instance_.vk_instance(),
-                                "vkCreateImagePipeSurfaceFUCHSIA"));
-  if (!vkCreateImagePipeSurfaceFUCHSIA_)
-    return false;
-
-  return true;
+  return vulkan_instance_.Initialize(required_extensions, required_layers);
 }
 
 gpu::VulkanInstance* VulkanImplementationScenic::GetVulkanInstance() {
@@ -97,7 +87,7 @@
   surface_create_info.imagePipeHandle =
       image_pipe.Unbind().TakeChannel().release();
 
-  VkResult result = vkCreateImagePipeSurfaceFUCHSIA_(
+  VkResult result = vkCreateImagePipeSurfaceFUCHSIA(
       vulkan_instance_.vk_instance(), &surface_create_info, nullptr, &surface);
   if (result != VK_SUCCESS) {
     // This shouldn't fail, and we don't know whether imagePipeHandle was closed
diff --git a/ui/ozone/platform/scenic/vulkan_implementation_scenic.h b/ui/ozone/platform/scenic/vulkan_implementation_scenic.h
index 14285fc..8b077b8 100644
--- a/ui/ozone/platform/scenic/vulkan_implementation_scenic.h
+++ b/ui/ozone/platform/scenic/vulkan_implementation_scenic.h
@@ -67,9 +67,6 @@
 
   gpu::VulkanInstance vulkan_instance_;
 
-  PFN_vkCreateImagePipeSurfaceFUCHSIA vkCreateImagePipeSurfaceFUCHSIA_ =
-      nullptr;
-
   DISALLOW_COPY_AND_ASSIGN(VulkanImplementationScenic);
 };
 
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn
index 770bd20..5d48ba8 100644
--- a/ui/ozone/platform/wayland/BUILD.gn
+++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -292,13 +292,15 @@
 fuzzer_test("wayland_buffer_fuzzer") {
   defines = [ "WL_HIDE_DEPRECATED" ]
   sources = [
-    "wayland_buffer_fuzzer.cc",
+    "fuzzer/wayland_buffer_fuzzer.cc",
   ]
   deps = [
     ":test_support",
     ":wayland",
     "//base/test:test_support",
     "//build/config/linux/libdrm",
+    "//mojo/core/embedder",
+    "//mojo/public/cpp/system",
     "//ui/gfx:test_support",
     "//ui/platform_window:platform_window",
   ]
diff --git a/ui/ozone/platform/wayland/fuzzer/DEPS b/ui/ozone/platform/wayland/fuzzer/DEPS
new file mode 100644
index 0000000..9243dcd6
--- /dev/null
+++ b/ui/ozone/platform/wayland/fuzzer/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+mojo/core/embedder",
+]
diff --git a/ui/ozone/platform/wayland/wayland_buffer_fuzzer.cc b/ui/ozone/platform/wayland/fuzzer/wayland_buffer_fuzzer.cc
similarity index 80%
rename from ui/ozone/platform/wayland/wayland_buffer_fuzzer.cc
rename to ui/ozone/platform/wayland/fuzzer/wayland_buffer_fuzzer.cc
index 463b773..2254853 100644
--- a/ui/ozone/platform/wayland/wayland_buffer_fuzzer.cc
+++ b/ui/ozone/platform/wayland/fuzzer/wayland_buffer_fuzzer.cc
@@ -11,14 +11,19 @@
 #include <memory>
 #include <vector>
 
+#include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/message_loop/message_pump_type.h"
 #include "base/task/single_thread_task_executor.h"
+#include "mojo/core/embedder/embedder.h"
+#include "mojo/public/cpp/system/platform_handle.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/libFuzzer/src/utils/FuzzedDataProvider.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
 #include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_output_manager.h"
 #include "ui/ozone/platform/wayland/host/wayland_window.h"
 #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
 #include "ui/platform_window/platform_window_delegate.h"
@@ -40,7 +45,7 @@
   MOCK_METHOD1(DispatchEvent, void(ui::Event* event));
   MOCK_METHOD0(OnCloseRequest, void());
   MOCK_METHOD0(OnClosed, void());
-  MOCK_METHOD1(OnWindowStateChanged, void(PlatformWindowState new_state));
+  MOCK_METHOD1(OnWindowStateChanged, void(ui::PlatformWindowState new_state));
   MOCK_METHOD0(OnLostCapture, void());
   MOCK_METHOD1(OnAcceleratedWidgetAvailable,
                void(gfx::AcceleratedWidget widget));
@@ -56,6 +61,9 @@
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   FuzzedDataProvider data_provider(data, size);
 
+  mojo::core::Init();
+  base::CommandLine::Init(0, nullptr);
+
   std::vector<uint32_t> known_fourccs{
       DRM_FORMAT_R8,          DRM_FORMAT_GR88,        DRM_FORMAT_ABGR8888,
       DRM_FORMAT_XBGR8888,    DRM_FORMAT_ARGB8888,    DRM_FORMAT_XRGB8888,
@@ -64,17 +72,21 @@
 
   base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
 
-  MockPlatformWindowDelegate delegate;
+  wl::TestWaylandServerThread server;
+  CHECK(server.Start(6));
+
   std::unique_ptr<ui::WaylandConnection> connection =
       std::make_unique<ui::WaylandConnection>();
+  CHECK(connection->Initialize());
+
+  auto screen = connection->wayland_output_manager()->CreateWaylandScreen(
+      connection.get());
+
+  MockPlatformWindowDelegate delegate;
   std::unique_ptr<ui::WaylandWindow> window =
       std::make_unique<ui::WaylandWindow>(&delegate, connection.get());
   gfx::AcceleratedWidget widget = gfx::kNullAcceleratedWidget;
 
-  wl::TestWaylandServerThread server;
-  CHECK(server.Start(6));
-  CHECK(connection->Initialize());
-
   EXPECT_CALL(delegate, OnAcceleratedWidgetAvailable(_))
       .WillOnce(testing::SaveArg<0>(&widget));
   ui::PlatformWindowInitProperties properties;
@@ -86,14 +98,14 @@
   // Wait until everything is initialised.
   base::RunLoop().RunUntilIdle();
 
-  base::FilePath temp_path;
-  EXPECT_TRUE(base::CreateTemporaryFile(&temp_path));
-  base::File temp(temp_path,
-                  base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS);
+  base::FilePath temp_dir, temp_path;
+  base::ScopedFD fd =
+      base::CreateAndOpenFdForTemporaryFileInDir(temp_dir, &temp_path);
+  EXPECT_TRUE(fd.is_valid());
 
   // 10K screens are reality these days.
-  const uint32_t kWidth = data_provider.ConsumeIntegralInRange(1U, 20000U);
-  const uint32_t kHeight = data_provider.ConsumeIntegralInRange(1U, 20000U);
+  const gfx::Size buffer_size(data_provider.ConsumeIntegralInRange(1U, 20000U),
+                              data_provider.ConsumeIntegralInRange(1U, 20000U));
   // The buffer manager opens a file descriptor for each plane so |plane_count|
   // cannot be really large.  Technically, the maximum is |ulimit| minus number
   // of file descriptors opened by this process already (which is 17 at the time
@@ -118,15 +130,16 @@
   const uint32_t kBufferId = 1;
 
   EXPECT_CALL(*server.zwp_linux_dmabuf_v1(), CreateParams(_, _, _));
-
-  connection->CreateZwpLinuxDmabuf(std::move(temp), kWidth, kHeight, strides,
-                                   offsets, kFormat, modifiers, kPlaneCount,
-                                   kBufferId);
+  auto* manager_host = connection->buffer_manager_host();
+  manager_host->CreateDmabufBasedBuffer(
+      widget, mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(fd))),
+      buffer_size, strides, offsets, modifiers, kFormat, kPlaneCount,
+      kBufferId);
 
   // Wait until the buffers are created.
   base::RunLoop().RunUntilIdle();
 
-  connection->DestroyZwpLinuxDmabuf(kBufferId);
+  manager_host->DestroyBuffer(widget, kBufferId);
 
   // Wait until the buffers are destroyed.
   base::RunLoop().RunUntilIdle();
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 2532669..8cde33a 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -1247,11 +1247,13 @@
                        ui::ScrollEventPhase::kBegan);
 }
 
-void HWNDMessageHandler::ApplyPanGestureScrollEnd() {
+void HWNDMessageHandler::ApplyPanGestureScrollEnd(bool transitioning_to_pinch) {
   if (!precision_touchpad_scroll_phase_enabled_)
     return;
 
-  ApplyPanGestureEvent(0, 0, ui::EventMomentumPhase::NONE,
+  ApplyPanGestureEvent(0, 0,
+                       transitioning_to_pinch ? ui::EventMomentumPhase::BLOCKED
+                                              : ui::EventMomentumPhase::NONE,
                        ui::ScrollEventPhase::kEnd);
 }
 
diff --git a/ui/views/win/hwnd_message_handler.h b/ui/views/win/hwnd_message_handler.h
index aa6323a..d85486a 100644
--- a/ui/views/win/hwnd_message_handler.h
+++ b/ui/views/win/hwnd_message_handler.h
@@ -239,7 +239,7 @@
   void ApplyPanGestureScroll(int scroll_x, int scroll_y) override;
   void ApplyPanGestureFling(int scroll_x, int scroll_y) override;
   void ApplyPanGestureScrollBegin(int scroll_x, int scroll_y) override;
-  void ApplyPanGestureScrollEnd() override;
+  void ApplyPanGestureScrollEnd(bool transitioning_to_pinch) override;
   void ApplyPanGestureFlingBegin() override;
   void ApplyPanGestureFlingEnd() override;
 
diff --git a/ui/webui/resources/cr_components/managed_footnote/managed_footnote.html b/ui/webui/resources/cr_components/managed_footnote/managed_footnote.html
index a01a60b5..fc3dc1d2 100644
--- a/ui/webui/resources/cr_components/managed_footnote/managed_footnote.html
+++ b/ui/webui/resources/cr_components/managed_footnote/managed_footnote.html
@@ -42,7 +42,9 @@
 
     <template is="dom-if" if="[[isManaged_]]">
       <iron-icon icon="cr:domain"></iron-icon>
-      <div id="content" inner-h-t-m-l="[[i18nAdvanced('managedByOrg')]]"></div>
+      <div id="content"
+          inner-h-t-m-l="[[getManagementString_(showDeviceInfo)]]">
+      </div>
     </template>
   </template>
   <script src="managed_footnote.js"></script>
diff --git a/ui/webui/resources/cr_components/managed_footnote/managed_footnote.js b/ui/webui/resources/cr_components/managed_footnote/managed_footnote.js
index b5e4e2d2..fc08e3f0 100644
--- a/ui/webui/resources/cr_components/managed_footnote/managed_footnote.js
+++ b/ui/webui/resources/cr_components/managed_footnote/managed_footnote.js
@@ -19,8 +19,9 @@
 
   properties: {
     /**
-     * Whether the browser is managed by their organization through enterprise
+     * Whether the user is managed by their organization through enterprise
      * policies.
+     * @type {boolean}
      * @private
      */
     isManaged_: {
@@ -30,6 +31,16 @@
         return loadTimeData.getBoolean('isManaged');
       },
     },
+
+    /**
+     * Whether the device should be indicated as managed rather than the
+     * browser.
+     * @type {boolean}
+     */
+    showDeviceInfo: {
+      type: Boolean,
+      value: false,
+    }
   },
 
   /** @override */
@@ -39,6 +50,19 @@
       this.isManaged_ = managed;
     });
   },
+
+  /**
+   * @return {string} Message to display to the user.
+   * @private
+   */
+  getManagementString_: function() {
+    // <if expr="chromeos">
+    if (this.showDeviceInfo) {
+      return this.i18nAdvanced('deviceManagedByOrg');
+    }
+    // </if>
+    return this.i18nAdvanced('browserManagedByOrg');
+  },
 });
 
 chrome.send('observeManagedUI');
diff --git a/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html b/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
index 8673bf3..3f99856 100644
--- a/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
+++ b/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
@@ -1,5 +1,6 @@
 <link rel="import" href="../../html/polymer.html">
 
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="../../html/assert.html">
 <link rel="import" href="../../html/util.html">
 <link rel="import" href="../shared_vars_css.html">
@@ -73,7 +74,6 @@
         font-size: 123.08%;  /* go to 16px from 13px */
         font-weight: var(--cr-drawer-header-font-weight, inherit);
         min-height: 56px;
-        outline: none;
         padding-inline-start: var(--cr-drawer-header-padding, 24px);
       }
 
@@ -83,6 +83,17 @@
         }
       }
 
+      #heading {
+        outline: none;
+      }
+
+      #iconButton {
+        cursor: pointer;
+        margin-inline-end: 14px;
+        margin-inline-start: 0;
+        outline: none;
+      }
+
       :host ::slotted(.drawer-content) {
         height: calc(100% - 56px);
         overflow: auto;
@@ -91,7 +102,15 @@
     <dialog id="dialog" on-cancel="onDialogCancel_" on-tap="onDialogTap_"
         on-close="onDialogClose_">
       <div id="container" on-tap="onContainerTap_">
-        <div class="drawer-header" tabindex="-1">[[heading]]</div>
+        <div class="drawer-header">
+          <!-- Hidden from a11y because this icon is decorative. Clicking closes
+              the dialog, but screen reader users can do this by pressing ESC,
+              so aria-hidden is OK here. -->
+          <iron-icon id="iconButton" icon="[[iconName]]" on-tap="onIconTap_"
+              title="[[iconTitle]]" hidden="[[!iconName]]" aria-hidden="true">
+          </iron-icon>
+          <div id="heading" tabindex="-1">[[heading]]</div>
+        </div>
         <slot></slot>
       </div>
     </dialog>
diff --git a/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.js b/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.js
index e3be0c9..9695f7d 100644
--- a/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.js
+++ b/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.js
@@ -20,6 +20,18 @@
       value: 'ltr',
       reflectToAttribute: true,
     },
+
+    /**
+     * An iron-icon resource name, e.g. "cr20:menu". If null, no icon will
+     * be shown.
+     */
+    iconName: {
+      type: String,
+      value: null,
+    },
+
+    /** Title attribute for the icon, if shown. */
+    iconTitle: String,
   },
 
   /** @type {boolean} */
@@ -83,6 +95,15 @@
   },
 
   /**
+   * Handles a tap on the (optional) icon.
+   * @param {!Event} event
+   * @private
+   */
+  onIconTap_: function(event) {
+    this.cancel();
+  },
+
+  /**
    * Stop propagation of a tap event inside the container. This will allow
    * |onDialogTap_| to only be called when clicked outside the container.
    * @param {!Event} event
diff --git a/ui/webui/resources/js/chromeos/onc_mojo.js b/ui/webui/resources/js/chromeos/onc_mojo.js
index 1d3d492..3184eb3 100644
--- a/ui/webui/resources/js/chromeos/onc_mojo.js
+++ b/ui/webui/resources/js/chromeos/onc_mojo.js
@@ -584,8 +584,10 @@
             mojom.AuthenticationType.kNone;
         break;
       case mojom.NetworkType.kTether:
-        // TODO(stevenjb): Provide Tether managed properties for completeness.
-        // (CrNetworkIcon does not currently require Tether properties).
+        if (properties.tether) {
+          networkState.tether = /** @type {!mojom.TetherStateProperties}*/ (
+              Object.assign({}, properties.tether));
+        }
         break;
       case mojom.NetworkType.kVPN:
         networkState.vpn.providerName = properties.vpn.providerName;