diff --git a/DEPS b/DEPS
index 01bddd8..5fc7e64 100644
--- a/DEPS
+++ b/DEPS
@@ -142,7 +142,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '13da560bef16ab254fe91e85541f8dd4db6c337a',
+  'v8_revision': 'c843aa75886abbd9aa4ea65818ece3d661ab2ed3',
   # 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.
@@ -154,11 +154,11 @@
   # 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': '5b424e69101a103a630327b8de7a6636dfddea2c',
+  'swiftshader_revision': '5c9e165bc4d554840ed32e52876f0fbde45c9a4f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'a8f7a450950b845a9356b5d20e995f41cdcd53ed',
+  'pdfium_revision': '27a4c4609a1f746646d11d19eb177e631b5084ea',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -173,7 +173,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling googletest
   # and whatever else without interference from each other.
-  'googletest_revision': 'f5edb4f542e155c75bc4b516f227911d99ec167c',
+  'googletest_revision': '31200def0dec8a624c861f919e86e4444e6e6ee7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling lighttpd
   # and whatever else without interference from each other.
@@ -201,7 +201,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': 'a6e2399787969ec410bd08f0baf56d7f5dc6ba9b',
+  'catapult_revision': '991302572440fa546ef523789fa821943184b4bf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -257,7 +257,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '4a00a80c40484a6f6f72f48c9d34943cf8f180d4',
+  'spv_tools_revision': '08cc49ec59c3ff4d6bd4bb4f2097ede35e802158',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -273,7 +273,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'b632bc58ed373eb2020af65e0fdeaf07d46d1a9c',
+  'dawn_revision': '9cd21f1bf945175cd6247a386e3704f42c39661a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -807,7 +807,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'e77dbf6003584c55a7da1fa1ab4fecec032e17bf',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'b933eedeb03d81be108a053619f602b598879dc7',
       'condition': 'checkout_linux',
   },
 
@@ -832,7 +832,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '075cb05bdeb5eb7f42cf259a5a40eebf54dc21dd',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '8a37389368143a44c4acd48117be68a4c3214e5b',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -901,7 +901,7 @@
   },
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '2f4a8dfd3a596d75e3c26cb7ae9b68886d3a19cf',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'd3692c701b1265955221aa0d6ebc656bc4442b2a',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1187,7 +1187,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'f80d2d4f0a64cfa3a19b772b83adfdfcda1aaa19',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '3515443cb534f2503dffb45e54c384b30598141f',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1355,7 +1355,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6f0b34abee8dba611c253738d955c59f703c147a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '479a3c0f92b957a9ba1c242e7fe6e4d37b49ce32',
+    Var('webrtc_git') + '/src.git' + '@' + '74e63b8abbbfdc25df05c90a6f4bc40cead73304',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1396,7 +1396,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@bc5e8283ebb09d654705946728d20d6985c0994c',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c86b527c737088eddef3cd2003b278107812c17e',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/permission/media_access_permission_request.cc b/android_webview/browser/permission/media_access_permission_request.cc
index 633798b..f6923234 100644
--- a/android_webview/browser/permission/media_access_permission_request.cc
+++ b/android_webview/browser/permission/media_access_permission_request.cc
@@ -55,7 +55,8 @@
     return;
   }
 
-  if (request_.audio_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE) {
+  if (request_.audio_type ==
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
     const MediaStreamDevices& audio_devices =
         audio_test_devices_.empty()
             ? MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices()
@@ -66,7 +67,8 @@
       devices.push_back(*device);
   }
 
-  if (request_.video_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE) {
+  if (request_.video_type ==
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
     const MediaStreamDevices& video_devices =
         video_test_devices_.empty()
             ? MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices()
@@ -88,10 +90,12 @@
 }
 
 int64_t MediaAccessPermissionRequest::GetResources() {
-  return (request_.audio_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE
+  return (request_.audio_type ==
+                  blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE
               ? AwPermissionRequest::AudioCapture
               : 0) |
-         (request_.video_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE
+         (request_.video_type ==
+                  blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE
               ? AwPermissionRequest::VideoCapture
               : 0);
 }
diff --git a/android_webview/browser/permission/media_access_permission_request_unittest.cc b/android_webview/browser/permission/media_access_permission_request_unittest.cc
index 7b0a7d0..5073fc657 100644
--- a/android_webview/browser/permission/media_access_permission_request_unittest.cc
+++ b/android_webview/browser/permission/media_access_permission_request_unittest.cc
@@ -36,21 +36,26 @@
       std::string video_id) {
     blink::MediaStreamDevices audio_devices;
     audio_devices.push_back(blink::MediaStreamDevice(
-        blink::MEDIA_DEVICE_AUDIO_CAPTURE, first_audio_device_id_, "a2"));
+        blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+        first_audio_device_id_, "a2"));
     audio_devices.push_back(blink::MediaStreamDevice(
-        blink::MEDIA_DEVICE_AUDIO_CAPTURE, audio_device_id_, "a1"));
+        blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, audio_device_id_,
+        "a1"));
 
     blink::MediaStreamDevices video_devices;
     video_devices.push_back(blink::MediaStreamDevice(
-        blink::MEDIA_DEVICE_VIDEO_CAPTURE, first_video_device_id_, "v2"));
+        blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+        first_video_device_id_, "v2"));
     video_devices.push_back(blink::MediaStreamDevice(
-        blink::MEDIA_DEVICE_VIDEO_CAPTURE, video_device_id_, "v1"));
+        blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, video_device_id_,
+        "v1"));
 
     GURL origin("https://www.google.com");
     content::MediaStreamRequest request(
         0, 0, 0, origin, false, blink::MEDIA_GENERATE_STREAM, audio_id,
-        video_id, blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-        blink::MEDIA_DEVICE_VIDEO_CAPTURE, false /* disable_local_echo */);
+        video_id, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+        blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+        false /* disable_local_echo */);
 
     std::unique_ptr<TestMediaAccessPermissionRequest> permission_request;
     permission_request.reset(new TestMediaAccessPermissionRequest(
@@ -89,10 +94,10 @@
   bool video_exist = false;
   for (blink::MediaStreamDevices::iterator i = devices_.begin();
        i != devices_.end(); ++i) {
-    if (i->type == blink::MEDIA_DEVICE_AUDIO_CAPTURE &&
+    if (i->type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE &&
         i->id == audio_device_id_) {
       audio_exist = true;
-    } else if (i->type == blink::MEDIA_DEVICE_VIDEO_CAPTURE &&
+    } else if (i->type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE &&
                i->id == video_device_id_) {
       video_exist = true;
     }
@@ -113,10 +118,10 @@
   bool video_exist = false;
   for (blink::MediaStreamDevices::iterator i = devices_.begin();
        i != devices_.end(); ++i) {
-    if (i->type == blink::MEDIA_DEVICE_AUDIO_CAPTURE &&
+    if (i->type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE &&
         i->id == first_audio_device_id_) {
       audio_exist = true;
-    } else if (i->type == blink::MEDIA_DEVICE_VIDEO_CAPTURE &&
+    } else if (i->type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE &&
                i->id == first_video_device_id_) {
       video_exist = true;
     }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 45cce9a..41e5c9a 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -55,6 +55,7 @@
     "public/cpp/multi_user_window_manager.h",
     "public/cpp/multi_user_window_manager_delegate.h",
     "public/cpp/multi_user_window_manager_observer.h",
+    "public/cpp/window_finder.h",
     "public/cpp/window_tree_host_lookup.h",
     "root_window_controller.h",
     "screenshot_delegate.h",
@@ -83,7 +84,6 @@
     "wm/tablet_mode/tablet_mode_observer.h",
     "wm/tablet_mode/tablet_mode_window_drag_controller.h",
     "wm/tablet_mode/tablet_mode_window_drag_delegate.h",
-    "wm/window_finder.h",
     "wm/window_positioner.h",
     "wm/window_positioning_utils.h",
     "wm/window_properties.h",
@@ -350,8 +350,8 @@
     "keyboard/virtual_keyboard_controller.h",
     "kiosk_next/kiosk_next_home_controller.cc",
     "kiosk_next/kiosk_next_home_controller.h",
-    "kiosk_next/kiosk_next_shell_controller.cc",
-    "kiosk_next/kiosk_next_shell_controller.h",
+    "kiosk_next/kiosk_next_shell_controller_impl.cc",
+    "kiosk_next/kiosk_next_shell_controller_impl.h",
     "kiosk_next/kiosk_next_shell_observer.h",
     "laser/laser_pointer_controller.cc",
     "laser/laser_pointer_controller.h",
@@ -448,8 +448,8 @@
     "magnifier/magnifier_utils.h",
     "magnifier/partial_magnification_controller.cc",
     "magnifier/partial_magnification_controller.h",
-    "media/media_controller.cc",
-    "media/media_controller.h",
+    "media/media_controller_impl.cc",
+    "media/media_controller_impl.h",
     "media/media_notification_background.cc",
     "media/media_notification_background.h",
     "media/media_notification_constants.cc",
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc
index bbf58bb..368f3b1 100644
--- a/ash/accelerators/accelerator_controller_impl.cc
+++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -25,10 +25,10 @@
 #include "ash/ime/ime_controller.h"
 #include "ash/ime/ime_switch_type.h"
 #include "ash/keyboard/ui/keyboard_controller.h"
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/magnifier/docked_magnifier_controller_impl.h"
 #include "ash/magnifier/magnification_controller.h"
-#include "ash/media/media_controller.h"
+#include "ash/media/media_controller_impl.h"
 #include "ash/metrics/user_metrics_recorder.h"
 #include "ash/multi_profile_uma.h"
 #include "ash/public/cpp/ash_features.h"
@@ -922,14 +922,19 @@
 }
 
 void SetHighContrastEnabled(bool enabled) {
-  if (enabled) {
+  AccessibilityController* accessibility_controller =
+      Shell::Get()->accessibility_controller();
+  accessibility_controller->SetHighContrastEnabled(enabled);
+  // Value could differ from one that were set because of higher-priority pref
+  // source, eg. policy. See crbug.com/953245.
+  const bool actual_enabled = accessibility_controller->high_contrast_enabled();
+  if (enabled && actual_enabled) {
     CreateAndShowStickyNotification(IDS_HIGH_CONTRAST_ACCEL_TITLE,
                                     IDS_HIGH_CONTRAST_ACCEL_MSG,
                                     kHighContrastToggleAccelNotificationId);
   } else {
     RemoveStickyNotitification(kHighContrastToggleAccelNotificationId);
   }
-  Shell::Get()->accessibility_controller()->SetHighContrastEnabled(enabled);
 }
 
 void HandleToggleHighContrast() {
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index cf66b10..dee5465 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -20,7 +20,7 @@
 #include "ash/ime/test_ime_controller_client.h"
 #include "ash/magnifier/docked_magnifier_controller_impl.h"
 #include "ash/magnifier/magnification_controller.h"
-#include "ash/media/media_controller.h"
+#include "ash/media/media_controller_impl.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
@@ -1966,8 +1966,8 @@
     client_ = std::make_unique<TestMediaClient>();
     controller_ = std::make_unique<media_session::test::TestMediaController>();
 
-    MediaController* media_controller = Shell::Get()->media_controller();
-    media_controller->SetClient(client_->CreateAssociatedPtrInfo());
+    MediaControllerImpl* media_controller = Shell::Get()->media_controller();
+    media_controller->SetClient(client_.get());
     media_controller->SetMediaSessionControllerForTest(
         controller_->CreateMediaControllerPtr());
     media_controller->SetForceMediaClientKeyHandling(
diff --git a/ash/accessibility/accessibility_controller.cc b/ash/accessibility/accessibility_controller.cc
index 1d9c74d..b50e6df 100644
--- a/ash/accessibility/accessibility_controller.cc
+++ b/ash/accessibility/accessibility_controller.cc
@@ -527,8 +527,13 @@
                                  enabled);
   active_user_prefs_->CommitPendingWrite();
 
+  // Value could be left unchanged because of higher-priority pref source, eg.
+  // policy. See crbug.com/953245.
+  const bool actual_enabled = active_user_prefs_->GetBoolean(
+      prefs::kAccessibilitySpokenFeedbackEnabled);
+
   A11yNotificationType type = A11yNotificationType::kNone;
-  if (enabled && notify == A11Y_NOTIFICATION_SHOW)
+  if (enabled && actual_enabled && notify == A11Y_NOTIFICATION_SHOW)
     type = A11yNotificationType::kSpokenFeedbackEnabled;
   ShowAccessibilityNotification(type);
 }
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index ac953a9..a81a5aaef 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -19,6 +19,7 @@
 #include "ash/assistant/assistant_controller.h"
 #include "ash/assistant/assistant_ui_controller.h"
 #include "ash/assistant/model/assistant_ui_model.h"
+#include "ash/assistant/ui/assistant_ui_constants.h"
 #include "ash/assistant/ui/assistant_view_delegate.h"
 #include "ash/assistant/util/assistant_util.h"
 #include "ash/assistant/util/deep_link_util.h"
@@ -685,13 +686,8 @@
              base::TimeTicks());
       }
 
-      if (!IsShowingEmbeddedAssistantUI()) {
-        if (presenter_.GetView()->app_list_state() ==
-            ash::AppListViewState::kPeeking) {
-          presenter_.GetView()->SetState(ash::AppListViewState::kHalf);
-        }
+      if (!IsShowingEmbeddedAssistantUI())
         presenter_.ShowEmbeddedAssistantUI(true);
-      }
       break;
     case AssistantVisibility::kHidden:
       NOTREACHED();
@@ -700,21 +696,21 @@
       if (!IsShowingEmbeddedAssistantUI())
         break;
 
-      // Reset model state.
       // When Launcher is closing, we do not want to call
       // |ShowEmbeddedAssistantUI(false)|, which will show previous state page
-      // in Launcher and make the Ui flashing.
+      // in Launcher and make the UI flash.
       if (IsHomeScreenAvailable()) {
         presenter_.ShowEmbeddedAssistantUI(false);
-        presenter_.GetView()
-            ->app_list_main_view()
-            ->contents_view()
-            ->ResetForShow();
-        presenter_.GetView()->SetState(
-            ash::AppListViewState::kFullscreenAllApps);
+
+        if (exit_point != AssistantExitPoint::kBackInLauncher) {
+          presenter_.GetView()
+              ->search_box_view()
+              ->ClearSearchAndDeactivateSearchBox();
+        }
       } else if (exit_point != AssistantExitPoint::kBackInLauncher) {
         DismissAppList();
       }
+
       break;
   }
 }
@@ -1008,6 +1004,7 @@
 
   CloseAssistantUi(AssistantExitPoint::kLauncherClose);
   model_->SetState(AppListState::kInvalidState);
+
   if (client_)
     client_->ViewClosing();
 }
diff --git a/ash/app_list/app_list_view_delegate.h b/ash/app_list/app_list_view_delegate.h
index 3d729e5..21b609f0 100644
--- a/ash/app_list/app_list_view_delegate.h
+++ b/ash/app_list/app_list_view_delegate.h
@@ -163,10 +163,6 @@
   // Returns true if the Assistant feature is allowed and enabled.
   virtual bool IsAssistantAllowedAndEnabled() const = 0;
 
-  // Called when the app list view animation is completed.
-  virtual void OnStateTransitionAnimationCompleted(
-      ash::AppListViewState state) = 0;
-
   // Returns true if the Assistant privacy info view should be shown.
   virtual bool ShouldShowAssistantPrivacyInfo() const = 0;
 
@@ -178,6 +174,10 @@
   // indicate not to show the view any more.
   virtual void MarkAssistantPrivacyInfoDismissed() = 0;
 
+  // Called when the app list view animation is completed.
+  virtual void OnStateTransitionAnimationCompleted(
+      ash::AppListViewState state) = 0;
+
   // Fills the given AppLaunchedMetricParams with info known by the delegate.
   virtual void GetAppLaunchedMetricParams(
       AppLaunchedMetricParams* metric_params) = 0;
diff --git a/ash/app_list/model/search/search_result.h b/ash/app_list/model/search/search_result.h
index 0a411b6..e56aa37 100644
--- a/ash/app_list/model/search/search_result.h
+++ b/ash/app_list/model/search/search_result.h
@@ -106,6 +106,11 @@
     metadata_->result_type = result_type;
   }
 
+  int result_subtype() const { return metadata_->result_subtype; }
+  void set_result_subtype(int result_subtype) {
+    metadata_->result_subtype = result_subtype;
+  }
+
   int distance_from_origin() { return distance_from_origin_; }
   void set_distance_from_origin(int distance) {
     distance_from_origin_ = distance;
diff --git a/ash/app_list/test/app_list_test_view_delegate.cc b/ash/app_list/test/app_list_test_view_delegate.cc
index feb8bf74..7fe6fa38 100644
--- a/ash/app_list/test/app_list_test_view_delegate.cc
+++ b/ash/app_list/test/app_list_test_view_delegate.cc
@@ -162,9 +162,6 @@
   return false;
 }
 
-void AppListTestViewDelegate::OnStateTransitionAnimationCompleted(
-    ash::AppListViewState state) {}
-
 bool AppListTestViewDelegate::ShouldShowAssistantPrivacyInfo() const {
   return false;
 }
@@ -173,6 +170,9 @@
 
 void AppListTestViewDelegate::MarkAssistantPrivacyInfoDismissed() {}
 
+void AppListTestViewDelegate::OnStateTransitionAnimationCompleted(
+    ash::AppListViewState state) {}
+
 void AppListTestViewDelegate::GetAppLaunchedMetricParams(
     app_list::AppLaunchedMetricParams* metric_params) {}
 
diff --git a/ash/app_list/test/app_list_test_view_delegate.h b/ash/app_list/test/app_list_test_view_delegate.h
index 8b13ff4..475c181 100644
--- a/ash/app_list/test/app_list_test_view_delegate.h
+++ b/ash/app_list/test/app_list_test_view_delegate.h
@@ -99,11 +99,11 @@
   void OnSearchResultVisibilityChanged(const std::string& id,
                                        bool visibility) override;
   bool IsAssistantAllowedAndEnabled() const override;
-  void OnStateTransitionAnimationCompleted(
-      ash::AppListViewState state) override;
   bool ShouldShowAssistantPrivacyInfo() const override;
   void MaybeIncreaseAssistantPrivacyInfoShownCount() override;
   void MarkAssistantPrivacyInfoDismissed() override;
+  void OnStateTransitionAnimationCompleted(
+      ash::AppListViewState state) override;
   void GetAppLaunchedMetricParams(
       app_list::AppLaunchedMetricParams* metric_params) override;
 
diff --git a/ash/app_list/views/app_list_folder_view.cc b/ash/app_list/views/app_list_folder_view.cc
index c7f1070..2651c5f 100644
--- a/ash/app_list/views/app_list_folder_view.cc
+++ b/ash/app_list/views/app_list_folder_view.cc
@@ -47,7 +47,7 @@
 
 namespace {
 
-constexpr int kItemGridsBottomPadding = 24;
+constexpr int kFolderHeaderPadding = 12;
 constexpr int kOnscreenKeyboardTopPadding = 16;
 
 // Indexes of interesting views in ViewModel of AppListFolderView.
@@ -522,12 +522,12 @@
 }
 
 gfx::Size AppListFolderView::CalculatePreferredSize() const {
-  gfx::Size size = items_grid_view_->GetTileGridSizeWithoutPadding();
+  gfx::Size size = items_grid_view_->GetTileGridSizeWithPadding();
   gfx::Size header_size = folder_header_view_->GetPreferredSize();
-  size.Enlarge(0, kItemGridsBottomPadding + header_size.height());
   const int folder_padding =
       AppListConfig::instance().grid_tile_spacing_in_folder();
-  size.Enlarge(folder_padding * 2, folder_padding * 2);
+  size.Enlarge(folder_padding * 2, folder_padding + kFolderHeaderPadding * 2 +
+                                       header_size.height());
   return size;
 }
 
@@ -696,21 +696,21 @@
 
   // Calculate bounds for items grid view.
   gfx::Rect grid_frame(rect);
-  grid_frame.Inset(items_grid_view_->GetTilePadding());
   grid_frame.set_height(items_grid_view_->GetPreferredSize().height());
   view_model_->set_ideal_bounds(kIndexChildItems, grid_frame);
 
   // Calculate bounds for folder header view.
   gfx::Rect header_frame(rect);
-  header_frame.set_y(grid_frame.bottom() +
-                     items_grid_view_->GetTilePadding().bottom() +
-                     kItemGridsBottomPadding);
+  header_frame.set_y(GetContentsBounds().bottom() - kFolderHeaderPadding -
+                     folder_header_view_->GetPreferredSize().height());
   header_frame.set_height(folder_header_view_->GetPreferredSize().height());
   view_model_->set_ideal_bounds(kIndexFolderHeader, header_frame);
 
   // Calculate bounds for page_switcher.
   gfx::Rect page_switcher_frame(rect);
   gfx::Size page_switcher_size = page_switcher_->GetPreferredSize();
+  page_switcher_size.set_height(
+      AppListConfig::instance().folder_header_height());
   page_switcher_frame.set_x(page_switcher_frame.right() -
                             page_switcher_size.width());
   page_switcher_frame.set_y(header_frame.y());
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc
index 252cdf7..5195f31 100644
--- a/ash/app_list/views/app_list_item_view.cc
+++ b/ash/app_list/views/app_list_item_view.cc
@@ -238,15 +238,16 @@
   title_->SetBackgroundColor(SK_ColorTRANSPARENT);
   title_->SetHandlesTooltips(false);
   title_->SetFontList(AppListConfig::instance().app_title_font());
-  title_->SetLineHeight(AppListConfig::instance().app_title_max_line_height());
   title_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
   title_->SetEnabledColor(apps_grid_view_->is_in_folder()
                               ? kFolderGridTitleColor
                               : AppListConfig::instance().grid_title_color());
   if (!is_in_folder) {
-    title_->SetShadows(
-        gfx::ShadowValues(1, gfx::ShadowValue(gfx::Vector2d(), kTitleShadowBlur,
-                                              kTitleShadowColor)));
+    gfx::ShadowValues title_shadow = gfx::ShadowValues(
+        1,
+        gfx::ShadowValue(gfx::Vector2d(), kTitleShadowBlur, kTitleShadowColor));
+    title_->SetShadows(title_shadow);
+    title_shadow_margins_ = gfx::ShadowValue::GetMargin(title_shadow);
   }
 
   AddChildView(icon_);
@@ -608,8 +609,13 @@
         GetIconBoundsForTargetViewBounds(rect, icon_shadow_->GetImage().size());
     icon_shadow_->SetBoundsRect(icon_shadow_bounds);
   }
-  title_->SetBoundsRect(
-      GetTitleBoundsForTargetViewBounds(rect, title_->GetPreferredSize()));
+
+  gfx::Rect title_bounds =
+      GetTitleBoundsForTargetViewBounds(rect, title_->GetPreferredSize());
+  if (!apps_grid_view_->is_in_folder())
+    title_bounds.Inset(title_shadow_margins_);
+  title_->SetBoundsRect(title_bounds);
+
   progress_bar_->SetBoundsRect(GetProgressBarBoundsForTargetViewBounds(
       rect, progress_bar_->GetPreferredSize()));
 }
@@ -866,7 +872,8 @@
   gfx::Rect rect(target_bounds);
   rect.Inset(AppListConfig::instance().grid_title_horizontal_padding(),
              AppListConfig::instance().grid_title_top_padding(),
-             AppListConfig::instance().grid_title_horizontal_padding(), 0);
+             AppListConfig::instance().grid_title_horizontal_padding(),
+             AppListConfig::instance().grid_title_bottom_padding());
   rect.ClampToCenteredSize(title_size);
   return rect;
 }
diff --git a/ash/app_list/views/app_list_item_view.h b/ash/app_list/views/app_list_item_view.h
index 2d971b61..94134877 100644
--- a/ash/app_list/views/app_list_item_view.h
+++ b/ash/app_list/views/app_list_item_view.h
@@ -265,6 +265,9 @@
   // A timer to defer showing drag UI when the app item is touch pressed.
   base::OneShotTimer touch_drag_timer_;
 
+  // The shadow margins added to the app list item title.
+  gfx::Insets title_shadow_margins_;
+
   base::WeakPtrFactory<AppListItemView> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AppListItemView);
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index 140ce06..1de590d 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -67,7 +67,7 @@
 namespace {
 
 // The height of the half app list from the bottom of the screen.
-constexpr int kHalfAppListHeight = 561;
+constexpr int kHalfAppListHeight = 545;
 
 // The scroll offset in order to transition from PEEKING to FULLSCREEN
 constexpr int kAppListMinScrollToSwitchStates = 20;
@@ -829,6 +829,13 @@
     return;
   }
 
+  // Close embedded Assistant UI if it is shown.
+  if (app_list_main_view()->contents_view()->IsShowingEmbeddedAssistantUI()) {
+    Back();
+    search_box_view_->ClearSearchAndDeactivateSearchBox();
+    return;
+  }
+
   // Clear focus if the located event is not handled by any child view.
   GetFocusManager()->ClearFocus();
 
@@ -865,10 +872,6 @@
     return;
   }
 
-  // Reset the AppListState if the embedded Assistant UI is shown.
-  if (app_list_main_view()->contents_view()->IsShowingEmbeddedAssistantUI())
-    Back();
-
   search_box_view_->ClearSearchAndDeactivateSearchBox();
 }
 
@@ -1071,7 +1074,8 @@
   switch (transition) {
     case kPeekingToHalf:
     case kFullscreenAllAppsToFullscreenSearch:
-      delegate_->MaybeIncreaseAssistantPrivacyInfoShownCount();
+      if (app_list_main_view()->contents_view()->IsShowingSearchResults())
+        delegate_->MaybeIncreaseAssistantPrivacyInfoShownCount();
       break;
     default:
       break;
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 17d4e8b..fc1264e 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -389,10 +389,22 @@
   return gfx::Insets(-vertical_tile_padding_, -horizontal_tile_padding_);
 }
 
-gfx::Size AppsGridView::GetTileGridSizeWithoutPadding() const {
-  gfx::Size size = GetTileGridSize();
-  gfx::Insets grid_padding = GetTilePadding();
-  size.Enlarge(grid_padding.width(), grid_padding.height());
+gfx::Size AppsGridView::GetTileGridSizeWithPadding() const {
+  gfx::Size size(GetTileViewSize());
+  size.SetSize(size.width() * cols_, size.height() * rows_per_page_);
+
+  int horizontal_padding = horizontal_tile_padding_ * 2;
+  int vertical_padding = vertical_tile_padding_ * 2;
+
+  if (folder_delegate_) {
+    const int tile_padding_in_folder =
+        AppListConfig::instance().grid_tile_spacing_in_folder();
+    horizontal_padding = tile_padding_in_folder;
+    vertical_padding = tile_padding_in_folder;
+  }
+
+  size.Enlarge(horizontal_padding * (cols_ - 1),
+               vertical_padding * (rows_per_page_ - 1));
   return size;
 }
 
diff --git a/ash/app_list/views/apps_grid_view.h b/ash/app_list/views/apps_grid_view.h
index c78bb92..9cfe494 100644
--- a/ash/app_list/views/apps_grid_view.h
+++ b/ash/app_list/views/apps_grid_view.h
@@ -107,8 +107,8 @@
   // Returns the padding around a tile view.
   gfx::Insets GetTilePadding() const;
 
-  // Returns the size of the entire tile grid without padding.
-  gfx::Size GetTileGridSizeWithoutPadding() const;
+  // Returns the size of the entire tile grid with padding between tiles.
+  gfx::Size GetTileGridSizeWithPadding() const;
 
   // Returns the minimum size of the entire tile grid.
   gfx::Size GetMinimumTileGridSize(int cols, int rows_per_page) const;
diff --git a/ash/app_list/views/assistant/assistant_main_stage.cc b/ash/app_list/views/assistant/assistant_main_stage.cc
index bdf678f..8fc966d 100644
--- a/ash/app_list/views/assistant/assistant_main_stage.cc
+++ b/ash/app_list/views/assistant/assistant_main_stage.cc
@@ -4,7 +4,10 @@
 
 #include "ash/app_list/views/assistant/assistant_main_stage.h"
 
+#include <numeric>
+
 #include "ash/assistant/model/assistant_query.h"
+#include "ash/assistant/ui/assistant_ui_constants.h"
 #include "ash/assistant/ui/assistant_view_delegate.h"
 #include "ash/assistant/ui/main_stage/assistant_footer_view.h"
 #include "ash/assistant/ui/main_stage/assistant_progress_indicator.h"
@@ -12,14 +15,17 @@
 #include "ash/assistant/ui/main_stage/ui_element_container_view.h"
 #include "ash/assistant/util/animation_util.h"
 #include "ash/assistant/util/assistant_util.h"
+#include "ash/strings/grit/ash_strings.h"
 #include "base/bind.h"
 #include "base/time/time.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/compositor/callback_layer_animation_observer.h"
 #include "ui/compositor/layer_animation_element.h"
 #include "ui/compositor/layer_animator.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/views/border.h"
+#include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/layout_manager.h"
@@ -51,11 +57,129 @@
 constexpr base::TimeDelta kFooterEntryAnimationFadeInDuration =
     base::TimeDelta::FromMilliseconds(167);
 
-// Progress animation.
-constexpr base::TimeDelta kProgressAnimationFadeInDelay =
+// Divider animation.
+constexpr base::TimeDelta kDividerAnimationFadeInDelay =
     base::TimeDelta::FromMilliseconds(233);
-constexpr base::TimeDelta kProgressAnimationFadeInDuration =
+constexpr base::TimeDelta kDividerAnimationFadeInDuration =
     base::TimeDelta::FromMilliseconds(167);
+constexpr base::TimeDelta kDividerAnimationFadeOutDuration =
+    base::TimeDelta::FromMilliseconds(83);
+
+// Greeting animation.
+constexpr base::TimeDelta kGreetingAnimationFadeOutDuration =
+    base::TimeDelta::FromMilliseconds(83);
+
+// StackLayout -----------------------------------------------------------------
+// TODO(wutao): Move an implementation of StackLayout to ash/assistant/ui/base
+// so that it can be reused across standalone and embedded Assistant
+// implementations.
+
+// A layout manager which lays out its views atop each other. This differs from
+// FillLayout in that we respect the preferred size of views during layout. It's
+// possible to explicitly specify which dimension to respect. In contrast,
+// FillLayout will cause its views to match the bounds of the host.
+class StackLayout : public views::LayoutManager {
+ public:
+  enum class RespectDimension : uint32_t {
+    // Respect width. If enabled, child's preferred width will be used and will
+    // be horizontally center positioned. Otherwise, the child will be stretched
+    // to match parent width.
+    kWidth = 1,
+    // Respect height. If enabled, child's preferred height will be used.
+    // Otherwise, the child will be stretched to match parent height.
+    // Note that the child is always top-aligned.
+    kHeight = 1 << 1,
+    kAll = kWidth | kHeight,
+  };
+
+  enum class VerticalAlignment {
+    kCenter = 1,
+  };
+
+  StackLayout() = default;
+  ~StackLayout() override = default;
+
+  void Installed(views::View* host) override { host_ = host; }
+
+  void ViewRemoved(views::View* host, views::View* view) override {
+    DCHECK(view);
+    respect_dimension_map_.erase(view);
+    vertical_alignment_map_.erase(view);
+  }
+
+  void SetRespectDimensionForView(views::View* view,
+                                  RespectDimension dimension) {
+    DCHECK(host_ && view->parent() == host_);
+    respect_dimension_map_[view] = dimension;
+  }
+
+  void SetVerticalAlignmentForView(views::View* view,
+                                   VerticalAlignment alignment) {
+    DCHECK(host_ && view->parent() == host_);
+    vertical_alignment_map_[view] = alignment;
+  }
+
+  gfx::Size GetPreferredSize(const views::View* host) const override {
+    return std::accumulate(host->children().cbegin(), host->children().cend(),
+                           gfx::Size(), [](gfx::Size size, const auto* v) {
+                             size.SetToMax(v->GetPreferredSize());
+                             return size;
+                           });
+  }
+
+  int GetPreferredHeightForWidth(const views::View* host,
+                                 int width) const override {
+    const auto& children = host->children();
+    if (children.empty())
+      return 0;
+    std::vector<int> heights(children.size());
+    std::transform(
+        children.cbegin(), children.cend(), heights.begin(),
+        [width](const views::View* v) { return v->GetHeightForWidth(width); });
+    return *std::max_element(heights.cbegin(), heights.cend());
+  }
+
+  void Layout(views::View* host) override {
+    const int host_width = host->GetContentsBounds().width();
+    const int host_height = host->GetContentsBounds().height();
+
+    for (auto* child : host->children()) {
+      int child_width = host_width;
+      int child_height = host_height;
+
+      int child_x = 0;
+      uint32_t dimension = static_cast<uint32_t>(RespectDimension::kAll);
+
+      if (respect_dimension_map_.find(child) != respect_dimension_map_.end())
+        dimension = static_cast<uint32_t>(respect_dimension_map_[child]);
+
+      if (dimension & static_cast<uint32_t>(RespectDimension::kWidth)) {
+        child_width = std::min(child->GetPreferredSize().width(), host_width);
+        child_x = (host_width - child_width) / 2;
+      }
+
+      if (dimension & static_cast<uint32_t>(RespectDimension::kHeight))
+        child_height = child->GetHeightForWidth(child_width);
+
+      int child_y = 0;
+      auto iter = vertical_alignment_map_.find(child);
+      if (iter != vertical_alignment_map_.end()) {
+        VerticalAlignment vertical_alignment = iter->second;
+        if (vertical_alignment == VerticalAlignment::kCenter)
+          child_y = std::max(0, (host_height - child_height) / 2);
+      }
+
+      child->SetBounds(child_x, child_y, child_width, child_height);
+    }
+  }
+
+ private:
+  views::View* host_ = nullptr;
+  std::map<views::View*, RespectDimension> respect_dimension_map_;
+  std::map<views::View*, VerticalAlignment> vertical_alignment_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(StackLayout);
+};
 
 // HorizontalSeparator ---------------------------------------------------------
 
@@ -88,6 +212,12 @@
   DISALLOW_COPY_AND_ASSIGN(HorizontalSeparator);
 };
 
+bool IsLayerVisible(views::View* view) {
+  DCHECK(view->layer());
+  return view->IsDrawn() &&
+         !cc::MathUtil::IsWithinEpsilon(view->layer()->GetTargetOpacity(), 0.f);
+}
+
 }  // namespace
 
 // AssistantMainStage ----------------------------------------------------------
@@ -104,9 +234,9 @@
                   base::Unretained(this)))) {
   InitLayout();
 
-  // The view hierarchy will be destructed before |assistant_controller_| in
-  // Shell, which owns AssistantViewDelegate, so AssistantViewDelegate is
-  // guaranteed to outlive the AssistantMainStage.
+  // The view hierarchy will be destructed before AssistantController in Shell,
+  // which owns AssistantViewDelegate, so AssistantViewDelegate is guaranteed to
+  // outlive the AssistantMainStage.
   delegate_->AddInteractionModelObserver(this);
   delegate_->AddUiModelObserver(this);
 }
@@ -126,9 +256,17 @@
 
 void AssistantMainStage::OnViewPreferredSizeChanged(views::View* view) {
   PreferredSizeChanged();
+  Layout();
+  SchedulePaint();
 }
 
 void AssistantMainStage::InitLayout() {
+  // The children of AssistantMainStage will be animated on their own layers and
+  // we want them to be clipped by their parent layer.
+  SetPaintToLayer();
+  layer()->SetFillsBoundsOpaquely(false);
+  layer()->SetMasksToBounds(true);
+
   views::BoxLayout* layout =
       SetLayoutManager(std::make_unique<views::BoxLayout>(
           views::BoxLayout::Orientation::kVertical));
@@ -136,39 +274,115 @@
   layout->set_cross_axis_alignment(
       views::BoxLayout::CrossAxisAlignment::kCenter);
 
-  // The children of AssistantMainStage will be animated on their own layers and
-  // we want them to be clipped by their parent layer.
-  SetPaintToLayer();
-  layer()->SetFillsBoundsOpaquely(false);
-  layer()->SetMasksToBounds(true);
+  auto* content_layout_container = CreateContentLayoutContainer();
+  AddChildView(content_layout_container);
+  layout->SetFlexForView(content_layout_container, 1);
 
-  // Separators: the progress indicator and the horizontal separator will be the
-  // separator when querying and showing the results, respectively. The height
-  // of the horizontal separator is set to be the same as the progress indicator
-  // in order to avoid height changes and relayout.
+  AddChildView(CreateFooterLayoutContainer());
+}
+
+views::View* AssistantMainStage::CreateContentLayoutContainer() {
+  // The content layout container stacks two views.
+  // On top is a main content container including the line separator, progress
+  // indicator query view and |ui_element_container_|.
+  // |greeting_label_| is laid out beneath of the main content container. As
+  // such, it appears underneath and does not cause repositioning to any of
+  // content layout's underlying views.
+  views::View* content_layout_container = new views::View();
+
+  InitGreetingLabel();
+  content_layout_container->AddChildView(greeting_label_);
+  auto* stack_layout = content_layout_container->SetLayoutManager(
+      std::make_unique<StackLayout>());
+
+  // We need to stretch |greeting_label_| to match its parent so that it
+  // won't use heuristics in Label to infer line breaking, which seems to cause
+  // text clipping with DPI adjustment. See b/112843496.
+  stack_layout->SetRespectDimensionForView(
+      greeting_label_, StackLayout::RespectDimension::kHeight);
+  stack_layout->SetVerticalAlignmentForView(
+      greeting_label_, StackLayout::VerticalAlignment::kCenter);
+
+  auto* main_content_layout_container = CreateMainContentLayoutContainer();
+  content_layout_container->AddChildView(main_content_layout_container);
+
+  // Do not respect height, otherwise bounds will not be set correctly for
+  // scrolling.
+  stack_layout->SetRespectDimensionForView(
+      main_content_layout_container, StackLayout::RespectDimension::kWidth);
+
+  return content_layout_container;
+}
+
+void AssistantMainStage::InitGreetingLabel() {
+  // Greeting label, which will be animated on its own layer.
+  greeting_label_ = new views::Label(
+      l10n_util::GetStringUTF16(IDS_ASH_ASSISTANT_PROMPT_DEFAULT));
+  greeting_label_->SetAutoColorReadabilityEnabled(false);
+  greeting_label_->SetEnabledColor(ash::kTextColorPrimary);
+  greeting_label_->SetFontList(
+      ash::assistant::ui::GetDefaultFontList()
+          .DeriveWithSizeDelta(8)
+          .DeriveWithWeight(gfx::Font::Weight::MEDIUM));
+  greeting_label_->SetHorizontalAlignment(
+      gfx::HorizontalAlignment::ALIGN_CENTER);
+  greeting_label_->SetMultiLine(true);
+  greeting_label_->SetPaintToLayer();
+  greeting_label_->layer()->SetFillsBoundsOpaquely(false);
+}
+
+views::View* AssistantMainStage::CreateMainContentLayoutContainer() {
+  views::View* content_layout_container = new views::View();
+  views::BoxLayout* content_layout = content_layout_container->SetLayoutManager(
+      std::make_unique<views::BoxLayout>(
+          views::BoxLayout::Orientation::kVertical));
+  content_layout->set_main_axis_alignment(
+      views::BoxLayout::MainAxisAlignment::kCenter);
+  content_layout->set_cross_axis_alignment(
+      views::BoxLayout::CrossAxisAlignment::kCenter);
+
+  content_layout_container->AddChildView(CreateDividerLayoutContainer());
+
+  // Query view. Will be animated on its own layer.
+  query_view_ = new ash::AssistantQueryView();
+  query_view_->SetPaintToLayer();
+  query_view_->layer()->SetFillsBoundsOpaquely(false);
+  query_view_->AddObserver(this);
+  content_layout_container->AddChildView(query_view_);
+
+  // UI element container.
+  ui_element_container_ = new ash::UiElementContainerView(delegate_);
+  ui_element_container_->AddObserver(this);
+  content_layout_container->AddChildView(ui_element_container_);
+  content_layout->SetFlexForView(ui_element_container_, 1,
+                                 /*use_min_size=*/true);
+
+  return content_layout_container;
+}
+
+views::View* AssistantMainStage::CreateDividerLayoutContainer() {
+  // Dividers: the progress indicator and the horizontal separator will be the
+  // separator when querying and showing the results, respectively.
+  views::View* divider_container = new views::View();
+  divider_container->SetLayoutManager(std::make_unique<StackLayout>());
+
   // Progress indicator, which will be animated on its own layer.
   progress_indicator_ = new ash::AssistantProgressIndicator();
   progress_indicator_->SetPaintToLayer();
   progress_indicator_->layer()->SetFillsBoundsOpaquely(false);
-  progress_indicator_->SetVisible(false);
-  AddChildView(progress_indicator_);
+  divider_container->AddChildView(progress_indicator_);
 
-  // Horizontal separator.
+  // Horizontal separator, which will be animated on its own layer.
   horizontal_separator_ = new HorizontalSeparator(
       kSeparatorWidthDip, progress_indicator_->GetPreferredSize().height());
-  AddChildView(horizontal_separator_);
+  horizontal_separator_->SetPaintToLayer();
+  horizontal_separator_->layer()->SetFillsBoundsOpaquely(false);
+  divider_container->AddChildView(horizontal_separator_);
 
-  // Query view.
-  query_view_ = new ash::AssistantQueryView();
-  AddChildView(query_view_);
+  return divider_container;
+}
 
-  // UI element container.
-  ui_element_container_ = new ash::UiElementContainerView(delegate_);
-  AddChildView(ui_element_container_);
-
-  layout->SetFlexForView(ui_element_container_, 1,
-                         /*use_min_size=*/true);
-
+views::View* AssistantMainStage::CreateFooterLayoutContainer() {
   // Footer.
   // Note that the |footer_| is placed within its own view container so that as
   // its visibility changes, its parent container will still reserve the same
@@ -185,36 +399,66 @@
   footer_->layer()->SetFillsBoundsOpaquely(false);
 
   footer_container->AddChildView(footer_);
-  AddChildView(footer_container);
+
+  return footer_container;
 }
 
 void AssistantMainStage::OnCommittedQueryChanged(
     const ash::AssistantQuery& query) {
+  // Update the view.
+  query_view_->SetQuery(query);
+
+  // If query is empty and we are showing greeting label, do not update the Ui.
+  if (query.Empty() && IsLayerVisible(greeting_label_))
+    return;
+
   using ash::assistant::util::CreateLayerAnimationSequence;
   using ash::assistant::util::CreateOpacityElement;
 
-  // TODO(wutao): Replace the visibility change by animations.
-  horizontal_separator_->SetVisible(false);
+  // Hide the horizontal separator.
+  horizontal_separator_->layer()->GetAnimator()->StartAnimation(
+      CreateLayerAnimationSequence(ash::assistant::util::CreateOpacityElement(
+          0.f, kDividerAnimationFadeOutDuration)));
 
   // Show the progress indicator.
-  progress_indicator_->SetVisible(true);
   progress_indicator_->layer()->GetAnimator()->StartAnimation(
       CreateLayerAnimationSequence(
           // Delay...
           ui::LayerAnimationElement::CreatePauseElement(
               ui::LayerAnimationElement::AnimatableProperty::OPACITY,
-              kProgressAnimationFadeInDelay),
+              kDividerAnimationFadeInDelay),
           // ...then fade in.
           ash::assistant::util::CreateOpacityElement(
-              1.f, kProgressAnimationFadeInDuration)));
+              1.f, kDividerAnimationFadeInDuration)));
 
-  // Update the view.
-  query_view_->SetQuery(query);
+  MaybeHideGreetingLabel();
 }
 
 void AssistantMainStage::OnPendingQueryChanged(
     const ash::AssistantQuery& query) {
+  // Update the view.
   query_view_->SetQuery(query);
+
+  if (!IsLayerVisible(greeting_label_))
+    return;
+
+  using ash::assistant::util::CreateLayerAnimationSequence;
+  using ash::assistant::util::CreateOpacityElement;
+
+  // Animate the opacity to 100% with delay equal to |greeting_label_| fade out
+  // animation duration to avoid the two views displaying at the same time.
+  constexpr base::TimeDelta kQueryAnimationFadeInDuration =
+      base::TimeDelta::FromMilliseconds(433);
+  query_view_->layer()->SetOpacity(0.f);
+  query_view_->layer()->GetAnimator()->StartAnimation(
+      CreateLayerAnimationSequence(
+          ui::LayerAnimationElement::CreatePauseElement(
+              ui::LayerAnimationElement::AnimatableProperty::OPACITY,
+              kGreetingAnimationFadeOutDuration),
+          CreateOpacityElement(1.f, kQueryAnimationFadeInDuration)));
+
+  if (!query.Empty())
+    MaybeHideGreetingLabel();
 }
 
 void AssistantMainStage::OnPendingQueryCleared() {
@@ -227,9 +471,26 @@
 
 void AssistantMainStage::OnResponseChanged(
     const std::shared_ptr<ash::AssistantResponse>& response) {
-  // TODO(wutao): Replace the visibility change by animations.
-  horizontal_separator_->SetVisible(true);
-  progress_indicator_->SetVisible(false);
+  MaybeHideGreetingLabel();
+
+  using ash::assistant::util::CreateLayerAnimationSequence;
+  using ash::assistant::util::CreateOpacityElement;
+
+  // Show the horizontal separator.
+  horizontal_separator_->layer()->GetAnimator()->StartAnimation(
+      CreateLayerAnimationSequence(
+          // Delay...
+          ui::LayerAnimationElement::CreatePauseElement(
+              ui::LayerAnimationElement::AnimatableProperty::OPACITY,
+              kDividerAnimationFadeInDelay),
+          // ...then fade in.
+          ash::assistant::util::CreateOpacityElement(
+              1.f, kDividerAnimationFadeInDuration)));
+
+  // Hide the progress indicator.
+  progress_indicator_->layer()->GetAnimator()->StartAnimation(
+      CreateLayerAnimationSequence(ash::assistant::util::CreateOpacityElement(
+          0.f, kDividerAnimationFadeOutDuration)));
 
   UpdateFooter();
 }
@@ -241,11 +502,49 @@
     base::Optional<ash::AssistantExitPoint> exit_point) {
   if (ash::assistant::util::IsStartingSession(new_visibility, old_visibility)) {
     // When Assistant is starting a new session, we animate in the appearance of
-    // the footer.
+    // the greeting label and footer.
     using ash::assistant::util::CreateLayerAnimationSequence;
     using ash::assistant::util::CreateOpacityElement;
     using ash::assistant::util::CreateTransformElement;
 
+    const bool from_search =
+        entry_point == ash::AssistantEntryPoint::kLauncherSearchResult;
+    progress_indicator_->layer()->SetOpacity(0.f);
+    horizontal_separator_->layer()->SetOpacity(from_search ? 1.f : 0.f);
+
+    greeting_label_->layer()->GetAnimator()->StopAnimating();
+
+    // We're going to animate the greeting label up into position so we'll
+    // need to apply an initial transformation.
+    constexpr int kGreetingAnimationTranslationDip = 115;
+    gfx::Transform transform;
+    transform.Translate(0, kGreetingAnimationTranslationDip);
+
+    // Set up our pre-animation values.
+    greeting_label_->layer()->SetOpacity(0.f);
+    greeting_label_->layer()->SetTransform(transform);
+    if (!from_search) {
+      constexpr base::TimeDelta kGreetingAnimationFadeInDelay =
+          base::TimeDelta::FromMilliseconds(33);
+      constexpr base::TimeDelta kGreetingAnimationFadeInDuration =
+          base::TimeDelta::FromMilliseconds(167);
+      constexpr base::TimeDelta kGreetingAnimationTranslateUpDuration =
+          base::TimeDelta::FromMilliseconds(250);
+
+      // Start animating greeting label.
+      greeting_label_->layer()->GetAnimator()->StartTogether(
+          {// Animate the transformation.
+           CreateLayerAnimationSequence(CreateTransformElement(
+               gfx::Transform(), kGreetingAnimationTranslateUpDuration,
+               gfx::Tween::Type::FAST_OUT_SLOW_IN_2)),
+           // Animate the opacity to 100% with delay.
+           CreateLayerAnimationSequence(
+               ui::LayerAnimationElement::CreatePauseElement(
+                   ui::LayerAnimationElement::AnimatableProperty::OPACITY,
+                   kGreetingAnimationFadeInDelay),
+               CreateOpacityElement(1.f, kGreetingAnimationFadeInDuration))});
+    }
+
     // Set up our pre-animation values.
     footer_->layer()->SetOpacity(0.f);
 
@@ -259,17 +558,23 @@
     return;
   }
 
-  if (!ash::assistant::util::IsFinishingSession(new_visibility))
-    return;
-
-  progress_indicator_->layer()->SetOpacity(0.f);
-  progress_indicator_->layer()->SetTransform(gfx::Transform());
-
   query_view_->SetQuery(ash::AssistantNullQuery());
 
   UpdateFooter();
 }
 
+void AssistantMainStage::MaybeHideGreetingLabel() {
+  if (!IsLayerVisible(greeting_label_))
+    return;
+
+  using ash::assistant::util::CreateLayerAnimationSequence;
+  using ash::assistant::util::CreateOpacityElement;
+
+  greeting_label_->layer()->GetAnimator()->StartAnimation(
+      CreateLayerAnimationSequence(
+          CreateOpacityElement(0.f, kGreetingAnimationFadeOutDuration)));
+}
+
 void AssistantMainStage::UpdateFooter() {
   using ash::assistant::util::CreateLayerAnimationSequence;
   using ash::assistant::util::CreateOpacityElement;
@@ -279,7 +584,7 @@
 
   // The footer is only visible when the progress indicator is not.
   // When it is not visible, it should not process events.
-  bool visible = !progress_indicator_->GetVisible();
+  bool visible = !IsLayerVisible(progress_indicator_);
 
   // Reset visibility to enable animation.
   footer_->SetVisible(true);
@@ -335,7 +640,7 @@
     const ui::CallbackLayerAnimationObserver& observer) {
   // The footer should only process events when visible. It is only visible when
   // the progress indicator is not visible.
-  bool visible = !progress_indicator_->GetVisible();
+  bool visible = !IsLayerVisible(progress_indicator_);
   footer_->set_can_process_events_within_subtree(visible);
   footer_->SetVisible(visible);
 
diff --git a/ash/app_list/views/assistant/assistant_main_stage.h b/ash/app_list/views/assistant/assistant_main_stage.h
index 87f3f11..c060da9a 100644
--- a/ash/app_list/views/assistant/assistant_main_stage.h
+++ b/ash/app_list/views/assistant/assistant_main_stage.h
@@ -26,6 +26,10 @@
 class CallbackLayerAnimationObserver;
 }  // namespace ui
 
+namespace views {
+class Label;
+}  // namespace views
+
 namespace app_list {
 
 // AssistantMainStage is the child of AssistantMainView responsible for
@@ -63,6 +67,13 @@
 
  private:
   void InitLayout();
+  views::View* CreateContentLayoutContainer();
+  void InitGreetingLabel();
+  views::View* CreateMainContentLayoutContainer();
+  views::View* CreateDividerLayoutContainer();
+  views::View* CreateFooterLayoutContainer();
+
+  void MaybeHideGreetingLabel();
 
   void UpdateFooter();
 
@@ -78,6 +89,7 @@
   views::View* horizontal_separator_;
   ash::AssistantQueryView* query_view_;
   ash::UiElementContainerView* ui_element_container_;
+  views::Label* greeting_label_;
   ash::AssistantFooterView* footer_;
 
   std::unique_ptr<ui::CallbackLayerAnimationObserver>
diff --git a/ash/app_list/views/assistant/assistant_main_view.cc b/ash/app_list/views/assistant/assistant_main_view.cc
index 06490ce..7d50339 100644
--- a/ash/app_list/views/assistant/assistant_main_view.cc
+++ b/ash/app_list/views/assistant/assistant_main_view.cc
@@ -4,34 +4,49 @@
 
 #include "ash/app_list/views/assistant/assistant_main_view.h"
 
+#include <algorithm>
 #include <memory>
 
 #include "ash/app_list/views/assistant/assistant_main_stage.h"
 #include "ash/app_list/views/assistant/dialog_plate.h"
 #include "ash/assistant/ui/assistant_ui_constants.h"
 #include "ash/assistant/ui/assistant_view_delegate.h"
+#include "ash/assistant/util/animation_util.h"
+#include "ash/assistant/util/assistant_util.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ui/chromeos/search_box/search_box_constants.h"
 #include "ui/views/layout/box_layout.h"
 
 namespace app_list {
 
+namespace {
+
+// Dialog plate animation.
+constexpr base::TimeDelta kDialogPlateAnimationFadeInDelay =
+    base::TimeDelta::FromMilliseconds(283);
+constexpr base::TimeDelta kDialogPlateAnimationFadeInDuration =
+    base::TimeDelta::FromMilliseconds(167);
+
+}  // namespace
+
 AssistantMainView::AssistantMainView(ash::AssistantViewDelegate* delegate)
     : delegate_(delegate) {
   InitLayout();
+
+  // The view hierarchy will be destructed before AssistantController in Shell,
+  // which owns AssistantViewDelegate, so AssistantViewDelegate is guaranteed to
+  // outlive the AssistantMainStage.
+  delegate_->AddUiModelObserver(this);
 }
 
-AssistantMainView::~AssistantMainView() = default;
+AssistantMainView::~AssistantMainView() {
+  delegate_->RemoveUiModelObserver(this);
+}
 
 const char* AssistantMainView::GetClassName() const {
   return "AssistantMainView";
 }
 
-gfx::Size AssistantMainView::CalculatePreferredSize() const {
-  return gfx::Size(ash::kPreferredWidthDip,
-                   GetHeightForWidth(ash::kPreferredWidthDip));
-}
-
 void AssistantMainView::ChildPreferredSizeChanged(views::View* child) {
   PreferredSizeChanged();
 
@@ -59,6 +74,31 @@
   dialog_plate_->RequestFocus();
 }
 
+void AssistantMainView::OnUiVisibilityChanged(
+    ash::AssistantVisibility new_visibility,
+    ash::AssistantVisibility old_visibility,
+    base::Optional<ash::AssistantEntryPoint> entry_point,
+    base::Optional<ash::AssistantExitPoint> exit_point) {
+  if (!ash::assistant::util::IsStartingSession(new_visibility,
+                                               old_visibility)) {
+    return;
+  }
+
+  // When Assistant is starting a new session, we animate in the appearance of
+  // the dialog plate.
+  using ash::assistant::util::CreateLayerAnimationSequence;
+  using ash::assistant::util::CreateOpacityElement;
+
+  // Animate the dialog plate from 0% to 100% opacity with delay.
+  dialog_plate_->layer()->SetOpacity(0.f);
+  dialog_plate_->layer()->GetAnimator()->StartAnimation(
+      CreateLayerAnimationSequence(
+          ui::LayerAnimationElement::CreatePauseElement(
+              ui::LayerAnimationElement::AnimatableProperty::OPACITY,
+              kDialogPlateAnimationFadeInDelay),
+          CreateOpacityElement(1.f, kDialogPlateAnimationFadeInDuration)));
+}
+
 void AssistantMainView::InitLayout() {
   constexpr int radius = search_box::kSearchBoxBorderCornerRadiusSearchResult;
 
@@ -72,8 +112,10 @@
   layout->set_cross_axis_alignment(
       views::BoxLayout::CrossAxisAlignment::kCenter);
 
-  // Dialog plate.
+  // Dialog plate, which will be animated on its own layer.
   dialog_plate_ = new DialogPlate(delegate_);
+  dialog_plate_->SetPaintToLayer();
+  dialog_plate_->layer()->SetFillsBoundsOpaquely(false);
   AddChildView(dialog_plate_);
 
   // Main stage.
diff --git a/ash/app_list/views/assistant/assistant_main_view.h b/ash/app_list/views/assistant/assistant_main_view.h
index 262919f8..38a3625a 100644
--- a/ash/app_list/views/assistant/assistant_main_view.h
+++ b/ash/app_list/views/assistant/assistant_main_view.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "ash/app_list/app_list_export.h"
+#include "ash/assistant/model/assistant_ui_model_observer.h"
 #include "base/macros.h"
 #include "ui/views/view.h"
 
@@ -20,18 +21,25 @@
 class AssistantMainStage;
 class DialogPlate;
 
-class APP_LIST_EXPORT AssistantMainView : public views::View {
+class APP_LIST_EXPORT AssistantMainView : public views::View,
+                                          public ash::AssistantUiModelObserver {
  public:
   explicit AssistantMainView(ash::AssistantViewDelegate* delegate);
   ~AssistantMainView() override;
 
   // views::View:
   const char* GetClassName() const override;
-  gfx::Size CalculatePreferredSize() const override;
   void ChildPreferredSizeChanged(views::View* child) override;
   void ChildVisibilityChanged(views::View* child) override;
   void RequestFocus() override;
 
+  // ash::AssistantUiModelObserver:
+  void OnUiVisibilityChanged(
+      ash::AssistantVisibility new_visibility,
+      ash::AssistantVisibility old_visibility,
+      base::Optional<ash::AssistantEntryPoint> entry_point,
+      base::Optional<ash::AssistantExitPoint> exit_point) override;
+
   // Returns the first focusable view or nullptr to defer to views::FocusSearch.
   views::View* FindFirstFocusableView();
 
diff --git a/ash/app_list/views/assistant/assistant_page_view.cc b/ash/app_list/views/assistant/assistant_page_view.cc
index 2ef84ac..ee71fb4 100644
--- a/ash/app_list/views/assistant/assistant_page_view.cc
+++ b/ash/app_list/views/assistant/assistant_page_view.cc
@@ -8,6 +8,8 @@
 #include <utility>
 
 #include "ash/app_list/app_list_view_delegate.h"
+#include "ash/app_list/views/app_list_main_view.h"
+#include "ash/app_list/views/app_list_view.h"
 #include "ash/app_list/views/assistant/assistant_main_view.h"
 #include "ash/app_list/views/contents_view.h"
 #include "ash/assistant/model/assistant_ui_model.h"
@@ -28,18 +30,28 @@
 
 namespace {
 
-// The height of the search box in |search_result_page_view_|. It is only for
-// animation.
-constexpr int kSearchBoxHeightDip = 56;
-
 // The shadow elevation value for the shadow of the Assistant search box.
 constexpr int kShadowElevation = 12;
 
+int GetPreferredHeightForAppListState(AppListView* app_list_view) {
+  auto app_list_view_state = app_list_view->app_list_state();
+  switch (app_list_view_state) {
+    case ash::AppListViewState::kHalf:
+    case ash::AppListViewState::kFullscreenSearch:
+      return ash::kMaxHeightEmbeddedDip;
+    default:
+      return ash::kMinHeightEmbeddedDip;
+  }
+}
+
 }  // namespace
 
 AssistantPageView::AssistantPageView(
-    ash::AssistantViewDelegate* assistant_view_delegate)
-    : assistant_view_delegate_(assistant_view_delegate) {
+    ash::AssistantViewDelegate* assistant_view_delegate,
+    ContentsView* contents_view)
+    : assistant_view_delegate_(assistant_view_delegate),
+      contents_view_(contents_view),
+      min_height_dip_(ash::kMinHeightEmbeddedDip) {
   InitLayout();
 
   // |assistant_view_delegate_| could be nullptr in test.
@@ -91,7 +103,26 @@
 }
 
 gfx::Size AssistantPageView::CalculatePreferredSize() const {
-  return gfx::Size(ash::kPreferredWidthDip, ash::kMaxHeightEmbeddedDip);
+  constexpr int width = ash::kPreferredWidthDip;
+  return gfx::Size(width, GetHeightForWidth(width));
+}
+
+int AssistantPageView::GetHeightForWidth(int width) const {
+  int preferred_height =
+      GetPreferredHeightForAppListState(contents_view_->app_list_view());
+
+  preferred_height = std::max(preferred_height, min_height_dip_);
+  return GetChildViewPreferredHeight() > preferred_height
+             ? ash::kMaxHeightEmbeddedDip
+             : preferred_height;
+}
+
+void AssistantPageView::OnBoundsChanged(const gfx::Rect& prev_bounds) {
+  if (!IsDrawn())
+    return;
+
+  // Until Assistant UI is closed, the view may grow in height but not shrink.
+  min_height_dip_ = std::max(min_height_dip_, GetContentsBounds().height());
 }
 
 void AssistantPageView::RequestFocus() {
@@ -119,6 +150,22 @@
   node_data->SetName(l10n_util::GetStringUTF16(IDS_ASH_ASSISTANT_WINDOW));
 }
 
+void AssistantPageView::ChildPreferredSizeChanged(views::View* child) {
+  MaybeUpdateAppListState(child->GetPreferredSize().height());
+  PreferredSizeChanged();
+
+  // After layout events, focus can be lost so we need to explicitly request
+  // on behalf of the child views.
+  RequestFocus();
+}
+
+void AssistantPageView::ChildVisibilityChanged(views::View* child) {
+  if (!child->GetVisible())
+    return;
+
+  MaybeUpdateAppListState(child->GetPreferredSize().height());
+}
+
 void AssistantPageView::OnMouseEvent(ui::MouseEvent* event) {
   switch (event->type()) {
     case ui::ET_MOUSE_PRESSED:
@@ -153,15 +200,6 @@
   return AddShadowBorderToBounds(bounds);
 }
 
-gfx::Rect AssistantPageView::GetSearchBoxBounds() const {
-  gfx::Rect bounds(AppListPage::GetSearchBoxBounds());
-
-  bounds.Offset((bounds.width() - ash::kPreferredWidthDip) / 2, 0);
-  bounds.set_size(gfx::Size(ash::kPreferredWidthDip, kSearchBoxHeightDip));
-
-  return bounds;
-}
-
 views::View* AssistantPageView::GetFirstFocusableView() {
   return GetFocusManager()->GetNextFocusableView(
       this, GetWidget(), /*reverse=*/false, /*dont_loop=*/false);
@@ -204,8 +242,10 @@
   if (!assistant_view_delegate_)
     return;
 
-  if (new_visibility != ash::AssistantVisibility::kVisible)
+  if (new_visibility != ash::AssistantVisibility::kVisible) {
+    min_height_dip_ = ash::kMinHeightEmbeddedDip;
     return;
+  }
 
   const bool prefer_voice = assistant_view_delegate_->IsTabletMode() ||
                             assistant_view_delegate_->IsLaunchWithMicOpen();
@@ -215,6 +255,44 @@
   }
 }
 
+int AssistantPageView::GetChildViewPreferredHeight() const {
+  int height = 0;
+  if (assistant_view_delegate_) {
+    switch (assistant_view_delegate_->GetUiModel()->ui_mode()) {
+      case ash::AssistantUiMode::kLauncherEmbeddedUi:
+        if (assistant_main_view_)
+          height = assistant_main_view_->GetPreferredSize().height();
+        break;
+      case ash::AssistantUiMode::kWebUi:
+        if (assistant_web_view_)
+          height = assistant_web_view_->GetPreferredSize().height();
+        break;
+      case ash::AssistantUiMode::kMainUi:
+      case ash::AssistantUiMode::kMiniUi:
+        NOTREACHED();
+        break;
+    }
+  }
+  return height;
+}
+
+void AssistantPageView::MaybeUpdateAppListState(int child_height) {
+  auto* app_list_view = contents_view_->app_list_view();
+  auto* widget = app_list_view->GetWidget();
+  // |app_list_view| may not be initialized.
+  if (!widget || !widget->IsVisible())
+    return;
+
+  // Update app list view state for |assistant_page_view_|.
+  // Embedded Assistant Ui only has two sizes. The only states change is from
+  // kPeeking to kHalf state.
+  if (app_list_view->app_list_state() != ash::AppListViewState::kPeeking)
+    return;
+
+  if (child_height > GetPreferredHeightForAppListState(app_list_view))
+    app_list_view->SetState(ash::AppListViewState::kHalf);
+}
+
 gfx::Rect AssistantPageView::AddShadowBorderToBounds(
     const gfx::Rect& bounds) const {
   gfx::Rect new_bounds(bounds);
diff --git a/ash/app_list/views/assistant/assistant_page_view.h b/ash/app_list/views/assistant/assistant_page_view.h
index c5c92b1a..461898f 100644
--- a/ash/app_list/views/assistant/assistant_page_view.h
+++ b/ash/app_list/views/assistant/assistant_page_view.h
@@ -20,13 +20,14 @@
 namespace app_list {
 
 class AssistantMainView;
+class ContentsView;
 
 // The Assistant page for the app list.
 class APP_LIST_EXPORT AssistantPageView : public AppListPage,
                                           public ash::AssistantUiModelObserver {
  public:
-  explicit AssistantPageView(
-      ash::AssistantViewDelegate* assistant_view_delegate);
+  AssistantPageView(ash::AssistantViewDelegate* assistant_view_delegate,
+                    ContentsView* contents_view);
   ~AssistantPageView() override;
 
   void InitLayout();
@@ -34,8 +35,12 @@
   // views::View:
   const char* GetClassName() const override;
   gfx::Size CalculatePreferredSize() const override;
+  int GetHeightForWidth(int width) const override;
+  void OnBoundsChanged(const gfx::Rect& prev_bounds) override;
   void RequestFocus() override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+  void ChildPreferredSizeChanged(views::View* child) override;
+  void ChildVisibilityChanged(views::View* child) override;
 
   // ui::EventHandler:
   void OnMouseEvent(ui::MouseEvent* event) override;
@@ -43,7 +48,6 @@
 
   // AppListPage:
   gfx::Rect GetPageBoundsForState(ash::AppListState state) const override;
-  gfx::Rect GetSearchBoxBounds() const override;
   views::View* GetFirstFocusableView() override;
   views::View* GetLastFocusableView() override;
 
@@ -56,15 +60,20 @@
       base::Optional<ash::AssistantEntryPoint> entry_point,
       base::Optional<ash::AssistantExitPoint> exit_point) override;
 
+ private:
+  int GetChildViewPreferredHeight() const;
+  void MaybeUpdateAppListState(int child_height);
   gfx::Rect AddShadowBorderToBounds(const gfx::Rect& bounds) const;
 
- private:
   ash::AssistantViewDelegate* const assistant_view_delegate_;
+  ContentsView* contents_view_;
 
   // Owned by the views hierarchy.
   AssistantMainView* assistant_main_view_ = nullptr;
   ash::AssistantWebView* assistant_web_view_ = nullptr;
 
+  int min_height_dip_;
+
   DISALLOW_COPY_AND_ASSIGN(AssistantPageView);
 };
 
diff --git a/ash/app_list/views/contents_view.cc b/ash/app_list/views/contents_view.cc
index 66ff367..683af005 100644
--- a/ash/app_list/views/contents_view.cc
+++ b/ash/app_list/views/contents_view.cc
@@ -111,7 +111,7 @@
 
   if (app_list_features::IsEmbeddedAssistantUIEnabled()) {
     assistant_page_view_ =
-        new AssistantPageView(view_delegate->GetAssistantViewDelegate());
+        new AssistantPageView(view_delegate->GetAssistantViewDelegate(), this);
     assistant_page_view_->SetVisible(false);
     AddLauncherPage(assistant_page_view_,
                     ash::AppListState::kStateEmbeddedAssistant);
@@ -121,6 +121,7 @@
   DCHECK_GE(initial_page_index, 0);
 
   page_before_search_ = initial_page_index;
+  page_before_assistant_ = initial_page_index;
   // Must only call SetTotalPages once all the launcher pages have been added
   // (as it will trigger a SelectedPageChanged call).
   pagination_model_.SetTotalPages(app_list_pages_.size());
@@ -197,7 +198,10 @@
   DCHECK(state != ash::AppListState::kStateSearchResults &&
          state != ash::AppListState::kStateEmbeddedAssistant);
 
-  SetActiveStateInternal(GetPageIndexForState(state), false, animate);
+  const int page_index = GetPageIndexForState(state);
+  page_before_search_ = page_index;
+  page_before_assistant_ = page_index;
+  SetActiveStateInternal(page_index, animate);
 }
 
 int ContentsView::GetActivePageIndex() const {
@@ -242,15 +246,10 @@
   return horizontal_page_container_->apps_container_view();
 }
 
-void ContentsView::SetActiveStateInternal(int page_index,
-                                          bool show_search_or_assistant_results,
-                                          bool animate) {
+void ContentsView::SetActiveStateInternal(int page_index, bool animate) {
   if (!GetPageView(page_index)->GetVisible())
     return;
 
-  if (!show_search_or_assistant_results)
-    page_before_search_ = page_index;
-
   app_list_pages_[GetActivePageIndex()]->OnWillBeHidden();
 
   // Start animating to the new page.
@@ -283,7 +282,8 @@
   // Hide or Show results
   GetPageView(search_page)->SetVisible(show);
 
-  SetActiveStateInternal(show ? search_page : page_before_search_, show,
+  page_before_assistant_ = show ? search_page : page_before_search_;
+  SetActiveStateInternal(show ? search_page : page_before_search_,
                          !AppListView::ShortAnimationsForTesting());
 }
 
@@ -301,16 +301,24 @@
   if (show)
     GetPageView(assistant_page)->RequestFocus();
 
-  // Embedded Assistant UI can only be transitioned from/to
-  // |search_result_page_view_|.
   const int search_results_page =
       GetPageIndexForState(ash::AppListState::kStateSearchResults);
   DCHECK_GE(search_results_page, 0);
-  GetPageView(search_results_page)->SetVisible(!show);
-  SetActiveStateInternal(show ? assistant_page : search_results_page,
-                         /*show_search_or_assistant_results=*/true,
-                         /*animate=*/false);
-  expand_arrow_view_->layer()->SetOpacity(0.0f);
+  GetPageView(page_before_assistant_)->SetVisible(!show);
+
+  // No animation when transiting from/to |search_results_page| and in test.
+  const bool animate = !AppListView::ShortAnimationsForTesting() &&
+                       page_before_assistant_ != search_results_page;
+  SetActiveStateInternal(show ? assistant_page : page_before_assistant_,
+                         animate);
+  // If |page_before_assistant_| is kStateApps, we need to set app_list_view to
+  // kPeeking and layout the suggestion chips.
+  if (!show && page_before_assistant_ ==
+                   GetPageIndexForState(ash::AppListState::kStateApps)) {
+    GetSearchBoxView()->ClearSearch();
+    GetSearchBoxView()->SetSearchBoxActive(false, ui::ET_UNKNOWN);
+    GetAppsContainerView()->Layout();
+  }
 }
 
 bool ContentsView::IsShowingEmbeddedAssistantUI() const {
@@ -347,7 +355,6 @@
     // Animate linearly (the PaginationModel handles easing).
     gfx::Rect bounds(
         gfx::Tween::RectValueBetween(progress, from_rect, to_rect));
-
     page->SetBoundsRect(bounds);
 
     if (ShouldLayoutPage(page, current_state, target_state))
@@ -396,15 +403,12 @@
 void ContentsView::UpdateExpandArrowOpacity(double progress,
                                             ash::AppListState current_state,
                                             ash::AppListState target_state) {
-  if ((current_state == ash::AppListState::kStateSearchResults ||
-       current_state == ash::AppListState::kStateEmbeddedAssistant) &&
-      target_state == ash::AppListState::kStateApps) {
+  if (target_state == ash::AppListState::kStateApps) {
     // Fade in the expand arrow when search results page is closed.
     expand_arrow_view_->layer()->SetOpacity(
         gfx::Tween::FloatValueBetween(progress, 0, 1));
-  } else if ((target_state == ash::AppListState::kStateSearchResults ||
-              target_state == ash::AppListState::kStateEmbeddedAssistant) &&
-             current_state == ash::AppListState::kStateApps) {
+  } else if (target_state == ash::AppListState::kStateSearchResults ||
+             target_state == ash::AppListState::kStateEmbeddedAssistant) {
     // Fade out the expand arrow when search results page is opened.
     expand_arrow_view_->layer()->SetOpacity(
         gfx::Tween::FloatValueBetween(progress, 1, 0));
diff --git a/ash/app_list/views/contents_view.h b/ash/app_list/views/contents_view.h
index 0e81c12..ed402742 100644
--- a/ash/app_list/views/contents_view.h
+++ b/ash/app_list/views/contents_view.h
@@ -205,11 +205,8 @@
   void RemoveSearchBoxUpdateObserver(SearchBoxUpdateObserver* observer);
 
  private:
-  // Sets the active launcher page, accounting for whether the change is for
-  // search results.
-  void SetActiveStateInternal(int page_index,
-                              bool show_search_or_assistant_results,
-                              bool animate);
+  // Sets the active launcher page.
+  void SetActiveStateInternal(int page_index, bool animate);
 
   // Invoked when active view is changed.
   void ActivePageChanged();
@@ -290,6 +287,9 @@
   // The page that was showing before ShowSearchResults(true) was invoked.
   int page_before_search_ = 0;
 
+  // The page that was showing before ShowEmbeddedAssistantUi(true) was invoked.
+  int page_before_assistant_ = 0;
+
   // Manages the pagination for the launcher pages.
   ash::PaginationModel pagination_model_;
 
diff --git a/ash/app_list/views/folder_header_view.cc b/ash/app_list/views/folder_header_view.cc
index 877309c..843d801 100644
--- a/ash/app_list/views/folder_header_view.cc
+++ b/ash/app_list/views/folder_header_view.cc
@@ -44,6 +44,11 @@
 
   ~FolderNameView() override = default;
 
+  gfx::Size CalculatePreferredSize() const override {
+    return gfx::Size(kMaxFolderNameWidth,
+                     AppListConfig::instance().folder_header_height());
+  }
+
   void OnFocus() override {
     SetText(base::UTF8ToUTF16(folder_header_view_->folder_item_->name()));
     starting_name_ = text();
diff --git a/ash/ash_prefs.cc b/ash/ash_prefs.cc
index ade78c0e..ba0d08e 100644
--- a/ash/ash_prefs.cc
+++ b/ash/ash_prefs.cc
@@ -9,7 +9,7 @@
 #include "ash/assistant/assistant_controller.h"
 #include "ash/detachable_base/detachable_base_handler.h"
 #include "ash/display/display_prefs.h"
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/login/login_screen_controller.h"
 #include "ash/magnifier/docked_magnifier_controller_impl.h"
 #include "ash/shelf/shelf_controller.h"
@@ -37,7 +37,7 @@
   BluetoothPowerController::RegisterProfilePrefs(registry);
   CapsLockNotificationController::RegisterProfilePrefs(registry, for_test);
   DockedMagnifierControllerImpl::RegisterProfilePrefs(registry, for_test);
-  KioskNextShellController::RegisterProfilePrefs(registry, for_test);
+  KioskNextShellControllerImpl::RegisterProfilePrefs(registry, for_test);
   LoginScreenController::RegisterProfilePrefs(registry, for_test);
   LogoutButtonTray::RegisterProfilePrefs(registry);
   MessageCenterController::RegisterProfilePrefs(registry);
diff --git a/ash/assistant/ui/assistant_ui_constants.h b/ash/assistant/ui/assistant_ui_constants.h
index 757f555..119bdc6 100644
--- a/ash/assistant/ui/assistant_ui_constants.h
+++ b/ash/assistant/ui/assistant_ui_constants.h
@@ -21,6 +21,7 @@
 constexpr int kMiniUiCornerRadiusDip = 24;
 constexpr int kMaxHeightDip = 640;
 constexpr int kMaxHeightEmbeddedDip = 440;
+constexpr int kMinHeightEmbeddedDip = 180;
 constexpr int kPaddingDip = 14;
 constexpr int kPreferredWidthDip = 640;
 constexpr int kSpacingDip = 8;
diff --git a/ash/custom_tab/arc_custom_tab_view.cc b/ash/custom_tab/arc_custom_tab_view.cc
index b19e082..52ea32a8 100644
--- a/ash/custom_tab/arc_custom_tab_view.cc
+++ b/ash/custom_tab/arc_custom_tab_view.cc
@@ -114,6 +114,10 @@
   native_view_container_->AddObserver(this);
 }
 
+gfx::NativeView ArcCustomTabView::GetHostView() {
+  return host_->native_view();
+}
+
 void ArcCustomTabView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
   if (previous_bounds.size() != size()) {
     InvalidateLayout();
diff --git a/ash/custom_tab/arc_custom_tab_view.h b/ash/custom_tab/arc_custom_tab_view.h
index 1b3d4297..b70af7b 100644
--- a/ash/custom_tab/arc_custom_tab_view.h
+++ b/ash/custom_tab/arc_custom_tab_view.h
@@ -37,6 +37,7 @@
  private:
   // ArcCustomTab:
   void Attach(gfx::NativeView view) override;
+  gfx::NativeView GetHostView() override;
 
   // views::View:
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
diff --git a/ash/display/screen_orientation_controller_unittest.cc b/ash/display/screen_orientation_controller_unittest.cc
index f5c4c21..844ce6f 100644
--- a/ash/display/screen_orientation_controller_unittest.cc
+++ b/ash/display/screen_orientation_controller_unittest.cc
@@ -745,10 +745,11 @@
     set_start_session(false);
     AshTestBase::SetUp();
 
-    client_ = BindMockKioskNextShellClient();
+    client_ = std::make_unique<MockKioskNextShellClient>();
   }
 
   void TearDown() override {
+    client_.reset();
     home_screen_window_.reset();
     AshTestBase::TearDown();
   }
diff --git a/ash/home_screen/home_screen_presenter.cc b/ash/home_screen/home_screen_presenter.cc
index 2011c9f..ff2b478 100644
--- a/ash/home_screen/home_screen_presenter.cc
+++ b/ash/home_screen/home_screen_presenter.cc
@@ -8,7 +8,7 @@
 
 #include "ash/home_screen/home_screen_controller.h"
 #include "ash/home_screen/home_screen_delegate.h"
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/shell.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
diff --git a/ash/kiosk_next/kiosk_next_home_controller_unittest.cc b/ash/kiosk_next/kiosk_next_home_controller_unittest.cc
index 2cd7c988..10ec17c 100644
--- a/ash/kiosk_next/kiosk_next_home_controller_unittest.cc
+++ b/ash/kiosk_next/kiosk_next_home_controller_unittest.cc
@@ -32,15 +32,15 @@
     scoped_feature_list_.InitAndEnableFeature(features::kKioskNextShell);
     set_start_session(false);
     AshTestBase::SetUp();
-    client_ = BindMockKioskNextShellClient();
+    client_ = std::make_unique<MockKioskNextShellClient>();
     LogInKioskNextUser(GetSessionControllerClient());
     SetUpHomeWindow();
   }
 
   void TearDown() override {
     home_screen_window_.reset();
-    AshTestBase::TearDown();
     client_.reset();
+    AshTestBase::TearDown();
   }
 
   void SetUpHomeWindow() {
diff --git a/ash/kiosk_next/kiosk_next_shell_controller.cc b/ash/kiosk_next/kiosk_next_shell_controller_impl.cc
similarity index 78%
rename from ash/kiosk_next/kiosk_next_shell_controller.cc
rename to ash/kiosk_next/kiosk_next_shell_controller_impl.cc
index d4623aad..51ca121 100644
--- a/ash/kiosk_next/kiosk_next_shell_controller.cc
+++ b/ash/kiosk_next/kiosk_next_shell_controller_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 
 #include <memory>
 #include <utility>
@@ -52,12 +52,12 @@
 
 }  // namespace
 
-KioskNextShellController::KioskNextShellController() = default;
+KioskNextShellControllerImpl::KioskNextShellControllerImpl() = default;
 
-KioskNextShellController::~KioskNextShellController() = default;
+KioskNextShellControllerImpl::~KioskNextShellControllerImpl() = default;
 
 // static
-void KioskNextShellController::RegisterProfilePrefs(
+void KioskNextShellControllerImpl::RegisterProfilePrefs(
     PrefRegistrySimple* registry,
     bool for_test) {
   if (for_test) {
@@ -67,43 +67,40 @@
   }
 }
 
-void KioskNextShellController::BindRequest(
-    mojom::KioskNextShellControllerRequest request) {
-  bindings_.AddBinding(this, std::move(request));
+void KioskNextShellControllerImpl::SetClientAndLaunchSession(
+    KioskNextShellClient* client) {
+  DCHECK_NE(!!client, !!client_);
+  client_ = client;
+  LaunchKioskNextShellIfEnabled();
 }
 
-bool KioskNextShellController::IsEnabled() {
+bool KioskNextShellControllerImpl::IsEnabled() {
   return kiosk_next_enabled_;
 }
 
-void KioskNextShellController::AddObserver(KioskNextShellObserver* observer) {
+void KioskNextShellControllerImpl::AddObserver(
+    KioskNextShellObserver* observer) {
   observer_list_.AddObserver(observer);
 }
 
-void KioskNextShellController::RemoveObserver(
+void KioskNextShellControllerImpl::RemoveObserver(
     KioskNextShellObserver* observer) {
   observer_list_.RemoveObserver(observer);
 }
 
-void KioskNextShellController::SetClient(
-    mojom::KioskNextShellClientPtr client) {
-  kiosk_next_shell_client_ = std::move(client);
-  LaunchKioskNextShellIfEnabled();
-}
-
-void KioskNextShellController::OnActiveUserPrefServiceChanged(
+void KioskNextShellControllerImpl::OnActiveUserPrefServiceChanged(
     PrefService* pref_service) {
   LaunchKioskNextShellIfEnabled();
 }
 
-void KioskNextShellController::LaunchKioskNextShellIfEnabled() {
+void KioskNextShellControllerImpl::LaunchKioskNextShellIfEnabled() {
   SessionControllerImpl* session_controller =
       Shell::Get()->session_controller();
   PrefService* pref_service = session_controller->GetPrimaryUserPrefService();
   if (!pref_service)
     return;
 
-  if (!kiosk_next_shell_client_.is_bound())
+  if (!client_)
     return;
 
   bool prev_kiosk_next_enabled = kiosk_next_enabled_;
@@ -119,7 +116,7 @@
       kiosk_next_home_controller_.get());
   Shell::Get()->RemoveAppListController();
 
-  kiosk_next_shell_client_->LaunchKioskNextShell(
+  client_->LaunchKioskNextShell(
       session_controller->GetPrimaryUserSession()->user_info.account_id);
   UMA_HISTOGRAM_BOOLEAN("KioskNextShell.Launched", true);
 
diff --git a/ash/kiosk_next/kiosk_next_shell_controller.h b/ash/kiosk_next/kiosk_next_shell_controller_impl.h
similarity index 61%
rename from ash/kiosk_next/kiosk_next_shell_controller.h
rename to ash/kiosk_next/kiosk_next_shell_controller_impl.h
index 6de77c06..acb5c9b2 100644
--- a/ash/kiosk_next/kiosk_next_shell_controller.h
+++ b/ash/kiosk_next/kiosk_next_shell_controller_impl.h
@@ -2,18 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_KIOSK_NEXT_KIOSK_NEXT_SHELL_CONTROLLER_H_
-#define ASH_KIOSK_NEXT_KIOSK_NEXT_SHELL_CONTROLLER_H_
+#ifndef ASH_KIOSK_NEXT_KIOSK_NEXT_SHELL_CONTROLLER_IMPL_H_
+#define ASH_KIOSK_NEXT_KIOSK_NEXT_SHELL_CONTROLLER_IMPL_H_
 
 #include <memory>
 
 #include "ash/ash_export.h"
 #include "ash/kiosk_next/kiosk_next_shell_observer.h"
-#include "ash/public/interfaces/kiosk_next_shell.mojom.h"
+#include "ash/public/cpp/kiosk_next_shell.h"
 #include "ash/session/session_observer.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
 
 class PrefRegistrySimple;
 
@@ -22,21 +21,20 @@
 class KioskNextHomeController;
 class ShelfModel;
 
-// KioskNextShellController allows an ash consumer to manage a Kiosk Next
+// KioskNextShellControllerImpl allows an ash consumer to manage a Kiosk Next
 // session. During this session most system functions are disabled and we launch
 // a specific app (Kiosk Next Home) that takes the whole screen.
-class ASH_EXPORT KioskNextShellController
-    : public mojom::KioskNextShellController,
-      public SessionObserver {
+class ASH_EXPORT KioskNextShellControllerImpl : public KioskNextShellController,
+                                                public SessionObserver {
  public:
-  KioskNextShellController();
-  ~KioskNextShellController() override;
+  KioskNextShellControllerImpl();
+  ~KioskNextShellControllerImpl() override;
 
   // Register prefs related to the Kiosk Next Shell.
   static void RegisterProfilePrefs(PrefRegistrySimple* registry, bool for_test);
 
-  // Binds the mojom::KioskNextShellController interface to this object.
-  void BindRequest(mojom::KioskNextShellControllerRequest request);
+  // KioskNextShellController:
+  void SetClientAndLaunchSession(KioskNextShellClient* client) override;
 
   // Returns if the Kiosk Next Shell is enabled for the current user. If there's
   // no signed-in user, this returns false.
@@ -45,9 +43,6 @@
   void AddObserver(KioskNextShellObserver* observer);
   void RemoveObserver(KioskNextShellObserver* observer);
 
-  // mojom::KioskNextShellController:
-  void SetClient(mojom::KioskNextShellClientPtr client) override;
-
   // SessionObserver:
   void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
 
@@ -58,8 +53,8 @@
   // available.
   void LaunchKioskNextShellIfEnabled();
 
-  mojom::KioskNextShellClientPtr kiosk_next_shell_client_;
-  mojo::BindingSet<mojom::KioskNextShellController> bindings_;
+  KioskNextShellClient* client_ = nullptr;
+
   base::ObserverList<KioskNextShellObserver> observer_list_;
   ScopedSessionObserver session_observer_{this};
   bool kiosk_next_enabled_ = false;
@@ -68,13 +63,13 @@
   std::unique_ptr<KioskNextHomeController> kiosk_next_home_controller_;
 
   // When KioskNextShell is enabled, only the home button and back button are
-  // made visible on the Shelf. KioskNextShellController therefore hosts its own
-  // ShelfModel to control the entries visible on the shelf.
+  // made visible on the Shelf. KioskNextShellControllerImpl therefore hosts its
+  // own ShelfModel to control the entries visible on the shelf.
   std::unique_ptr<ShelfModel> shelf_model_;
 
-  DISALLOW_COPY_AND_ASSIGN(KioskNextShellController);
+  DISALLOW_COPY_AND_ASSIGN(KioskNextShellControllerImpl);
 };
 
 }  // namespace ash
 
-#endif  // ASH_KIOSK_NEXT_KIOSK_NEXT_SHELL_CONTROLLER_H_
+#endif  // ASH_KIOSK_NEXT_KIOSK_NEXT_SHELL_CONTROLLER_IMPL_H_
diff --git a/ash/kiosk_next/kiosk_next_shell_controller_unittest.cc b/ash/kiosk_next/kiosk_next_shell_controller_unittest.cc
index df5d740..bfb966a 100644
--- a/ash/kiosk_next/kiosk_next_shell_controller_unittest.cc
+++ b/ash/kiosk_next/kiosk_next_shell_controller_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 
 #include <memory>
 
@@ -18,7 +18,7 @@
 namespace ash {
 namespace {
 
-KioskNextShellController* GetKioskNextShellController() {
+KioskNextShellControllerImpl* GetKioskNextShellController() {
   return Shell::Get()->kiosk_next_shell_controller();
 }
 
@@ -42,13 +42,13 @@
   void SetUp() override {
     set_start_session(false);
     AshTestBase::SetUp();
-    client_ = BindMockKioskNextShellClient();
+    client_ = std::make_unique<MockKioskNextShellClient>();
     scoped_feature_list_.InitAndEnableFeature(features::kKioskNextShell);
   }
 
   void TearDown() override {
-    AshTestBase::TearDown();
     client_.reset();
+    AshTestBase::TearDown();
   }
 
  protected:
diff --git a/ash/kiosk_next/mock_kiosk_next_shell_client.cc b/ash/kiosk_next/mock_kiosk_next_shell_client.cc
index 405e707..f9f78b7 100644
--- a/ash/kiosk_next/mock_kiosk_next_shell_client.cc
+++ b/ash/kiosk_next/mock_kiosk_next_shell_client.cc
@@ -4,27 +4,14 @@
 
 #include "ash/kiosk_next/mock_kiosk_next_shell_client.h"
 
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
-#include "ash/shell.h"
-
 namespace ash {
 
-MockKioskNextShellClient::MockKioskNextShellClient() = default;
-
-MockKioskNextShellClient::~MockKioskNextShellClient() = default;
-
-mojom::KioskNextShellClientPtr
-MockKioskNextShellClient::CreateInterfacePtrAndBind() {
-  mojom::KioskNextShellClientPtr ptr;
-  binding_.Bind(mojo::MakeRequest(&ptr));
-  return ptr;
+MockKioskNextShellClient::MockKioskNextShellClient() {
+  KioskNextShellController::Get()->SetClientAndLaunchSession(this);
 }
 
-std::unique_ptr<MockKioskNextShellClient> BindMockKioskNextShellClient() {
-  auto client = std::make_unique<MockKioskNextShellClient>();
-  Shell::Get()->kiosk_next_shell_controller()->SetClient(
-      client->CreateInterfacePtrAndBind());
-  return client;
+MockKioskNextShellClient::~MockKioskNextShellClient() {
+  KioskNextShellController::Get()->SetClientAndLaunchSession(nullptr);
 }
 
 }  // namespace ash
diff --git a/ash/kiosk_next/mock_kiosk_next_shell_client.h b/ash/kiosk_next/mock_kiosk_next_shell_client.h
index 6e3b5a56..9830ac0 100644
--- a/ash/kiosk_next/mock_kiosk_next_shell_client.h
+++ b/ash/kiosk_next/mock_kiosk_next_shell_client.h
@@ -5,36 +5,25 @@
 #ifndef ASH_KIOSK_NEXT_MOCK_KIOSK_NEXT_SHELL_CLIENT_H_
 #define ASH_KIOSK_NEXT_MOCK_KIOSK_NEXT_SHELL_CLIENT_H_
 
-#include <memory>
-
-#include "ash/public/interfaces/kiosk_next_shell.mojom.h"
+#include "ash/public/cpp/kiosk_next_shell.h"
 #include "base/macros.h"
 #include "components/account_id/account_id.h"
-#include "mojo/public/cpp/bindings/binding.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace ash {
 
-class MockKioskNextShellClient : public mojom::KioskNextShellClient {
+class MockKioskNextShellClient : public KioskNextShellClient {
  public:
   MockKioskNextShellClient();
   ~MockKioskNextShellClient() override;
 
-  mojom::KioskNextShellClientPtr CreateInterfacePtrAndBind();
-
-  // mojom::KioskNextShellClient:
+  // KioskNextShellClient:
   MOCK_METHOD1(LaunchKioskNextShell, void(const AccountId& account_id));
 
  private:
-  mojo::Binding<mojom::KioskNextShellClient> binding_{this};
-
   DISALLOW_COPY_AND_ASSIGN(MockKioskNextShellClient);
 };
 
-// Helper function to bind a KioskNextShellClient so that it receives mojo
-// calls.
-std::unique_ptr<MockKioskNextShellClient> BindMockKioskNextShellClient();
-
 }  // namespace ash
 
 #endif  // ASH_KIOSK_NEXT_MOCK_KIOSK_NEXT_SHELL_CLIENT_H_
diff --git a/ash/media/media_controller.cc b/ash/media/media_controller_impl.cc
similarity index 72%
rename from ash/media/media_controller.cc
rename to ash/media/media_controller_impl.cc
index 96f7f1c..6be6e1a4 100644
--- a/ash/media/media_controller.cc
+++ b/ash/media/media_controller_impl.cc
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/media/media_controller.h"
+#include "ash/media/media_controller_impl.h"
 
+#include "ash/public/cpp/media_client.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "base/bind.h"
@@ -29,7 +30,7 @@
 
 }  // namespace
 
-MediaController::MediaController(service_manager::Connector* connector)
+MediaControllerImpl::MediaControllerImpl(service_manager::Connector* connector)
     : connector_(connector) {
   // If media session media key handling is enabled this will setup a connection
   // and bind an observer to the media session service.
@@ -37,42 +38,35 @@
     GetMediaSessionController();
 }
 
-MediaController::~MediaController() = default;
+MediaControllerImpl::~MediaControllerImpl() = default;
 
-void MediaController::BindRequest(mojom::MediaControllerRequest request) {
-  bindings_.AddBinding(this, std::move(request));
-}
-
-void MediaController::AddObserver(MediaCaptureObserver* observer) {
+void MediaControllerImpl::AddObserver(MediaCaptureObserver* observer) {
   observers_.AddObserver(observer);
 }
 
-void MediaController::RemoveObserver(MediaCaptureObserver* observer) {
+void MediaControllerImpl::RemoveObserver(MediaCaptureObserver* observer) {
   observers_.RemoveObserver(observer);
 }
 
-void MediaController::SetClient(mojom::MediaClientAssociatedPtrInfo client) {
-  client_.Bind(std::move(client));
+void MediaControllerImpl::SetClient(MediaClient* client) {
+  client_ = client;
 
   // When |client_| is changed or encounters an error we should reset the
   // |force_media_client_key_handling_| bit.
   ResetForceMediaClientKeyHandling();
-  client_.set_connection_error_handler(
-      base::BindOnce(&MediaController::ResetForceMediaClientKeyHandling,
-                     base::Unretained(this)));
 }
 
-void MediaController::SetForceMediaClientKeyHandling(bool enabled) {
+void MediaControllerImpl::SetForceMediaClientKeyHandling(bool enabled) {
   force_media_client_key_handling_ = enabled;
 }
 
-void MediaController::NotifyCaptureState(
-    const base::flat_map<AccountId, mojom::MediaCaptureState>& capture_states) {
+void MediaControllerImpl::NotifyCaptureState(
+    const base::flat_map<AccountId, MediaCaptureState>& capture_states) {
   for (auto& observer : observers_)
     observer.OnMediaCaptureChanged(capture_states);
 }
 
-void MediaController::HandleMediaPlayPause() {
+void MediaControllerImpl::HandleMediaPlayPause() {
   if (Shell::Get()->session_controller()->IsScreenLocked())
     return;
 
@@ -89,13 +83,11 @@
     switch (media_session_info_->playback_state) {
       case media_session::mojom::MediaPlaybackState::kPaused:
         GetMediaSessionController()->Resume();
-        ui::RecordMediaHardwareKeyAction(
-            ui::MediaHardwareKeyAction::kPlay);
+        ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kPlay);
         break;
       case media_session::mojom::MediaPlaybackState::kPlaying:
         GetMediaSessionController()->Suspend();
-        ui::RecordMediaHardwareKeyAction(
-            ui::MediaHardwareKeyAction::kPause);
+        ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kPause);
         break;
     }
 
@@ -104,19 +96,17 @@
 
   // If media session does not handle the key then we don't know whether the
   // action will play or pause so we should record a generic "play/pause".
-  ui::RecordMediaHardwareKeyAction(
-      ui::MediaHardwareKeyAction::kPlayPause);
+  ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kPlayPause);
 
   if (client_)
     client_->HandleMediaPlayPause();
 }
 
-void MediaController::HandleMediaNextTrack() {
+void MediaControllerImpl::HandleMediaNextTrack() {
   if (Shell::Get()->session_controller()->IsScreenLocked())
     return;
 
-  ui::RecordMediaHardwareKeyAction(
-      ui::MediaHardwareKeyAction::kNextTrack);
+  ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kNextTrack);
 
   // If the |client_| is force handling the keys then we should forward them.
   if (client_ && force_media_client_key_handling_) {
@@ -135,12 +125,11 @@
     client_->HandleMediaNextTrack();
 }
 
-void MediaController::HandleMediaPrevTrack() {
+void MediaControllerImpl::HandleMediaPrevTrack() {
   if (Shell::Get()->session_controller()->IsScreenLocked())
     return;
 
-  ui::RecordMediaHardwareKeyAction(
-      ui::MediaHardwareKeyAction::kPreviousTrack);
+  ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kPreviousTrack);
 
   // If the |client_| is force handling the keys then we should forward them.
   if (client_ && force_media_client_key_handling_) {
@@ -159,22 +148,22 @@
     client_->HandleMediaPrevTrack();
 }
 
-void MediaController::RequestCaptureState() {
+void MediaControllerImpl::RequestCaptureState() {
   if (client_)
     client_->RequestCaptureState();
 }
 
-void MediaController::SuspendMediaSessions() {
+void MediaControllerImpl::SuspendMediaSessions() {
   if (client_)
     client_->SuspendMediaSessions();
 }
 
-void MediaController::MediaSessionInfoChanged(
+void MediaControllerImpl::MediaSessionInfoChanged(
     media_session::mojom::MediaSessionInfoPtr session_info) {
   media_session_info_ = std::move(session_info);
 }
 
-void MediaController::MediaSessionActionsChanged(
+void MediaControllerImpl::MediaSessionActionsChanged(
     const std::vector<media_session::mojom::MediaSessionAction>& actions) {
   supported_media_session_action_ = false;
 
@@ -186,22 +175,19 @@
   }
 }
 
-void MediaController::SetMediaSessionControllerForTest(
+void MediaControllerImpl::SetMediaSessionControllerForTest(
     media_session::mojom::MediaControllerPtr controller) {
   media_session_controller_ptr_ = std::move(controller);
   BindMediaControllerObserver();
 }
 
-void MediaController::FlushForTesting() {
-  if (client_)
-    client_.FlushForTesting();
-
+void MediaControllerImpl::FlushForTesting() {
   if (media_session_controller_ptr_)
     media_session_controller_ptr_.FlushForTesting();
 }
 
 media_session::mojom::MediaController*
-MediaController::GetMediaSessionController() {
+MediaControllerImpl::GetMediaSessionController() {
   // |connector_| can be null in tests.
   if (connector_ && !media_session_controller_ptr_.is_bound()) {
     media_session::mojom::MediaControllerManagerPtr controller_manager_ptr;
@@ -211,7 +197,7 @@
         mojo::MakeRequest(&media_session_controller_ptr_));
 
     media_session_controller_ptr_.set_connection_error_handler(
-        base::BindRepeating(&MediaController::OnMediaSessionControllerError,
+        base::BindRepeating(&MediaControllerImpl::OnMediaSessionControllerError,
                             base::Unretained(this)));
 
     BindMediaControllerObserver();
@@ -220,12 +206,12 @@
   return media_session_controller_ptr_.get();
 }
 
-void MediaController::OnMediaSessionControllerError() {
+void MediaControllerImpl::OnMediaSessionControllerError() {
   media_session_controller_ptr_.reset();
   supported_media_session_action_ = false;
 }
 
-void MediaController::BindMediaControllerObserver() {
+void MediaControllerImpl::BindMediaControllerObserver() {
   if (!media_session_controller_ptr_.is_bound())
     return;
 
@@ -234,13 +220,13 @@
   media_session_controller_ptr_->AddObserver(std::move(observer));
 }
 
-bool MediaController::ShouldUseMediaSession() {
+bool MediaControllerImpl::ShouldUseMediaSession() {
   return base::FeatureList::IsEnabled(media::kHardwareMediaKeyHandling) &&
          GetMediaSessionController() && supported_media_session_action_ &&
          !media_session_info_.is_null();
 }
 
-void MediaController::ResetForceMediaClientKeyHandling() {
+void MediaControllerImpl::ResetForceMediaClientKeyHandling() {
   force_media_client_key_handling_ = false;
 }
 
diff --git a/ash/media/media_controller.h b/ash/media/media_controller_impl.h
similarity index 79%
rename from ash/media/media_controller.h
rename to ash/media/media_controller_impl.h
index 120f656..ef7d069 100644
--- a/ash/media/media_controller.h
+++ b/ash/media/media_controller_impl.h
@@ -2,16 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_MEDIA_MEDIA_CONTROLLER_H_
-#define ASH_MEDIA_MEDIA_CONTROLLER_H_
+#ifndef ASH_MEDIA_MEDIA_CONTROLLER_IMPL_H_
+#define ASH_MEDIA_MEDIA_CONTROLLER_IMPL_H_
 
 #include "ash/ash_export.h"
-#include "ash/public/interfaces/media.mojom.h"
+#include "ash/public/cpp/media_controller.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
-#include "components/account_id/interfaces/account_id.mojom.h"
-#include "mojo/public/cpp/bindings/associated_binding.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "components/account_id/account_id.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "services/media_session/public/mojom/media_controller.mojom.h"
 
 namespace service_manager {
@@ -20,13 +19,14 @@
 
 namespace ash {
 
+class MediaClient;
+
 // Forwards notifications from the MediaController to observers.
 class MediaCaptureObserver {
  public:
   // Called when media capture state has changed.
   virtual void OnMediaCaptureChanged(
-      const base::flat_map<AccountId, mojom::MediaCaptureState>&
-          capture_states) = 0;
+      const base::flat_map<AccountId, MediaCaptureState>& capture_states) = 0;
 
  protected:
   virtual ~MediaCaptureObserver() {}
@@ -35,25 +35,22 @@
 // Provides the MediaController interface to the outside world. This lets a
 // consumer of ash provide a MediaClient, which we will dispatch to if one has
 // been provided to us.
-class ASH_EXPORT MediaController
-    : public mojom::MediaController,
+class ASH_EXPORT MediaControllerImpl
+    : public MediaController,
       public media_session::mojom::MediaControllerObserver {
  public:
   // |connector| can be null in tests.
-  explicit MediaController(service_manager::Connector* connector);
-  ~MediaController() override;
-
-  void BindRequest(mojom::MediaControllerRequest request);
+  explicit MediaControllerImpl(service_manager::Connector* connector);
+  ~MediaControllerImpl() override;
 
   void AddObserver(MediaCaptureObserver* observer);
   void RemoveObserver(MediaCaptureObserver* observer);
 
-  // mojom::MediaController:
-  void SetClient(mojom::MediaClientAssociatedPtrInfo client) override;
+  // MediaController:
+  void SetClient(MediaClient* client) override;
   void SetForceMediaClientKeyHandling(bool enabled) override;
-  void NotifyCaptureState(
-      const base::flat_map<AccountId, mojom::MediaCaptureState>& capture_states)
-      override;
+  void NotifyCaptureState(const base::flat_map<AccountId, MediaCaptureState>&
+                              capture_states) override;
 
   // If media session accelerators are enabled then these methods will use the
   // media session service to control playback. Otherwise it will forward to
@@ -133,16 +130,13 @@
   mojo::Binding<media_session::mojom::MediaControllerObserver>
       media_controller_observer_binding_{this};
 
-  // Bindings for users of the mojo interface.
-  mojo::BindingSet<mojom::MediaController> bindings_;
-
-  mojom::MediaClientAssociatedPtr client_;
+  MediaClient* client_ = nullptr;
 
   base::ObserverList<MediaCaptureObserver>::Unchecked observers_;
 
-  DISALLOW_COPY_AND_ASSIGN(MediaController);
+  DISALLOW_COPY_AND_ASSIGN(MediaControllerImpl);
 };
 
 }  // namespace ash
 
-#endif  // ASH_MEDIA_MEDIA_CONTROLLER_H_
+#endif  // ASH_MEDIA_MEDIA_CONTROLLER_IMPL_H_
diff --git a/ash/media/media_controller_unittest.cc b/ash/media/media_controller_unittest.cc
index 29340c6..7797bb6b 100644
--- a/ash/media/media_controller_unittest.cc
+++ b/ash/media/media_controller_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/media/media_controller.h"
+#include "ash/media/media_controller_impl.h"
 
 #include <memory>
 
@@ -25,7 +25,7 @@
 
     controller_ = std::make_unique<media_session::test::TestMediaController>();
 
-    MediaController* media_controller = Shell::Get()->media_controller();
+    MediaControllerImpl* media_controller = Shell::Get()->media_controller();
     media_controller->SetMediaSessionControllerForTest(
         controller_->CreateMediaControllerPtr());
     media_controller->FlushForTesting();
diff --git a/ash/mojo_interface_factory.cc b/ash/mojo_interface_factory.cc
index 2f452553..508da08 100644
--- a/ash/mojo_interface_factory.cc
+++ b/ash/mojo_interface_factory.cc
@@ -19,9 +19,8 @@
 #include "ash/ime/ime_controller.h"
 #include "ash/keyboard/ash_keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_controller.h"
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
 #include "ash/login/login_screen_controller.h"
-#include "ash/media/media_controller.h"
+#include "ash/media/media_controller_impl.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/voice_interaction_controller.h"
@@ -105,11 +104,6 @@
   Shell::Get()->ash_keyboard_controller()->BindRequest(std::move(request));
 }
 
-void BindKioskNextShellControllerRequestOnMainThread(
-    mojom::KioskNextShellControllerRequest request) {
-  Shell::Get()->kiosk_next_shell_controller()->BindRequest(std::move(request));
-}
-
 void BindLocaleUpdateControllerOnMainThread(
     mojom::LocaleUpdateControllerRequest request) {
   Shell::Get()->locale_update_controller()->BindRequest(std::move(request));
@@ -119,11 +113,6 @@
   Shell::Get()->login_screen_controller()->BindRequest(std::move(request));
 }
 
-void BindMediaControllerRequestOnMainThread(
-    mojom::MediaControllerRequest request) {
-  Shell::Get()->media_controller()->BindRequest(std::move(request));
-}
-
 void BindNightLightControllerRequestOnMainThread(
     mojom::NightLightControllerRequest request) {
   Shell::Get()->night_light_controller()->BindRequest(std::move(request));
@@ -185,11 +174,6 @@
   registry->AddInterface(
       base::BindRepeating(&BindAshMessageCenterControllerRequestOnMainThread),
       main_thread_task_runner);
-  if (base::FeatureList::IsEnabled(features::kKioskNextShell)) {
-    registry->AddInterface(
-        base::BindRepeating(&BindKioskNextShellControllerRequestOnMainThread),
-        main_thread_task_runner);
-  }
   registry->AddInterface(
       base::BindRepeating(&BindImeControllerRequestOnMainThread),
       main_thread_task_runner);
@@ -203,9 +187,6 @@
       base::BindRepeating(&BindLockScreenRequestOnMainThread),
       main_thread_task_runner);
   registry->AddInterface(
-      base::BindRepeating(&BindMediaControllerRequestOnMainThread),
-      main_thread_task_runner);
-  registry->AddInterface(
       base::BindRepeating(&BindNightLightControllerRequestOnMainThread),
       main_thread_task_runner);
   registry->AddInterface(
diff --git a/ash/multi_user/multi_user_window_manager_impl.cc b/ash/multi_user/multi_user_window_manager_impl.cc
index 22188ba..b44763a5 100644
--- a/ash/multi_user/multi_user_window_manager_impl.cc
+++ b/ash/multi_user/multi_user_window_manager_impl.cc
@@ -7,7 +7,7 @@
 #include <set>
 #include <vector>
 
-#include "ash/media/media_controller.h"
+#include "ash/media/media_controller_impl.h"
 #include "ash/multi_user/user_switch_animator.h"
 #include "ash/public/cpp/multi_user_window_manager_delegate.h"
 #include "ash/public/cpp/multi_user_window_manager_observer.h"
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index 2cc82fb..539d386 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -92,6 +92,8 @@
     "keyboard_shortcut_viewer.h",
     "kiosk_app_menu.cc",
     "kiosk_app_menu.h",
+    "kiosk_next_shell.cc",
+    "kiosk_next_shell.h",
     "lock_screen_widget_factory.cc",
     "lock_screen_widget_factory.h",
     "login_constants.h",
@@ -101,6 +103,9 @@
     "login_screen_model.h",
     "login_types.cc",
     "login_types.h",
+    "media_client.h",
+    "media_controller.cc",
+    "media_controller.h",
     "network_icon_image_source.cc",
     "network_icon_image_source.h",
     "new_window_delegate.cc",
@@ -159,6 +164,7 @@
     "tablet_mode_toggle_observer.h",
     "touch_uma.cc",
     "touch_uma.h",
+    "update_types.h",
     "voice_interaction_controller.cc",
     "voice_interaction_controller.h",
     "wallpaper_controller.cc",
diff --git a/ash/public/cpp/app_list/app_list_config.cc b/ash/public/cpp/app_list/app_list_config.cc
index 70952b7..890c9ae 100644
--- a/ash/public/cpp/app_list/app_list_config.cc
+++ b/ash/public/cpp/app_list/app_list_config.cc
@@ -17,8 +17,9 @@
       grid_tile_spacing_(0),
       grid_icon_dimension_(64),
       grid_icon_bottom_padding_(24),
-      grid_title_top_padding_(82),
-      grid_title_horizontal_padding_(0),
+      grid_title_top_padding_(92),
+      grid_title_bottom_padding_(8),
+      grid_title_horizontal_padding_(8),
       grid_title_width_(grid_tile_width_),
       grid_title_color_(SK_ColorWHITE),
       grid_focus_dimension_(80),
@@ -44,6 +45,7 @@
       expand_arrow_tile_height_(72),
       folder_bubble_radius_(44),
       folder_bubble_y_offset_(0),
+      folder_header_height_(20),
       folder_icon_dimension_(72),
       folder_unclipped_icon_dimension_(88),
       folder_icon_radius_(36),
diff --git a/ash/public/cpp/app_list/app_list_config.h b/ash/public/cpp/app_list/app_list_config.h
index 2a301364..18d873d 100644
--- a/ash/public/cpp/app_list/app_list_config.h
+++ b/ash/public/cpp/app_list/app_list_config.h
@@ -31,6 +31,7 @@
   int grid_icon_dimension() const { return grid_icon_dimension_; }
   int grid_icon_bottom_padding() const { return grid_icon_bottom_padding_; }
   int grid_title_top_padding() const { return grid_title_top_padding_; }
+  int grid_title_bottom_padding() const { return grid_title_bottom_padding_; }
   int grid_title_horizontal_padding() const {
     return grid_title_horizontal_padding_;
   }
@@ -73,6 +74,7 @@
   int expand_arrow_tile_height() const { return expand_arrow_tile_height_; }
   int folder_bubble_radius() const { return folder_bubble_radius_; }
   int folder_bubble_y_offset() const { return folder_bubble_y_offset_; }
+  int folder_header_height() const { return folder_header_height_; }
   int folder_icon_dimension() const { return folder_icon_dimension_; }
   int folder_unclipped_icon_dimension() const {
     return folder_unclipped_icon_dimension_;
@@ -192,8 +194,10 @@
   // The icon bottom padding in tile views in apps grid view.
   const int grid_icon_bottom_padding_;
 
-  // The title top and horizontal padding in tile views in apps grid view.
+  // The title top, bottom and horizontal padding in tile views in apps grid
+  // view.
   const int grid_title_top_padding_;
+  const int grid_title_bottom_padding_;
   const int grid_title_horizontal_padding_;
 
   // The title width and color of tile views in apps grid view.
@@ -260,6 +264,9 @@
   // The y offset of folder image bubble center.
   const int folder_bubble_y_offset_;
 
+  // The height of the in folder name and pagination buttons.
+  const int folder_header_height_;
+
   // The icon dimension of folder.
   const int folder_icon_dimension_;
 
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h
index 5a0e167..c950e256 100644
--- a/ash/public/cpp/app_list/app_list_types.h
+++ b/ash/public/cpp/app_list/app_list_types.h
@@ -232,6 +232,12 @@
   // The type of this result.
   SearchResultType result_type = SearchResultType::kUnknown;
 
+  // The subtype of this result. Derived search result classes can use this to
+  // represent their own subtypes. Currently, OmniboxResult sets this to
+  // indicate this is a history result, previous query, etc. A value of -1
+  // indicates no subtype has been set.
+  int result_subtype = -1;
+
   // How this result is displayed.
   SearchResultDisplayType display_type = SearchResultDisplayType::kList;
 
diff --git a/ash/public/cpp/arc_custom_tab.h b/ash/public/cpp/arc_custom_tab.h
index b16af01..2121174 100644
--- a/ash/public/cpp/arc_custom_tab.h
+++ b/ash/public/cpp/arc_custom_tab.h
@@ -27,6 +27,10 @@
 
   virtual void Attach(gfx::NativeView view) = 0;
 
+  // Returns the view against which a view or dialog is positioned and parented
+  // in an ArcCustomTab.
+  virtual gfx::NativeView GetHostView() = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(ArcCustomTab);
 };
diff --git a/ash/public/cpp/kiosk_next_shell.cc b/ash/public/cpp/kiosk_next_shell.cc
new file mode 100644
index 0000000..cb9d503f
--- /dev/null
+++ b/ash/public/cpp/kiosk_next_shell.cc
@@ -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.
+
+#include "ash/public/cpp/kiosk_next_shell.h"
+
+#include "base/logging.h"
+
+namespace ash {
+
+namespace {
+KioskNextShellController* g_instance = nullptr;
+}
+
+// static
+KioskNextShellController* KioskNextShellController::Get() {
+  return g_instance;
+}
+
+KioskNextShellController::KioskNextShellController() {
+  DCHECK_EQ(nullptr, g_instance);
+  g_instance = this;
+}
+
+KioskNextShellController::~KioskNextShellController() {
+  DCHECK_EQ(this, g_instance);
+  g_instance = nullptr;
+}
+
+}  // namespace ash
diff --git a/ash/public/cpp/kiosk_next_shell.h b/ash/public/cpp/kiosk_next_shell.h
new file mode 100644
index 0000000..7011e6c
--- /dev/null
+++ b/ash/public/cpp/kiosk_next_shell.h
@@ -0,0 +1,42 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_PUBLIC_CPP_KIOSK_NEXT_SHELL_H_
+#define ASH_PUBLIC_CPP_KIOSK_NEXT_SHELL_H_
+
+#include "ash/public/cpp/ash_public_export.h"
+
+class AccountId;
+
+namespace ash {
+
+// Performs browser-side functionality for Kiosk Next, e.g. launching the
+// KioskNextShell.
+class ASH_PUBLIC_EXPORT KioskNextShellClient {
+ public:
+  // Launch the Kiosk Next Shell for the user identified by |account_id|.
+  virtual void LaunchKioskNextShell(const AccountId& account_id) = 0;
+
+ protected:
+  virtual ~KioskNextShellClient() = default;
+};
+
+// Interface that allows Chrome to notify Ash when the KioskNextShellClient is
+// ready.
+class ASH_PUBLIC_EXPORT KioskNextShellController {
+ public:
+  static KioskNextShellController* Get();
+
+  // Registers the client, and if non-null, launches the Kiosk Next Shell
+  // session.
+  virtual void SetClientAndLaunchSession(KioskNextShellClient* client) = 0;
+
+ protected:
+  KioskNextShellController();
+  virtual ~KioskNextShellController();
+};
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_KIOSK_NEXT_SHELL_H_
diff --git a/ash/public/cpp/manifest.cc b/ash/public/cpp/manifest.cc
index 83d8d0f..9d7ab1b 100644
--- a/ash/public/cpp/manifest.cc
+++ b/ash/public/cpp/manifest.cc
@@ -16,7 +16,6 @@
 #include "ash/public/interfaces/kiosk_next_shell.mojom.h"
 #include "ash/public/interfaces/locale.mojom.h"
 #include "ash/public/interfaces/login_screen.mojom.h"
-#include "ash/public/interfaces/media.mojom.h"
 #include "ash/public/interfaces/night_light_controller.mojom.h"
 #include "ash/public/interfaces/shelf_integration_test_api.mojom.h"
 #include "ash/public/interfaces/tray_action.mojom.h"
@@ -61,12 +60,11 @@
                   mojom::AssistantNotificationController,
                   mojom::AssistantScreenContextController,
                   mojom::AssistantVolumeControl,
-                  mojom::KioskNextShellController,
                   mojom::CrosDisplayConfigController, mojom::ImeController,
                   mojom::KeyboardController, mojom::LocaleUpdateController,
-                  mojom::LoginScreen, mojom::MediaController,
-                  mojom::NightLightController, mojom::TrayAction,
-                  mojom::VoiceInteractionController, mojom::VpnList>())
+                  mojom::LoginScreen, mojom::NightLightController,
+                  mojom::TrayAction, mojom::VoiceInteractionController,
+                  mojom::VpnList>())
           .ExposeCapability("test", service_manager::Manifest::InterfaceList<
                                         mojom::ShelfIntegrationTestApi>())
           .RequireCapability("*", "accessibility")
diff --git a/ash/public/cpp/media_client.h b/ash/public/cpp/media_client.h
new file mode 100644
index 0000000..5c1fab8a
--- /dev/null
+++ b/ash/public/cpp/media_client.h
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_PUBLIC_CPP_MEDIA_CLIENT_H_
+#define ASH_PUBLIC_CPP_MEDIA_CLIENT_H_
+
+#include "ash/public/cpp/ash_public_export.h"
+
+namespace ash {
+
+// This delegate allows the UI code in ash to forward UI commands.
+class ASH_PUBLIC_EXPORT MediaClient {
+ public:
+  // Handles the Next Track Media shortcut key.
+  virtual void HandleMediaNextTrack() = 0;
+
+  // Handles the Play/Pause Toggle Media shortcut key.
+  virtual void HandleMediaPlayPause() = 0;
+
+  // Handles the Previous Track Media shortcut key.
+  virtual void HandleMediaPrevTrack() = 0;
+
+  // Requests that the client resends the NotifyMediaCaptureChanged() message.
+  virtual void RequestCaptureState() = 0;
+
+  // Suspends all WebContents-associated media sessions to stop managed players.
+  virtual void SuspendMediaSessions() = 0;
+
+ protected:
+  virtual ~MediaClient() = default;
+};
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_MEDIA_CLIENT_H_
diff --git a/ash/public/cpp/media_controller.cc b/ash/public/cpp/media_controller.cc
new file mode 100644
index 0000000..4dfe99f5d
--- /dev/null
+++ b/ash/public/cpp/media_controller.cc
@@ -0,0 +1,41 @@
+// 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 "ash/public/cpp/media_controller.h"
+
+#include "base/logging.h"
+
+namespace ash {
+
+namespace {
+
+MediaController* g_instance = nullptr;
+
+}  // namespace
+
+MediaController::ScopedResetterForTest::ScopedResetterForTest()
+    : instance_(g_instance) {
+  g_instance = nullptr;
+}
+
+MediaController::ScopedResetterForTest::~ScopedResetterForTest() {
+  g_instance = instance_;
+}
+
+// static
+MediaController* MediaController::Get() {
+  return g_instance;
+}
+
+MediaController::MediaController() {
+  DCHECK(!g_instance);
+  g_instance = this;
+}
+
+MediaController::~MediaController() {
+  DCHECK_EQ(this, g_instance);
+  g_instance = nullptr;
+}
+
+}  // namespace ash
diff --git a/ash/public/cpp/media_controller.h b/ash/public/cpp/media_controller.h
new file mode 100644
index 0000000..a7a025b
--- /dev/null
+++ b/ash/public/cpp/media_controller.h
@@ -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.
+
+#ifndef ASH_PUBLIC_CPP_MEDIA_CONTROLLER_H_
+#define ASH_PUBLIC_CPP_MEDIA_CONTROLLER_H_
+
+#include "ash/public/cpp/ash_public_export.h"
+#include "base/containers/flat_map.h"
+#include "components/account_id/account_id.h"
+
+namespace ash {
+
+class MediaClient;
+
+// Describes whether media is currently being captured.
+enum class MediaCaptureState {
+  kNone = 0,
+  kAudio = 1,
+  kVideo = 2,
+  kAudioVideo = 3,
+};
+
+class ASH_PUBLIC_EXPORT MediaController {
+ public:
+  // Helper class to reset ShutdowController instance in constructor and
+  // restore it in destructor so that tests could create its own instance.
+  class ScopedResetterForTest {
+   public:
+    ScopedResetterForTest();
+    ~ScopedResetterForTest();
+
+   private:
+    MediaController* const instance_;
+  };
+
+  // Gets the singleton MediaController instance.
+  static MediaController* Get();
+
+  // Sets the client.
+  virtual void SetClient(MediaClient* client) = 0;
+
+  // Forces media shortcut key handling in MediaClient instead of in ash. This
+  // defaults to false and will be reset if the client encounters an error.
+  virtual void SetForceMediaClientKeyHandling(bool enabled) = 0;
+
+  // Called when the media capture state changes on the client, or in response
+  // to a RequestCaptureState() request. Returns a map from AccountId to
+  // MediaCaptureState representing every user's state.
+  virtual void NotifyCaptureState(
+      const base::flat_map<AccountId, MediaCaptureState>& capture_states) = 0;
+
+ protected:
+  MediaController();
+  virtual ~MediaController();
+};
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_MEDIA_CONTROLLER_H_
diff --git a/ash/public/cpp/system_tray.h b/ash/public/cpp/system_tray.h
index a42c6a8..d9cc7742 100644
--- a/ash/public/cpp/system_tray.h
+++ b/ash/public/cpp/system_tray.h
@@ -9,12 +9,14 @@
 
 #include "ash/public/cpp/ash_public_export.h"
 #include "ash/public/interfaces/locale.mojom-forward.h"
-#include "ash/public/interfaces/update.mojom-forward.h"
 #include "base/strings/string16.h"
 
 namespace ash {
 
 class SystemTrayClient;
+enum class NotificationStyle;
+enum class UpdateSeverity;
+enum class UpdateType;
 
 // Public interface to control the system tray bubble in ash.
 class ASH_PUBLIC_EXPORT SystemTray {
@@ -63,10 +65,10 @@
   //
   // These values are used to control the icon, color and the text of the
   // tooltip or the notification.
-  virtual void ShowUpdateIcon(mojom::UpdateSeverity severity,
+  virtual void ShowUpdateIcon(UpdateSeverity severity,
                               bool factory_reset_required,
                               bool rollback,
-                              mojom::UpdateType update_type) = 0;
+                              UpdateType update_type) = 0;
 
   // Sets new strings for update notification in the unified system menu,
   // according to different policies, when there is an update available
@@ -80,7 +82,7 @@
   // the default.
   // |notification_body| the new notification body which overwrites the default.
   virtual void SetUpdateNotificationState(
-      mojom::NotificationStyle style,
+      NotificationStyle style,
       const base::string16& notification_title,
       const base::string16& notification_body) = 0;
 
diff --git a/ash/public/cpp/test/shell_test_api.h b/ash/public/cpp/test/shell_test_api.h
index d50c413..ae9b724 100644
--- a/ash/public/cpp/test/shell_test_api.h
+++ b/ash/public/cpp/test/shell_test_api.h
@@ -21,6 +21,7 @@
 class DragDropController;
 class MessageCenterController;
 class NativeCursorManagerAsh;
+class PaginationModel;
 class PowerPrefs;
 class ScreenPositionController;
 class Shell;
@@ -94,6 +95,10 @@
   // state transition animation.
   void WaitForLauncherAnimationState(AppListViewState state);
 
+  // Returns the pagination model of the currently visible app-list view.
+  // It returns nullptr when app-list is not shown.
+  PaginationModel* GetAppListPaginationModel();
+
   // Returns the list of windows used in overview item. Returns empty
   // if not in the overview mode.
   std::vector<aura::Window*> GetItemWindowListInOverviewGrids();
diff --git a/ash/public/cpp/update_types.h b/ash/public/cpp/update_types.h
new file mode 100644
index 0000000..f29eaf0
--- /dev/null
+++ b/ash/public/cpp/update_types.h
@@ -0,0 +1,43 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_PUBLIC_CPP_UPDATE_TYPES_H_
+#define ASH_PUBLIC_CPP_UPDATE_TYPES_H_
+
+namespace ash {
+
+// Urgency of a pending software update. Sets the system tray update icon color.
+// These correspond to values in UpgradeDetector's
+// UpgradeNotificationAnnoyanceLevel enum. Their use is platform-specific. On
+// Chrome OS, kLow severity is issued when an update is detected. kElevated
+// follows after two days, and kHigh two days after that. These time deltas may
+// be overridden by administrators via the RelaunchNotificationPeriod policy
+// setting.
+// TODO(jamescook): UpgradeDetector::UpgradeNotificationAnnoyanceLevel could be
+// replaced with this if this moves into a component shared with non-ash chrome.
+enum class UpdateSeverity {
+  kNone,
+  kVeryLow,
+  kLow,
+  kElevated,
+  kHigh,
+  kCritical,
+};
+
+// The type of update being applied. Sets the string in the system tray.
+enum class UpdateType {
+  kFlash,
+  kSystem,
+};
+
+// Notification style for system updates, set by different policies.
+enum class NotificationStyle {
+  kDefault,
+  kAdminRecommended,  // Relaunch Notification policy
+  kAdminRequired,     // Relaunch Notification policy
+};
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_UPDATE_TYPES_H_
diff --git a/ash/wm/window_finder.h b/ash/public/cpp/window_finder.h
similarity index 86%
rename from ash/wm/window_finder.h
rename to ash/public/cpp/window_finder.h
index ab1af72..d70139b8 100644
--- a/ash/wm/window_finder.h
+++ b/ash/public/cpp/window_finder.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_WM_WINDOW_FINDER_H_
-#define ASH_WM_WINDOW_FINDER_H_
+#ifndef ASH_PUBLIC_CPP_WINDOW_FINDER_H_
+#define ASH_PUBLIC_CPP_WINDOW_FINDER_H_
 
 #include <set>
 
@@ -18,7 +18,6 @@
 }
 
 namespace ash {
-namespace wm {
 
 // Finds the topmost window at |screen_point| with ignoring |ignore|. If
 // |real_topmost| is not nullptr, it will be updated to the topmost visible
@@ -31,7 +30,6 @@
     const std::set<aura::Window*>& ignore,
     aura::Window** real_topmost);
 
-}  // namespace wm
 }  // namespace ash
 
-#endif  // ASH_WM_WINDOW_FINDER_H_
+#endif  // ASH_PUBLIC_CPP_WINDOW_FINDER_H_
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn
index c5c5a4b9..8db2301 100644
--- a/ash/public/interfaces/BUILD.gn
+++ b/ash/public/interfaces/BUILD.gn
@@ -29,14 +29,11 @@
     "keyboard_config.mojom",
     "keyboard_controller.mojom",
     "keyboard_controller_types.mojom",
-    "kiosk_next_shell.mojom",
     "locale.mojom",
     "login_screen.mojom",
-    "media.mojom",
     "night_light_controller.mojom",
     "shelf_integration_test_api.mojom",
     "tray_action.mojom",
-    "update.mojom",
     "voice_interaction_controller.mojom",
     "vpn_list.mojom",
   ]
diff --git a/ash/public/interfaces/kiosk_next_shell.mojom b/ash/public/interfaces/kiosk_next_shell.mojom
deleted file mode 100644
index a9ec5d2..0000000
--- a/ash/public/interfaces/kiosk_next_shell.mojom
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module ash.mojom;
-
-import "components/account_id/interfaces/account_id.mojom";
-
-// Performs browser-side functionality for Kiosk Next, e.g. launching the
-// KioskNextShell.
-interface KioskNextShellClient {
-  // Launch the Kiosk Next Shell for the user identified by |account_id|.
-  LaunchKioskNextShell(signin.mojom.AccountId account_id);
-};
-
-// Allows Ash and its consumers to interact with Kiosk Next.
-// These requests are forwarded to the KioskNextShellClient when necessary.
-interface KioskNextShellController {
-  // Provides a client for dispatching requests.
-  SetClient(KioskNextShellClient client);
-};
diff --git a/ash/public/interfaces/media.mojom b/ash/public/interfaces/media.mojom
deleted file mode 100644
index 14866ff1..0000000
--- a/ash/public/interfaces/media.mojom
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module ash.mojom;
-
-import "components/account_id/interfaces/account_id.mojom";
-
-// Describes whether media is currently being captured.
-enum MediaCaptureState {
-  NONE = 0,
-  AUDIO = 1,
-  VIDEO = 2,
-  AUDIO_VIDEO = 3
-};
-
-// Allows clients (e.g. Chrome browser) to interface with the ash media
-// indicators.
-interface MediaController {
-  // Sets the client interface.
-  SetClient(associated MediaClient client);
-
-  // Forces media shortcut key handling in MediaClient instead of in ash. This
-  // defaults to false and will be reset if the client encounters an error.
-  SetForceMediaClientKeyHandling(bool enabled);
-
-  // Called when the media capture state changes on the client, or in response
-  // to a RequestCaptureState() request. Returns a map from AccountId to
-  // MediaCaptureState representing every user's state.
-  NotifyCaptureState(map<signin.mojom.AccountId,
-      MediaCaptureState> capture_states);
-};
-
-// This delegate allows the UI code in ash to forward UI commands.
-interface MediaClient {
-  // Handles the Next Track Media shortcut key.
-  HandleMediaNextTrack();
-
-  // Handles the Play/Pause Toggle Media shortcut key.
-  HandleMediaPlayPause();
-
-  // Handles the Previous Track Media shortcut key.
-  HandleMediaPrevTrack();
-
-  // Requests that the client resends the NotifyMediaCaptureChanged() message.
-  RequestCaptureState();
-
-  // Suspends all WebContents-associated media sessions to stop managed players.
-  SuspendMediaSessions();
-};
diff --git a/ash/public/interfaces/update.mojom b/ash/public/interfaces/update.mojom
deleted file mode 100644
index 4f914d5..0000000
--- a/ash/public/interfaces/update.mojom
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module ash.mojom;
-
-// Urgency of a pending software update. Sets the system tray update icon color.
-// These correspond to values in UpgradeDetector's
-// UpgradeNotificationAnnoyanceLevel enum. Their use is platform-specific. On
-// Chrome OS, LOW severity is issued when an update is detected. ELEVATED
-// follows after two days, and HIGH two days after that. These time deltas may
-// be overridden by administrators via the RelaunchNotificationPeriod policy
-// setting.
-// TODO(jamescook): UpgradeDetector::UpgradeNotificationAnnoyanceLevel could be
-// replaced with this if this moves into a component shared with non-ash chrome.
-enum UpdateSeverity {
-  NONE,
-  VERY_LOW,
-  LOW,
-  ELEVATED,
-  HIGH,
-  CRITICAL,
-};
-
-// The type of update being applied. Sets the string in the system tray.
-enum UpdateType {
-  FLASH,
-  SYSTEM,
-};
-
-// Notification style for system updates, set by different policies.
-enum NotificationStyle {
-    DEFAULT,
-    ADMIN_RECOMMENDED,  // Relaunch Notification policy
-    ADMIN_REQUIRED,     // Relaunch Notification policy
-};
diff --git a/ash/shelf/app_list_button_unittest.cc b/ash/shelf/app_list_button_unittest.cc
index 1ecad65..5939d49 100644
--- a/ash/shelf/app_list_button_unittest.cc
+++ b/ash/shelf/app_list_button_unittest.cc
@@ -285,7 +285,12 @@
   void SetUp() override {
     set_start_session(false);
     AppListButtonTest::SetUp();
-    client_ = BindMockKioskNextShellClient();
+    client_ = std::make_unique<MockKioskNextShellClient>();
+  }
+
+  void TearDown() override {
+    client_.reset();
+    AppListButtonTest::TearDown();
   }
 
  private:
diff --git a/ash/shelf/back_button_unittest.cc b/ash/shelf/back_button_unittest.cc
index 0dc6d41..1322bab 100644
--- a/ash/shelf/back_button_unittest.cc
+++ b/ash/shelf/back_button_unittest.cc
@@ -133,7 +133,12 @@
   void SetUp() override {
     set_start_session(false);
     BackButtonTest::SetUp();
-    client_ = BindMockKioskNextShellClient();
+    client_ = std::make_unique<MockKioskNextShellClient>();
+  }
+
+  void TearDown() override {
+    client_.reset();
+    BackButtonTest::TearDown();
   }
 
   void SimulateKioskNextSession() {
diff --git a/ash/shelf/default_shelf_view.cc b/ash/shelf/default_shelf_view.cc
index aa2554a8..d6bc15d 100644
--- a/ash/shelf/default_shelf_view.cc
+++ b/ash/shelf/default_shelf_view.cc
@@ -8,18 +8,47 @@
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/shelf/app_list_button.h"
 #include "ash/shelf/back_button.h"
+#include "ash/shelf/overflow_button.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_app_button.h"
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
+#include "ash/system/model/system_tray_model.h"
+#include "ash/system/model/virtual_keyboard_model.h"
+#include "ash/system/status_area_widget.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/views/controls/separator.h"
 #include "ui/views/view.h"
+#include "ui/views/view_model.h"
 
 namespace ash {
 
 namespace {
 
+// Indices of the start-aligned system buttons (the "back" button in tablet
+// mode, and the "app list" button).
+constexpr int kBackButtonIndex = 0;
+constexpr int kAppListButtonIndex = 1;
+
+// White with ~20% opacity.
+constexpr SkColor kSeparatorColor = SkColorSetARGB(0x32, 0xFF, 0xFF, 0xFF);
+
+// The dimensions, in pixels, of the separator between pinned and unpinned
+// items.
+constexpr int kSeparatorSize = 20;
+constexpr int kSeparatorThickness = 1;
+
+// The margin between app icons and the overflow button. This is bigger than
+// between app icons because the overflow button is a little smaller.
+constexpr int kMarginBetweenAppsAndOverflow = 16;
+
+// The margin on either side of the group of app icons (including the overflow
+// button).
+constexpr int kAppIconGroupMargin = 16;
+
+// Helper to check if tablet mode is enabled.
 bool IsTabletModeEnabled() {
   // This check is needed, because tablet mode controller is destroyed before
   // shelf widget. See https://crbug.com/967149 for more details.
@@ -29,6 +58,23 @@
              ->IsTabletModeWindowManagerEnabled();
 }
 
+// Returns the size occupied by |count| app icons. If |with_overflow| is
+// true, returns the size of |count| app icons followed by an overflow
+// button.
+int GetSizeOfAppIcons(int count, bool with_overflow) {
+  if (count == 0)
+    return with_overflow ? kShelfControlSize : 0;
+
+  const int app_size = count * kShelfButtonSize;
+  int overflow_size = 0;
+  int total_padding = ShelfConstants::button_spacing() * (count - 1);
+  if (with_overflow) {
+    overflow_size += kShelfControlSize;
+    total_padding += kMarginBetweenAppsAndOverflow;
+  }
+  return app_size + total_padding + overflow_size;
+}
+
 }  // namespace
 
 DefaultShelfView::DefaultShelfView(ShelfModel* model,
@@ -50,11 +96,200 @@
   ConfigureChildView(back_and_app_list_background_);
   AddChildView(back_and_app_list_background_);
 
+  separator_ = new views::Separator();
+  separator_->SetColor(kSeparatorColor);
+  separator_->SetPreferredHeight(kSeparatorSize);
+  separator_->SetVisible(false);
+  ConfigureChildView(separator_);
+  AddChildView(separator_);
+
   // Calling Init() after adding background view, so control buttons are added
   // after background.
   ShelfView::Init();
 }
 
+void DefaultShelfView::CalculateIdealBounds() {
+  DCHECK(model()->item_count() == view_model()->view_size());
+
+  const int button_spacing = ShelfConstants::button_spacing();
+  const int available_size = shelf()->PrimaryAxisValue(width(), height());
+
+  const int separator_index = GetSeparatorIndex();
+  const AppCenteringStrategy app_centering_strategy =
+      CalculateAppCenteringStrategy();
+
+  // At this point we know that |last_visible_index_| is up to date.
+  const bool virtual_keyboard_visible =
+      Shell::Get()->system_tray_model()->virtual_keyboard()->visible();
+  // Don't show the separator if it isn't needed, or would appear after all
+  // visible items.
+  separator_->SetVisible(separator_index != -1 &&
+                         separator_index < last_visible_index() &&
+                         !virtual_keyboard_visible);
+  int app_list_button_position;
+
+  int x = shelf()->PrimaryAxisValue(button_spacing, 0);
+  int y = shelf()->PrimaryAxisValue(0, button_spacing);
+
+  for (int i = 0; i < view_model()->view_size(); ++i) {
+    // "Primary" as in "same direction as the shelf's direction". The
+    // "secondary" (orthogonal) size is always the full shelf to maximize click
+    // targets even for control buttons.
+    const int size_primary =
+        (i <= kAppListButtonIndex) ? kShelfControlSize : kShelfButtonSize;
+    const int size_secondary = kShelfButtonSize;
+
+    if (is_overflow_mode() && i < first_visible_index()) {
+      view_model()->set_ideal_bounds(i, gfx::Rect(x, y, 0, 0));
+      continue;
+    }
+    if (i == kAppListButtonIndex + 1) {
+      // Start centering after we've laid out the app list button.
+
+      StatusAreaWidget* status_widget = shelf_widget()->status_area_widget();
+      const int status_widget_size =
+          status_widget ? shelf()->PrimaryAxisValue(
+                              status_widget->GetWindowBoundsInScreen().width(),
+                              status_widget->GetWindowBoundsInScreen().height())
+                        : 0;
+      const int screen_size = available_size + status_widget_size;
+
+      const int available_size_for_app_icons = GetAvailableSpaceForAppIcons();
+      const int icons_size = GetSizeOfAppIcons(number_of_visible_apps(),
+                                               app_centering_strategy.overflow);
+      int padding_for_centering = 0;
+
+      if (app_centering_strategy.center_on_screen) {
+        padding_for_centering = (screen_size - icons_size) / 2;
+      } else {
+        padding_for_centering =
+            kShelfButtonSpacing +
+            (IsTabletModeEnabled() ? 2 : 1) * kShelfControlSize +
+            kAppIconGroupMargin +
+            (available_size_for_app_icons - icons_size) / 2;
+      }
+
+      if (padding_for_centering >
+          app_list_button_position + kAppIconGroupMargin) {
+        // Only shift buttons to the right, never let them interfere with the
+        // left-aligned system buttons.
+        x = shelf()->PrimaryAxisValue(padding_for_centering, 0);
+        y = shelf()->PrimaryAxisValue(0, padding_for_centering);
+      }
+    }
+
+    view_model()->set_ideal_bounds(
+        i,
+        gfx::Rect(x, y, shelf()->PrimaryAxisValue(size_primary, size_secondary),
+                  shelf()->PrimaryAxisValue(size_secondary, size_primary)));
+
+    // If not in tablet mode do not increase |x| or |y|. Instead just let the
+    // next item (app list button) cover the back button, which will have
+    // opacity 0 anyways.
+    if (i == kBackButtonIndex && !IsTabletModeEnabled())
+      continue;
+
+    x = shelf()->PrimaryAxisValue(x + size_primary + button_spacing, x);
+    y = shelf()->PrimaryAxisValue(y, y + size_primary + button_spacing);
+
+    if (i == kAppListButtonIndex) {
+      app_list_button_position = shelf()->PrimaryAxisValue(x, y);
+      // A larger minimum padding after the app list button is required:
+      // increment with the necessary extra amount.
+      x += shelf()->PrimaryAxisValue(kAppIconGroupMargin - button_spacing, 0);
+      y += shelf()->PrimaryAxisValue(0, kAppIconGroupMargin - button_spacing);
+    }
+
+    if (i == separator_index) {
+      // Place the separator halfway between the two icons it separates,
+      // vertically centered.
+      int half_space = button_spacing / 2;
+      int secondary_offset =
+          (ShelfConstants::shelf_size() - kSeparatorSize) / 2;
+      x -= shelf()->PrimaryAxisValue(half_space, 0);
+      y -= shelf()->PrimaryAxisValue(0, half_space);
+      separator_->SetBounds(
+          x + shelf()->PrimaryAxisValue(0, secondary_offset),
+          y + shelf()->PrimaryAxisValue(secondary_offset, 0),
+          shelf()->PrimaryAxisValue(kSeparatorThickness, kSeparatorSize),
+          shelf()->PrimaryAxisValue(kSeparatorSize, kSeparatorThickness));
+      x += shelf()->PrimaryAxisValue(half_space, 0);
+      y += shelf()->PrimaryAxisValue(0, half_space);
+    }
+  }
+
+  if (is_overflow_mode()) {
+    UpdateAllButtonsVisibilityInOverflowMode();
+    return;
+  }
+  // In the main shelf, the first visible index is either the back button (in
+  // tablet mode) or the launcher button (otherwise).
+  if (!is_overflow_mode()) {
+    set_first_visible_index(IsTabletModeEnabled() ? kBackButtonIndex
+                                                  : kAppListButtonIndex);
+  }
+
+  for (int i = 0; i < view_model()->view_size(); ++i) {
+    // To receive drag event continuously from |drag_view_| during the dragging
+    // off from the shelf, don't make |drag_view_| invisible. It will be
+    // eventually invisible and removed from the |view_model_| by
+    // FinalizeRipOffDrag().
+    if (dragged_off_shelf() && view_model()->view_at(i) == drag_view())
+      continue;
+    // If the virtual keyboard is visible, only the back button and the app
+    // list button are shown.
+    const bool is_visible_item = !virtual_keyboard_visible ||
+                                 i == kBackButtonIndex ||
+                                 i == kAppListButtonIndex;
+    view_model()->view_at(i)->SetVisible(i <= last_visible_index() &&
+                                         is_visible_item);
+  }
+
+  overflow_button()->SetVisible(app_centering_strategy.overflow);
+  if (app_centering_strategy.overflow) {
+    if (overflow_bubble() && overflow_bubble()->IsShowing())
+      UpdateOverflowRange(overflow_bubble()->bubble_view()->shelf_view());
+  } else {
+    if (overflow_bubble())
+      overflow_bubble()->Hide();
+  }
+}
+
+views::View* DefaultShelfView::CreateViewForItem(const ShelfItem& item) {
+  views::View* view = nullptr;
+  switch (item.type) {
+    case TYPE_PINNED_APP:
+    case TYPE_BROWSER_SHORTCUT:
+    case TYPE_APP:
+    case TYPE_DIALOG: {
+      ShelfAppButton* button = new ShelfAppButton(this);
+      button->SetImage(item.image);
+      button->ReflectItemStatus(item);
+      view = button;
+      break;
+    }
+
+    case TYPE_APP_LIST: {
+      view = new AppListButton(this, shelf());
+      break;
+    }
+
+    case TYPE_BACK_BUTTON: {
+      view = new BackButton(this);
+      break;
+    }
+
+    case TYPE_UNDEFINED:
+      return nullptr;
+  }
+
+  if (item.type != TYPE_BACK_BUTTON)
+    view->set_context_menu_controller(this);
+
+  ConfigureChildView(view);
+  return view;
+}
+
 void DefaultShelfView::LayoutAppListAndBackButtonHighlight() {
   // Don't show anything if this is the overflow menu.
   if (is_overflow_mode()) {
@@ -77,4 +312,92 @@
                                 back_and_app_list_background_size));
 }
 
+int DefaultShelfView::GetAvailableSpaceForAppIcons() const {
+  // Subtract space already allocated to the app list button, and the back
+  // button if applicable.
+  return shelf()->PrimaryAxisValue(width(), height()) - kShelfButtonSpacing -
+         (IsTabletModeEnabled() ? 2 : 1) * kShelfControlSize -
+         2 * kAppIconGroupMargin;
+}
+
+int DefaultShelfView::GetSeparatorIndex() const {
+  for (int i = 0; i < model()->item_count() - 1; ++i) {
+    if (IsItemPinned(model()->items()[i]) &&
+        model()->items()[i + 1].type == TYPE_APP) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+DefaultShelfView::AppCenteringStrategy
+DefaultShelfView::CalculateAppCenteringStrategy() {
+  // There are two possibilities. Either all the apps fit when centered
+  // on the whole screen width, in which case we do that. Or, when space
+  // becomes a little tight (which happens especially when the status area
+  // is wider because of extra panels), we center apps on the available space.
+
+  AppCenteringStrategy strategy;
+  // This is only relevant for the main shelf.
+  if (is_overflow_mode())
+    return strategy;
+
+  const int total_available_size = shelf()->PrimaryAxisValue(width(), height());
+  StatusAreaWidget* status_widget = shelf_widget()->status_area_widget();
+  const int status_widget_size =
+      status_widget ? shelf()->PrimaryAxisValue(
+                          status_widget->GetWindowBoundsInScreen().width(),
+                          status_widget->GetWindowBoundsInScreen().height())
+                    : 0;
+  const int screen_size = total_available_size + status_widget_size;
+
+  // An easy way to check whether the apps fit at the exact center of the
+  // screen is to imagine that we have another status widget on the other
+  // side (the status widget is always bigger than the app list button plus
+  // the back button if applicable) and see if the apps can fit in the middle.
+  int available_space_for_screen_centering =
+      screen_size - 2 * (status_widget_size + kAppIconGroupMargin);
+
+  // Views at index 0 and 1 are the back button and app list button.
+  if (GetSizeOfAppIcons(view_model()->view_size() - 2, false) <
+      available_space_for_screen_centering) {
+    // Everything fits in the center of the screen.
+    set_last_visible_index(view_model()->view_size() - 1);
+    strategy.center_on_screen = true;
+    return strategy;
+  }
+
+  const int available_size_for_app_icons = GetAvailableSpaceForAppIcons();
+  set_last_visible_index(1);
+  // We know that replacing the last app that fits with the overflow button
+  // will not change the outcome, so we ignore that case for now.
+  while (last_visible_index() < view_model()->view_size() - 1) {
+    if (GetSizeOfAppIcons(last_visible_index(), false) <=
+        available_size_for_app_icons) {
+      set_last_visible_index(last_visible_index() + 1);
+    } else {
+      strategy.overflow = true;
+      // Make space for the overflow button by showing one fewer app icon.
+      set_last_visible_index(last_visible_index() - 1);
+      break;
+    }
+  }
+  return strategy;
+}
+
+void DefaultShelfView::UpdateAllButtonsVisibilityInOverflowMode() {
+  // The overflow button is not shown in overflow mode.
+  overflow_button()->SetVisible(false);
+  DCHECK_LT(last_visible_index(), view_model()->view_size());
+  for (int i = 0; i < view_model()->view_size(); ++i) {
+    bool visible = i >= first_visible_index() && i <= last_visible_index();
+    // To track the dragging of |drag_view_| continuously, its visibility
+    // should be always true regardless of its position.
+    if (dragged_to_another_shelf() && view_model()->view_at(i) == drag_view())
+      view_model()->view_at(i)->SetVisible(true);
+    else
+      view_model()->view_at(i)->SetVisible(visible);
+  }
+}
+
 }  // namespace ash
diff --git a/ash/shelf/default_shelf_view.h b/ash/shelf/default_shelf_view.h
index 75469204..dc87218d 100644
--- a/ash/shelf/default_shelf_view.h
+++ b/ash/shelf/default_shelf_view.h
@@ -11,11 +11,13 @@
 
 namespace views {
 class View;
+class Separator;
 }  // namespace views
 
 namespace ash {
 
 class Shelf;
+struct ShelfItem;
 class ShelfModel;
 class ShelfWidget;
 
@@ -30,9 +32,37 @@
   // All ShelfView overrides are public to keep them together.
   // ShelfView:
   void Init() override;
+  void CalculateIdealBounds() override;
+  views::View* CreateViewForItem(const ShelfItem& item) override;
   void LayoutAppListAndBackButtonHighlight() override;
 
  private:
+  struct AppCenteringStrategy {
+    bool center_on_screen = false;
+    bool overflow = false;
+  };
+
+  // Returns the size that's actually available for app icons. Size occupied
+  // by the app list button and back button plus all appropriate margins is
+  // not available for app icons.
+  int GetAvailableSpaceForAppIcons() const;
+
+  // Returns the index of the item after which the separator should be shown,
+  // or -1 if no separator is required.
+  int GetSeparatorIndex() const;
+
+  // This method determines which centering strategy is adequate, returns that,
+  // and sets the |first_visible_index_| and |last_visible_index_| fields
+  // appropriately.
+  AppCenteringStrategy CalculateAppCenteringStrategy();
+
+  // Update all buttons' visibility in overflow.
+  void UpdateAllButtonsVisibilityInOverflowMode();
+
+  // A reference to the view used as a separator between pinned and unpinned
+  // items.
+  views::Separator* separator_ = nullptr;
+
   // A view to draw a background behind the app list and back buttons.
   // Owned by the view hierarchy.
   views::View* back_and_app_list_background_ = nullptr;
diff --git a/ash/shelf/home_button_delegate.cc b/ash/shelf/home_button_delegate.cc
index 739049f..f65b4199 100644
--- a/ash/shelf/home_button_delegate.cc
+++ b/ash/shelf/home_button_delegate.cc
@@ -8,7 +8,7 @@
 
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/home_screen/home_screen_controller.h"
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/shell.h"
 #include "base/logging.h"
diff --git a/ash/shelf/kiosk_next_shelf_view.cc b/ash/shelf/kiosk_next_shelf_view.cc
index c18b24b..f442120 100644
--- a/ash/shelf/kiosk_next_shelf_view.cc
+++ b/ash/shelf/kiosk_next_shelf_view.cc
@@ -21,11 +21,11 @@
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/insets_f.h"
+#include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/views/animation/flood_fill_ink_drop_ripple.h"
 #include "ui/views/animation/ink_drop_mask.h"
 #include "ui/views/animation/ink_drop_ripple.h"
-#include "ui/views/controls/separator.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 
@@ -121,9 +121,7 @@
   // TODO(agawronska): Separator and overflow button are not needed in Kiosk
   // Next shelf. They should be moved to DefaultShelfView subclass and the below
   // code should be removed.
-  DCHECK(separator());
   DCHECK(overflow_button());
-  separator()->SetVisible(false);
   overflow_button()->SetVisible(false);
 
   set_first_visible_index(0);
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index cfcb93065..ad096f310 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -22,6 +22,7 @@
 #include "ash/screen_util.h"
 #include "ash/shelf/app_list_button.h"
 #include "ash/shelf/back_button.h"
+#include "ash/shelf/default_shelf_view.h"
 #include "ash/shelf/overflow_button.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_app_button.h"
@@ -67,7 +68,6 @@
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/menu/menu_model_adapter.h"
 #include "ui/views/controls/menu/menu_runner.h"
-#include "ui/views/controls/separator.h"
 #include "ui/views/focus/focus_search.h"
 #include "ui/views/view_model.h"
 #include "ui/views/view_model_utils.h"
@@ -84,47 +84,14 @@
 // The margin between app icons and the overflow button. This is bigger than
 // between app icons because the overflow button is a little smaller.
 constexpr int kMarginBetweenAppsAndOverflow = 16;
-
-// Returns the size occupied by |count| app icons. If |with_overflow| is
-// true, returns the size of |count| app icons followed by an overflow
-// button.
-int GetSizeOfAppIcons(int count, bool with_overflow) {
-  if (count == 0)
-    return with_overflow ? kShelfControlSize : 0;
-
-  const int app_size = count * kShelfButtonSize;
-  int overflow_size = 0;
-  int total_padding = ShelfConstants::button_spacing() * (count - 1);
-  if (with_overflow) {
-    overflow_size += kShelfControlSize;
-    total_padding += kMarginBetweenAppsAndOverflow;
-  }
-  return app_size + total_padding + overflow_size;
-}
-
 }  // namespace
 
-// Indices of the start-aligned system buttons (the "back" button in tablet
-// mode, and the "app list" button).
-constexpr int kBackButtonIndex = 0;
 constexpr int kAppListButtonIndex = 1;
 
 // The distance of the cursor from the outer rim of the shelf before it
 // separates.
 constexpr int kRipOffDistance = 48;
 
-// The dimensions, in pixels, of the separator between pinned and unpinned
-// items.
-constexpr int kSeparatorSize = 20;
-constexpr int kSeparatorThickness = 1;
-
-// The margin on either side of the group of app icons (including the overflow
-// button).
-constexpr int kAppIconGroupMargin = 16;
-
-// White with ~20% opacity.
-constexpr SkColor kSeparatorColor = SkColorSetARGB(0x32, 0xFF, 0xFF, 0xFF);
-
 // The rip off drag and drop proxy image should get scaled by this factor.
 constexpr float kDragAndDropProxyScale = 1.2f;
 
@@ -383,7 +350,7 @@
 }
 
 void ShelfView::Init() {
-  model_->AddObserver(this);
+  model()->AddObserver(this);
 
   const ShelfItems& items(model_->items());
   for (ShelfItems::const_iterator i = items.begin(); i != items.end(); ++i) {
@@ -397,13 +364,6 @@
   AddChildView(overflow_button_);
   UpdateBackButton();
 
-  separator_ = new views::Separator();
-  separator_->SetColor(kSeparatorColor);
-  separator_->SetPreferredHeight(kSeparatorSize);
-  separator_->SetVisible(false);
-  ConfigureChildView(separator_);
-  AddChildView(separator_);
-
   // We'll layout when our bounds change.
 }
 
@@ -916,189 +876,6 @@
   view->layer()->SetFillsBoundsOpaquely(false);
 }
 
-void ShelfView::CalculateIdealBounds() {
-  DCHECK(model_->item_count() == view_model_->view_size());
-
-  const int button_spacing = ShelfConstants::button_spacing();
-  const int available_size = shelf_->PrimaryAxisValue(width(), height());
-
-  const int separator_index = GetSeparatorIndex();
-  const AppCenteringStrategy app_centering_strategy =
-      CalculateAppCenteringStrategy();
-
-  // At this point we know that |last_visible_index_| is up to date.
-  const bool virtual_keyboard_visible =
-      Shell::Get()->system_tray_model()->virtual_keyboard()->visible();
-  // Don't show the separator if it isn't needed, or would appear after all
-  // visible items.
-  separator_->SetVisible(separator_index != -1 &&
-                         separator_index < last_visible_index_ &&
-                         !virtual_keyboard_visible);
-  int app_list_button_position;
-
-  int x = shelf_->PrimaryAxisValue(button_spacing, 0);
-  int y = shelf_->PrimaryAxisValue(0, button_spacing);
-
-  for (int i = 0; i < view_model_->view_size(); ++i) {
-    // "Primary" as in "same direction as the shelf's direction". The
-    // "secondary" (orthogonal) size is always the full shelf to maximize click
-    // targets even for control buttons.
-    const int size_primary =
-        (i <= kAppListButtonIndex) ? kShelfControlSize : kShelfButtonSize;
-    const int size_secondary = kShelfButtonSize;
-
-    if (is_overflow_mode() && i < first_visible_index_) {
-      view_model_->set_ideal_bounds(i, gfx::Rect(x, y, 0, 0));
-      continue;
-    }
-    if (i == kAppListButtonIndex + 1) {
-      // Start centering after we've laid out the app list button.
-
-      StatusAreaWidget* status_widget = shelf_widget_->status_area_widget();
-      const int status_widget_size =
-          status_widget ? shelf_->PrimaryAxisValue(
-                              status_widget->GetWindowBoundsInScreen().width(),
-                              status_widget->GetWindowBoundsInScreen().height())
-                        : 0;
-      const int screen_size = available_size + status_widget_size;
-
-      const int available_size_for_app_icons = GetAvailableSpaceForAppIcons();
-      const int icons_size = GetSizeOfAppIcons(number_of_visible_apps(),
-                                               app_centering_strategy.overflow);
-      int padding_for_centering = 0;
-
-      if (app_centering_strategy.center_on_screen) {
-        padding_for_centering = (screen_size - icons_size) / 2;
-      } else {
-        padding_for_centering =
-            kShelfButtonSpacing +
-            (IsTabletModeEnabled() ? 2 : 1) * kShelfControlSize +
-            kAppIconGroupMargin +
-            (available_size_for_app_icons - icons_size) / 2;
-      }
-
-      if (padding_for_centering >
-          app_list_button_position + kAppIconGroupMargin) {
-        // Only shift buttons to the right, never let them interfere with the
-        // left-aligned system buttons.
-        x = shelf_->PrimaryAxisValue(padding_for_centering, 0);
-        y = shelf_->PrimaryAxisValue(0, padding_for_centering);
-      }
-    }
-
-    view_model_->set_ideal_bounds(
-        i,
-        gfx::Rect(x, y, shelf_->PrimaryAxisValue(size_primary, size_secondary),
-                  shelf_->PrimaryAxisValue(size_secondary, size_primary)));
-
-    // If not in tablet mode do not increase |x| or |y|. Instead just let the
-    // next item (app list button) cover the back button, which will have
-    // opacity 0 anyways.
-    if (i == kBackButtonIndex && !IsTabletModeEnabled())
-      continue;
-
-    x = shelf_->PrimaryAxisValue(x + size_primary + button_spacing, x);
-    y = shelf_->PrimaryAxisValue(y, y + size_primary + button_spacing);
-
-    if (i == kAppListButtonIndex) {
-      app_list_button_position = shelf_->PrimaryAxisValue(x, y);
-      // A larger minimum padding after the app list button is required:
-      // increment with the necessary extra amount.
-      x += shelf_->PrimaryAxisValue(kAppIconGroupMargin - button_spacing, 0);
-      y += shelf_->PrimaryAxisValue(0, kAppIconGroupMargin - button_spacing);
-    }
-
-    if (i == separator_index) {
-      // Place the separator halfway between the two icons it separates,
-      // vertically centered.
-      int half_space = button_spacing / 2;
-      int secondary_offset =
-          (ShelfConstants::shelf_size() - kSeparatorSize) / 2;
-      x -= shelf_->PrimaryAxisValue(half_space, 0);
-      y -= shelf_->PrimaryAxisValue(0, half_space);
-      separator_->SetBounds(
-          x + shelf_->PrimaryAxisValue(0, secondary_offset),
-          y + shelf_->PrimaryAxisValue(secondary_offset, 0),
-          shelf_->PrimaryAxisValue(kSeparatorThickness, kSeparatorSize),
-          shelf_->PrimaryAxisValue(kSeparatorSize, kSeparatorThickness));
-      x += shelf_->PrimaryAxisValue(half_space, 0);
-      y += shelf_->PrimaryAxisValue(0, half_space);
-    }
-  }
-
-  if (is_overflow_mode()) {
-    const_cast<ShelfView*>(this)->UpdateAllButtonsVisibilityInOverflowMode();
-    return;
-  }
-
-  // In the main shelf, the first visible index is either the back button (in
-  // tablet mode) or the launcher button (otherwise).
-  if (!is_overflow_mode()) {
-    first_visible_index_ =
-        IsTabletModeEnabled() ? kBackButtonIndex : kAppListButtonIndex;
-  }
-
-  for (int i = 0; i < view_model_->view_size(); ++i) {
-    // To receive drag event continuously from |drag_view_| during the dragging
-    // off from the shelf, don't make |drag_view_| invisible. It will be
-    // eventually invisible and removed from the |view_model_| by
-    // FinalizeRipOffDrag().
-    if (dragged_off_shelf_ && view_model_->view_at(i) == drag_view_)
-      continue;
-    // If the virtual keyboard is visible, only the back button and the app
-    // list button are shown.
-    const bool is_visible_item = !virtual_keyboard_visible ||
-                                 i == kBackButtonIndex ||
-                                 i == kAppListButtonIndex;
-    view_model_->view_at(i)->SetVisible(i <= last_visible_index_ &&
-                                        is_visible_item);
-  }
-
-  overflow_button_->SetVisible(app_centering_strategy.overflow);
-  if (app_centering_strategy.overflow) {
-    if (overflow_bubble_.get() && overflow_bubble_->IsShowing())
-      UpdateOverflowRange(overflow_bubble_->bubble_view()->shelf_view());
-  } else {
-    if (overflow_bubble_)
-      overflow_bubble_->Hide();
-  }
-}
-
-views::View* ShelfView::CreateViewForItem(const ShelfItem& item) {
-  views::View* view = nullptr;
-  switch (item.type) {
-    case TYPE_PINNED_APP:
-    case TYPE_BROWSER_SHORTCUT:
-    case TYPE_APP:
-    case TYPE_DIALOG: {
-      ShelfAppButton* button = new ShelfAppButton(this);
-      button->SetImage(item.image);
-      button->ReflectItemStatus(item);
-      view = button;
-      break;
-    }
-
-    case TYPE_APP_LIST: {
-      view = new AppListButton(this, shelf_);
-      break;
-    }
-
-    case TYPE_BACK_BUTTON: {
-      view = new BackButton(this);
-      break;
-    }
-
-    case TYPE_UNDEFINED:
-      return nullptr;
-  }
-
-  if (item.type != TYPE_BACK_BUTTON)
-    view->set_context_menu_controller(this);
-
-  ConfigureChildView(view);
-  return view;
-}
-
 void ShelfView::DestroyDragIconProxy() {
   drag_image_.reset();
   drag_image_offset_ = gfx::Vector2d(0, 0);
@@ -1343,97 +1120,10 @@
   return item.type == TYPE_PINNED_APP || item.type == TYPE_BROWSER_SHORTCUT;
 }
 
-int ShelfView::GetSeparatorIndex() const {
-  for (int i = 0; i < model_->item_count() - 1; ++i) {
-    if (IsItemPinned(model_->items()[i]) &&
-        model_->items()[i + 1].type == TYPE_APP) {
-      return i;
-    }
-  }
-  return -1;
-}
-
 void ShelfView::OnTabletModeChanged() {
   OnBoundsChanged(GetBoundsInScreen());
 }
 
-void ShelfView::UpdateAllButtonsVisibilityInOverflowMode() {
-  // The overflow button is not shown in overflow mode.
-  overflow_button_->SetVisible(false);
-  DCHECK_LT(last_visible_index_, view_model_->view_size());
-  for (int i = 0; i < view_model_->view_size(); ++i) {
-    bool visible = i >= first_visible_index_ && i <= last_visible_index_;
-    // To track the dragging of |drag_view_| continuously, its visibility
-    // should be always true regardless of its position.
-    if (dragged_to_another_shelf_ && view_model_->view_at(i) == drag_view_)
-      view_model_->view_at(i)->SetVisible(true);
-    else
-      view_model_->view_at(i)->SetVisible(visible);
-  }
-}
-
-int ShelfView::GetAvailableSpaceForAppIcons() const {
-  // Subtract space already allocated to the app list button, and the back
-  // button if applicable.
-  return shelf_->PrimaryAxisValue(width(), height()) - kShelfButtonSpacing -
-         (IsTabletModeEnabled() ? 2 : 1) * kShelfControlSize -
-         2 * kAppIconGroupMargin;
-}
-
-ShelfView::AppCenteringStrategy ShelfView::CalculateAppCenteringStrategy() {
-  // There are two possibilities. Either all the apps fit when centered
-  // on the whole screen width, in which case we do that. Or, when space
-  // becomes a little tight (which happens especially when the status area
-  // is wider because of extra panels), we center apps on the available space.
-
-  AppCenteringStrategy strategy;
-  // This is only relevant for the main shelf.
-  if (is_overflow_mode())
-    return strategy;
-
-  const int total_available_size = shelf_->PrimaryAxisValue(width(), height());
-  StatusAreaWidget* status_widget = shelf_widget_->status_area_widget();
-  const int status_widget_size =
-      status_widget ? shelf_->PrimaryAxisValue(
-                          status_widget->GetWindowBoundsInScreen().width(),
-                          status_widget->GetWindowBoundsInScreen().height())
-                    : 0;
-  const int screen_size = total_available_size + status_widget_size;
-
-  // An easy way to check whether the apps fit at the exact center of the
-  // screen is to imagine that we have another status widget on the other
-  // side (the status widget is always bigger than the app list button plus
-  // the back button if applicable) and see if the apps can fit in the middle.
-  int available_space_for_screen_centering =
-      screen_size - 2 * (status_widget_size + kAppIconGroupMargin);
-
-  // Views at index 0 and 1 are the back button and app list button.
-  if (GetSizeOfAppIcons(view_model_->view_size() - 2, false) <
-      available_space_for_screen_centering) {
-    // Everything fits in the center of the screen.
-    last_visible_index_ = view_model_->view_size() - 1;
-    strategy.center_on_screen = true;
-    return strategy;
-  }
-
-  const int available_size_for_app_icons = GetAvailableSpaceForAppIcons();
-  last_visible_index_ = 1;
-  // We know that replacing the last app that fits with the overflow button
-  // will not change the outcome, so we ignore that case for now.
-  while (last_visible_index_ < view_model_->view_size() - 1) {
-    if (GetSizeOfAppIcons(last_visible_index_, false) <=
-        available_size_for_app_icons) {
-      ++last_visible_index_;
-    } else {
-      strategy.overflow = true;
-      // Make space for the overflow button by showing one fewer app icon.
-      --last_visible_index_;
-      break;
-    }
-  }
-  return strategy;
-}
-
 void ShelfView::LayoutOverflowButton() const {
   DCHECK_NE(0, view_model_->view_size());
   int x = 0;
@@ -1905,7 +1595,8 @@
     overflow_bubble_.reset(new OverflowBubble(shelf_));
 
   // TODO(agawronska): Move this to DefaultShelfView.
-  ShelfView* overflow_view = new ShelfView(model_, shelf_, shelf_widget_);
+  ShelfView* overflow_view =
+      new DefaultShelfView(model_, shelf_, shelf_widget_);
   overflow_view->overflow_mode_ = true;
   overflow_view->Init();
   overflow_view->set_owner_overflow_bubble(overflow_bubble_.get());
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index e865c38..8fde061 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -44,7 +44,6 @@
 namespace views {
 class BoundsAnimator;
 class MenuRunner;
-class Separator;
 }  // namespace views
 
 namespace ash {
@@ -258,10 +257,6 @@
   // Returns whether |item| should belong in the pinned section of the shelf.
   bool IsItemPinned(const ShelfItem& item) const;
 
-  // Returns the index of the item after which the separator should be shown,
-  // or -1 if no separator is required.
-  int GetSeparatorIndex() const;
-
   // Update the layout when entering or exiting tablet mode. Have the owning
   // widget call this instead of observing changes ourselves to ensure this
   // happens after the tablet related changes in ShelfController.
@@ -337,38 +332,34 @@
   // Calculates the ideal bounds of shelf elements.
   // The bounds of each button corresponding to an item in the model is set in
   // |view_model_|.
-  virtual void CalculateIdealBounds();
+  virtual void CalculateIdealBounds() = 0;
 
   // Creates the view used to represent given shelf |item|.
   // Returns unowned pointer (view is owned by the view hierarchy).
-  // TODO(agawronska): This method could be pure virtual and implemented by
-  // succlasses, but creation of the overflow shelf has first be moved to
-  // DefaultShelfView.
-  virtual views::View* CreateViewForItem(const ShelfItem& item);
+  virtual views::View* CreateViewForItem(const ShelfItem& item) = 0;
 
   // Lays out control buttons background.
   // Child classes should implement this method if control buttons background
   // requires adjustment when shelf view layout changes.
   virtual void LayoutAppListAndBackButtonHighlight() {}
 
-  views::Separator* separator() { return separator_; }
+  // Updates the visible range of overflow items in |overflow_view|.
+  void UpdateOverflowRange(ShelfView* overflow_view) const;
 
   OverflowButton* overflow_button() { return overflow_button_; }
 
   void set_first_visible_index(int index) { first_visible_index_ = index; }
   void set_last_visible_index(int index) { last_visible_index_ = index; }
 
+  bool dragged_off_shelf() const { return dragged_off_shelf_; }
+  bool dragged_to_another_shelf() const { return dragged_to_another_shelf_; }
+
  private:
   friend class ShelfViewTestAPI;
 
   class FadeOutAnimationDelegate;
   class StartFadeAnimationDelegate;
 
-  struct AppCenteringStrategy {
-    bool center_on_screen = false;
-    bool overflow = false;
-  };
-
   enum RemovableState {
     REMOVABLE,      // Item can be removed when dragged away.
     DRAGGABLE,      // Item can be dragged, but will snap always back to origin.
@@ -383,18 +374,6 @@
   // Sets the bounds of each view to its ideal bounds.
   void LayoutToIdealBounds();
 
-  // Update all button's visibility in overflow.
-  void UpdateAllButtonsVisibilityInOverflowMode();
-
-  // Returns the size that's actually available for app icons. Size occupied
-  // by the app list button and back button plus all appropriate margins is
-  // not available for app icons.
-  int GetAvailableSpaceForAppIcons() const;
-
-  // This method determines which centering strategy is adequate, returns that,
-  // and sets the |first_visible_index_| and |last_visible_index_| fields
-  // appropriately.
-  AppCenteringStrategy CalculateAppCenteringStrategy();
   void LayoutOverflowButton() const;
 
   // Returns the index of the last view whose max primary axis coordinate is
@@ -465,9 +444,6 @@
   // Fade in last visible item.
   void StartFadeInLastVisibleItem();
 
-  // Updates the visible range of overflow items in |overflow_view|.
-  void UpdateOverflowRange(ShelfView* overflow_view) const;
-
   // Gets the menu anchor rect for menus. |source| is the view that is
   // asking for a menu, |location| is the location of the event, |context_menu|
   // is whether the menu is for a context or application menu.
@@ -688,10 +664,6 @@
   // Tracks UMA metrics based on shelf button press actions.
   ShelfButtonPressedMetricTracker shelf_button_pressed_metric_tracker_;
 
-  // A reference to the view used as a separator between pinned and unpinned
-  // items.
-  views::Separator* separator_ = nullptr;
-
   // The union of all visible shelf item bounds. Used for showing tooltips in
   // a continuous manner.
   gfx::Rect visible_shelf_item_bounds_union_;
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index e9cb7e7..bf32e17 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -15,7 +15,7 @@
 #include "ash/display/screen_orientation_controller_test_api.h"
 #include "ash/focus_cycler.h"
 #include "ash/ime/ime_controller.h"
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/kiosk_next/kiosk_next_shell_test_util.h"
 #include "ash/kiosk_next/mock_kiosk_next_shell_client.h"
 #include "ash/public/cpp/ash_features.h"
@@ -3944,7 +3944,12 @@
   void SetUp() override {
     set_start_session(false);
     ShelfViewTest::SetUp();
-    client_ = BindMockKioskNextShellClient();
+    client_ = std::make_unique<MockKioskNextShellClient>();
+  }
+
+  void TearDown() override {
+    client_.reset();
+    ShelfViewTest::TearDown();
   }
 
  protected:
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index 4612824..851c7e5 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -9,7 +9,7 @@
 #include "ash/animation/animation_change_type.h"
 #include "ash/focus_cycler.h"
 #include "ash/keyboard/ui/keyboard_controller.h"
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/shelf_model.h"
diff --git a/ash/shell.cc b/ash/shell.cc
index 3ef6c288..ffd57a21 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -55,14 +55,14 @@
 #include "ash/ime/ime_controller.h"
 #include "ash/keyboard/ash_keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_ui_factory.h"
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/laser/laser_pointer_controller.h"
 #include "ash/login/login_screen_controller.h"
 #include "ash/login_status.h"
 #include "ash/magnifier/docked_magnifier_controller_impl.h"
 #include "ash/magnifier/magnification_controller.h"
 #include "ash/magnifier/partial_magnification_controller.h"
-#include "ash/media/media_controller.h"
+#include "ash/media/media_controller_impl.h"
 #include "ash/media/media_notification_controller_impl.h"
 #include "ash/multi_device_setup/multi_device_notification_presenter.h"
 #include "ash/policy/policy_recommendation_restorer.h"
@@ -553,7 +553,7 @@
       keyboard_brightness_control_delegate_(
           std::make_unique<KeyboardBrightnessController>()),
       locale_update_controller_(std::make_unique<LocaleUpdateController>()),
-      media_controller_(std::make_unique<MediaController>(connector)),
+      media_controller_(std::make_unique<MediaControllerImpl>(connector)),
       session_controller_(std::make_unique<SessionControllerImpl>()),
       shell_delegate_(std::move(shell_delegate)),
       shell_state_(std::make_unique<ShellState>()),
@@ -893,7 +893,8 @@
         std::make_unique<MultiDeviceNotificationPresenter>(
             message_center::MessageCenter::Get(), connector_);
   }
-  kiosk_next_shell_controller_ = std::make_unique<KioskNextShellController>();
+  kiosk_next_shell_controller_ =
+      std::make_unique<KioskNextShellControllerImpl>();
   tablet_mode_controller_ = std::make_unique<TabletModeController>();
 
   accessibility_focus_ring_controller_ =
diff --git a/ash/shell.h b/ash/shell.h
index d5553f1..5f363fe 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -127,7 +127,7 @@
 class ImmersiveContext;
 class KeyAccessibilityEnabler;
 class KeyboardBrightnessControlDelegate;
-class KioskNextShellController;
+class KioskNextShellControllerImpl;
 class AshKeyboardController;
 class LaserPointerController;
 class LocaleUpdateController;
@@ -136,7 +136,7 @@
 class LoginScreenController;
 class MagnificationController;
 class TabletModeController;
-class MediaController;
+class MediaControllerImpl;
 class MediaNotificationControllerImpl;
 class MessageCenterController;
 class MouseCursorEventFilter;
@@ -378,7 +378,7 @@
   KeyboardBrightnessControlDelegate* keyboard_brightness_control_delegate() {
     return keyboard_brightness_control_delegate_.get();
   }
-  KioskNextShellController* kiosk_next_shell_controller() {
+  KioskNextShellControllerImpl* kiosk_next_shell_controller() {
     return kiosk_next_shell_controller_.get();
   }
   AshKeyboardController* ash_keyboard_controller() {
@@ -402,7 +402,7 @@
   MagnificationController* magnification_controller() {
     return magnification_controller_.get();
   }
-  MediaController* media_controller() { return media_controller_.get(); }
+  MediaControllerImpl* media_controller() { return media_controller_.get(); }
   MediaNotificationControllerImpl* media_notification_controller() {
     return media_notification_controller_.get();
   }
@@ -661,12 +661,12 @@
   std::unique_ptr<ImmersiveContext> immersive_context_;
   std::unique_ptr<KeyboardBrightnessControlDelegate>
       keyboard_brightness_control_delegate_;
-  std::unique_ptr<KioskNextShellController> kiosk_next_shell_controller_;
+  std::unique_ptr<KioskNextShellControllerImpl> kiosk_next_shell_controller_;
   std::unique_ptr<LocaleUpdateController> locale_update_controller_;
   std::unique_ptr<LoginScreenController> login_screen_controller_;
   std::unique_ptr<LogoutConfirmationController> logout_confirmation_controller_;
   std::unique_ptr<TabletModeController> tablet_mode_controller_;
-  std::unique_ptr<MediaController> media_controller_;
+  std::unique_ptr<MediaControllerImpl> media_controller_;
   std::unique_ptr<MediaNotificationControllerImpl>
       media_notification_controller_;
   std::unique_ptr<MruWindowTracker> mru_window_tracker_;
diff --git a/ash/shell_test_api.cc b/ash/shell_test_api.cc
index 54dfc27..6281d86c 100644
--- a/ash/shell_test_api.cc
+++ b/ash/shell_test_api.cc
@@ -10,6 +10,7 @@
 #include "ash/accelerators/accelerator_commands.h"
 #include "ash/accelerometer/accelerometer_reader.h"
 #include "ash/app_list/app_list_controller_impl.h"
+#include "ash/app_list/presenter/app_list_presenter_impl.h"
 #include "ash/app_list/views/app_list_view.h"
 #include "ash/keyboard/ash_keyboard_controller.h"
 #include "ash/public/cpp/app_list/app_list_types.h"
@@ -250,6 +251,14 @@
   run_loop.Run();
 }
 
+PaginationModel* ShellTestApi::GetAppListPaginationModel() {
+  app_list::AppListView* view =
+      Shell::Get()->app_list_controller()->presenter()->GetView();
+  if (!view)
+    return nullptr;
+  return view->GetAppsPaginationModel();
+}
+
 std::vector<aura::Window*> ShellTestApi::GetItemWindowListInOverviewGrids() {
   return ash::Shell::Get()
       ->overview_controller()
diff --git a/ash/system/ime_menu/ime_menu_tray.cc b/ash/system/ime_menu/ime_menu_tray.cc
index 0fbd3e6..2323d44 100644
--- a/ash/system/ime_menu/ime_menu_tray.cc
+++ b/ash/system/ime_menu/ime_menu_tray.cc
@@ -9,7 +9,7 @@
 #include "ash/keyboard/ash_keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/virtual_keyboard_controller.h"
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/public/cpp/ash_constants.h"
 #include "ash/public/cpp/system_tray_client.h"
 #include "ash/resources/vector_icons/vector_icons.h"
diff --git a/ash/system/ime_menu/ime_menu_tray_unittest.cc b/ash/system/ime_menu/ime_menu_tray_unittest.cc
index 5870391..59d91fb 100644
--- a/ash/system/ime_menu/ime_menu_tray_unittest.cc
+++ b/ash/system/ime_menu/ime_menu_tray_unittest.cc
@@ -368,7 +368,12 @@
   void SetUp() override {
     set_start_session(false);
     ImeMenuTrayTest::SetUp();
-    client_ = BindMockKioskNextShellClient();
+    client_ = std::make_unique<MockKioskNextShellClient>();
+  }
+
+  void TearDown() override {
+    client_.reset();
+    ImeMenuTrayTest::TearDown();
   }
 
  private:
diff --git a/ash/system/model/system_tray_model.cc b/ash/system/model/system_tray_model.cc
index 0133361..f96b8b1a 100644
--- a/ash/system/model/system_tray_model.cc
+++ b/ash/system/model/system_tray_model.cc
@@ -78,16 +78,16 @@
   locale()->SetLocaleList(std::move(locale_list), current_locale_iso_code);
 }
 
-void SystemTrayModel::ShowUpdateIcon(mojom::UpdateSeverity severity,
+void SystemTrayModel::ShowUpdateIcon(UpdateSeverity severity,
                                      bool factory_reset_required,
                                      bool rollback,
-                                     mojom::UpdateType update_type) {
+                                     UpdateType update_type) {
   update_model()->SetUpdateAvailable(severity, factory_reset_required, rollback,
                                      update_type);
 }
 
 void SystemTrayModel::SetUpdateNotificationState(
-    mojom::NotificationStyle style,
+    NotificationStyle style,
     const base::string16& notification_title,
     const base::string16& notification_body) {
   update_model()->SetUpdateNotificationState(style, notification_title,
diff --git a/ash/system/model/system_tray_model.h b/ash/system/model/system_tray_model.h
index 55198b7b..b99cd79 100644
--- a/ash/system/model/system_tray_model.h
+++ b/ash/system/model/system_tray_model.h
@@ -43,12 +43,12 @@
   void SetPerformanceTracingIconVisible(bool visible) override;
   void SetLocaleList(std::vector<mojom::LocaleInfoPtr> locale_list,
                      const std::string& current_locale_iso_code) override;
-  void ShowUpdateIcon(mojom::UpdateSeverity severity,
+  void ShowUpdateIcon(UpdateSeverity severity,
                       bool factory_reset_required,
                       bool rollback,
-                      mojom::UpdateType update_type) override;
+                      UpdateType update_type) override;
   void SetUpdateNotificationState(
-      mojom::NotificationStyle style,
+      NotificationStyle style,
       const base::string16& notification_title,
       const base::string16& notification_body) override;
   void SetUpdateOverCellularAvailableIconVisible(bool visible) override;
diff --git a/ash/system/model/update_model.cc b/ash/system/model/update_model.cc
index f0e5e8b..68f10993 100644
--- a/ash/system/model/update_model.cc
+++ b/ash/system/model/update_model.cc
@@ -17,10 +17,10 @@
   observers_.RemoveObserver(observer);
 }
 
-void UpdateModel::SetUpdateAvailable(mojom::UpdateSeverity severity,
+void UpdateModel::SetUpdateAvailable(UpdateSeverity severity,
                                      bool factory_reset_required,
                                      bool rollback,
-                                     mojom::UpdateType update_type) {
+                                     UpdateType update_type) {
   update_required_ = true;
   severity_ = severity;
   factory_reset_required_ = factory_reset_required;
@@ -30,10 +30,10 @@
 }
 
 void UpdateModel::SetUpdateNotificationState(
-    mojom::NotificationStyle style,
+    NotificationStyle style,
     const base::string16& notification_title,
     const base::string16& notification_body) {
-  DCHECK_EQ(update_type_, mojom::UpdateType::SYSTEM);
+  DCHECK_EQ(update_type_, UpdateType::kSystem);
   notification_style_ = style;
   notification_title_ = notification_title;
   notification_body_ = notification_body;
@@ -45,12 +45,11 @@
   NotifyUpdateAvailable();
 }
 
-mojom::UpdateSeverity UpdateModel::GetSeverity() const {
+UpdateSeverity UpdateModel::GetSeverity() const {
   // TODO(https://crbug.com/927010): adjust severity according the amount of
   // time passing after update is available over cellular connection. Use low
   // severity for update available over cellular connection.
-  return update_over_cellular_available_ ? mojom::UpdateSeverity::LOW
-                                         : severity_;
+  return update_over_cellular_available_ ? UpdateSeverity::kLow : severity_;
 }
 
 void UpdateModel::NotifyUpdateAvailable() {
diff --git a/ash/system/model/update_model.h b/ash/system/model/update_model.h
index c55bcb3..6f381e42 100644
--- a/ash/system/model/update_model.h
+++ b/ash/system/model/update_model.h
@@ -5,7 +5,7 @@
 #ifndef ASH_SYSTEM_MODEL_UPDATE_MODEL_H_
 #define ASH_SYSTEM_MODEL_UPDATE_MODEL_H_
 
-#include "ash/public/interfaces/update.mojom.h"
+#include "ash/public/cpp/update_types.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "base/strings/string16.h"
@@ -32,16 +32,16 @@
   // until reboot. Based on |severity|, |factory_reset_required| and |rollback|,
   // the observer views can indicate the severity of the update to users by
   // changing the icon, color, and tooltip.
-  void SetUpdateAvailable(mojom::UpdateSeverity severity,
+  void SetUpdateAvailable(UpdateSeverity severity,
                           bool factory_reset_required,
                           bool rollback,
-                          mojom::UpdateType update_type);
+                          UpdateType update_type);
 
   // Store the state of the notification according to the RelaunchNotification
   // policy. State persists until reboot or another call to this function.
   // The |notification_body| changes the text of the notification, as it
   // contains a countdown until the required reboot.
-  void SetUpdateNotificationState(mojom::NotificationStyle style,
+  void SetUpdateNotificationState(NotificationStyle style,
                                   const base::string16& notification_title,
                                   const base::string16& notification_body);
 
@@ -51,15 +51,13 @@
   // granted.
   void SetUpdateOverCellularAvailable(bool available);
 
-  mojom::UpdateSeverity GetSeverity() const;
+  UpdateSeverity GetSeverity() const;
 
   bool update_required() const { return update_required_; }
   bool factory_reset_required() const { return factory_reset_required_; }
   bool rollback() const { return rollback_; }
-  mojom::UpdateType update_type() const { return update_type_; }
-  mojom::NotificationStyle notification_style() const {
-    return notification_style_;
-  }
+  UpdateType update_type() const { return update_type_; }
+  NotificationStyle notification_style() const { return notification_style_; }
   const base::string16& notification_title() const {
     return notification_title_;
   }
@@ -72,12 +70,11 @@
   void NotifyUpdateAvailable();
 
   bool update_required_ = false;
-  mojom::UpdateSeverity severity_ = mojom::UpdateSeverity::NONE;
+  UpdateSeverity severity_ = UpdateSeverity::kNone;
   bool factory_reset_required_ = false;
   bool rollback_ = false;
-  mojom::UpdateType update_type_ = mojom::UpdateType::SYSTEM;
-  mojom::NotificationStyle notification_style_ =
-      mojom::NotificationStyle::DEFAULT;
+  UpdateType update_type_ = UpdateType::kSystem;
+  NotificationStyle notification_style_ = NotificationStyle::kDefault;
   base::string16 notification_title_;
   base::string16 notification_body_;
   bool update_over_cellular_available_ = false;
diff --git a/ash/system/network/network_state_list_detailed_view.cc b/ash/system/network/network_state_list_detailed_view.cc
index d01417c..356fd35 100644
--- a/ash/system/network/network_state_list_detailed_view.cc
+++ b/ash/system/network/network_state_list_detailed_view.cc
@@ -380,12 +380,8 @@
   const DeviceStateProperties* device =
       network ? model_->GetDevice(network->type) : nullptr;
   if (device) {
-    net::IPAddress ipv4(device->ipv4_address.data(),
-                        device->ipv4_address.size());
-    ipv4_address = ipv4.ToString();
-    net::IPAddress ipv6(device->ipv6_address.data(),
-                        device->ipv6_address.size());
-    ipv6_address = ipv6.ToString();
+    ipv4_address = device->ipv4_address.ToString();
+    ipv6_address = device->ipv6_address.ToString();
   }
 
   std::string ethernet_address, wifi_address;
diff --git a/ash/system/overview/overview_button_tray.cc b/ash/system/overview/overview_button_tray.cc
index da5f478..4572bdf 100644
--- a/ash/system/overview/overview_button_tray.cc
+++ b/ash/system/overview/overview_button_tray.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/overview/overview_button_tray.h"
 
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
diff --git a/ash/system/overview/overview_button_tray_unittest.cc b/ash/system/overview/overview_button_tray_unittest.cc
index d619967..ceb1cf5 100644
--- a/ash/system/overview/overview_button_tray_unittest.cc
+++ b/ash/system/overview/overview_button_tray_unittest.cc
@@ -400,7 +400,12 @@
   void SetUp() override {
     set_start_session(false);
     OverviewButtonTrayTest::SetUp();
-    client_ = BindMockKioskNextShellClient();
+    client_ = std::make_unique<MockKioskNextShellClient>();
+  }
+
+  void TearDown() override {
+    client_.reset();
+    OverviewButtonTrayTest::TearDown();
   }
 
  private:
diff --git a/ash/system/palette/palette_tray.cc b/ash/system/palette/palette_tray.cc
index 7da1b778..f1998614b 100644
--- a/ash/system/palette/palette_tray.cc
+++ b/ash/system/palette/palette_tray.cc
@@ -7,7 +7,7 @@
 #include <memory>
 
 #include "ash/accessibility/accessibility_controller.h"
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/stylus_utils.h"
 #include "ash/public/cpp/system_tray_client.h"
diff --git a/ash/system/palette/palette_tray_unittest.cc b/ash/system/palette/palette_tray_unittest.cc
index a98387f..89d000aec 100644
--- a/ash/system/palette/palette_tray_unittest.cc
+++ b/ash/system/palette/palette_tray_unittest.cc
@@ -775,7 +775,12 @@
   void SetUp() override {
     set_start_session(false);
     PaletteTrayTest::SetUp();
-    client_ = BindMockKioskNextShellClient();
+    client_ = std::make_unique<MockKioskNextShellClient>();
+  }
+
+  void TearDown() override {
+    client_.reset();
+    PaletteTrayTest::TearDown();
   }
 
  private:
diff --git a/ash/system/palette/palette_welcome_bubble.cc b/ash/system/palette/palette_welcome_bubble.cc
index 3e171c30..b21770d37 100644
--- a/ash/system/palette/palette_welcome_bubble.cc
+++ b/ash/system/palette/palette_welcome_bubble.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller_impl.h"
diff --git a/ash/system/power/power_button_controller_unittest.cc b/ash/system/power/power_button_controller_unittest.cc
index 13bbcda5..48c732a7 100644
--- a/ash/system/power/power_button_controller_unittest.cc
+++ b/ash/system/power/power_button_controller_unittest.cc
@@ -8,7 +8,7 @@
 #include "ash/accessibility/test_accessibility_controller_client.h"
 #include "ash/display/screen_orientation_controller.h"
 #include "ash/display/screen_orientation_controller_test_api.h"
-#include "ash/media/media_controller.h"
+#include "ash/media/media_controller_impl.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
@@ -709,7 +709,7 @@
 // sessions should be suspended.
 TEST_F(PowerButtonControllerTest, SuspendMediaSessions) {
   TestMediaClient client;
-  Shell::Get()->media_controller()->SetClient(client.CreateAssociatedPtrInfo());
+  Shell::Get()->media_controller()->SetClient(&client);
   ASSERT_FALSE(client.media_sessions_suspended());
 
   EnableTabletMode(true);
diff --git a/ash/system/power/power_button_display_controller.cc b/ash/system/power/power_button_display_controller.cc
index 961fc34..513d008 100644
--- a/ash/system/power/power_button_display_controller.cc
+++ b/ash/system/power/power_button_display_controller.cc
@@ -5,7 +5,7 @@
 #include "ash/system/power/power_button_display_controller.h"
 
 #include "ash/accessibility/accessibility_controller.h"
-#include "ash/media/media_controller.h"
+#include "ash/media/media_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/system/power/scoped_backlights_forced_off.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
diff --git a/ash/system/session/logout_button_tray.cc b/ash/system/session/logout_button_tray.cc
index c13275a9..2808a1b 100644
--- a/ash/system/session/logout_button_tray.cc
+++ b/ash/system/session/logout_button_tray.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/ash_typography.h"
 #include "ash/resources/vector_icons/vector_icons.h"
diff --git a/ash/system/session/logout_button_tray_unittest.cc b/ash/system/session/logout_button_tray_unittest.cc
index f4e6550..e100b20 100644
--- a/ash/system/session/logout_button_tray_unittest.cc
+++ b/ash/system/session/logout_button_tray_unittest.cc
@@ -150,7 +150,12 @@
 
   void SetUp() override {
     LogoutButtonTrayTest::SetUp();
-    client_ = BindMockKioskNextShellClient();
+    client_ = std::make_unique<MockKioskNextShellClient>();
+  }
+
+  void TearDown() override {
+    client_.reset();
+    LogoutButtonTrayTest::TearDown();
   }
 
  private:
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index f1d61d6..6f938c6 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/status_area_widget.h"
 
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
diff --git a/ash/system/unified/top_shortcuts_view.cc b/ash/system/unified/top_shortcuts_view.cc
index 0b5ff81..155c5ec 100644
--- a/ash/system/unified/top_shortcuts_view.cc
+++ b/ash/system/unified/top_shortcuts_view.cc
@@ -7,7 +7,7 @@
 #include <numeric>
 
 #include "ash/accessibility/accessibility_controller.h"
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/public/cpp/ash_view_ids.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
diff --git a/ash/system/unified/top_shortcuts_view_unittest.cc b/ash/system/unified/top_shortcuts_view_unittest.cc
index a105e62..8f72a2af 100644
--- a/ash/system/unified/top_shortcuts_view_unittest.cc
+++ b/ash/system/unified/top_shortcuts_view_unittest.cc
@@ -35,6 +35,7 @@
   }
 
   void TearDown() override {
+    kiosk_next_shell_client_.reset();
     controller_.reset();
     top_shortcuts_view_.reset();
     model_.reset();
@@ -47,7 +48,7 @@
   }
 
   void CreateKioskNextSession() {
-    kiosk_next_shell_client_ = BindMockKioskNextShellClient();
+    kiosk_next_shell_client_ = std::make_unique<MockKioskNextShellClient>();
     LogInKioskNextUser(GetSessionControllerClient());
   }
 
diff --git a/ash/system/unified/unified_system_tray_controller.cc b/ash/system/unified/unified_system_tray_controller.cc
index d4e0d712..2a8bd67 100644
--- a/ash/system/unified/unified_system_tray_controller.cc
+++ b/ash/system/unified/unified_system_tray_controller.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/unified/unified_system_tray_controller.h"
 
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/metrics/user_metrics_action.h"
 #include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/system_tray_client.h"
diff --git a/ash/system/unified/user_chooser_view.cc b/ash/system/unified/user_chooser_view.cc
index b21ec3aa..3b1be98 100644
--- a/ash/system/unified/user_chooser_view.cc
+++ b/ash/system/unified/user_chooser_view.cc
@@ -247,22 +247,22 @@
   SetFocusForPlatform();
 }
 
-void UserItemButton::SetCaptureState(mojom::MediaCaptureState capture_state) {
-  capture_icon_->SetVisible(capture_state != mojom::MediaCaptureState::NONE);
+void UserItemButton::SetCaptureState(MediaCaptureState capture_state) {
+  capture_icon_->SetVisible(capture_state != MediaCaptureState::kNone);
   Layout();
 
   int res_id = 0;
   switch (capture_state) {
-    case mojom::MediaCaptureState::AUDIO_VIDEO:
+    case MediaCaptureState::kAudioVideo:
       res_id = IDS_ASH_STATUS_TRAY_MEDIA_RECORDING_AUDIO_VIDEO;
       break;
-    case mojom::MediaCaptureState::AUDIO:
+    case MediaCaptureState::kAudio:
       res_id = IDS_ASH_STATUS_TRAY_MEDIA_RECORDING_AUDIO;
       break;
-    case mojom::MediaCaptureState::VIDEO:
+    case MediaCaptureState::kVideo:
       res_id = IDS_ASH_STATUS_TRAY_MEDIA_RECORDING_VIDEO;
       break;
-    case mojom::MediaCaptureState::NONE:
+    case MediaCaptureState::kNone:
       break;
   }
   if (res_id)
@@ -326,7 +326,7 @@
 }
 
 void UserChooserView::OnMediaCaptureChanged(
-    const base::flat_map<AccountId, mojom::MediaCaptureState>& capture_states) {
+    const base::flat_map<AccountId, MediaCaptureState>& capture_states) {
   if (user_item_buttons_.size() != capture_states.size())
     return;
 
diff --git a/ash/system/unified/user_chooser_view.h b/ash/system/unified/user_chooser_view.h
index d8c6302..8e9f332 100644
--- a/ash/system/unified/user_chooser_view.h
+++ b/ash/system/unified/user_chooser_view.h
@@ -5,7 +5,7 @@
 #ifndef ASH_SYSTEM_UNIFIED_USER_CHOOSER_VIEW_H_
 #define ASH_SYSTEM_UNIFIED_USER_CHOOSER_VIEW_H_
 
-#include "ash/media/media_controller.h"
+#include "ash/media/media_controller_impl.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/view.h"
 
@@ -32,7 +32,7 @@
                  bool has_close_button);
   ~UserItemButton() override = default;
 
-  void SetCaptureState(mojom::MediaCaptureState capture_states);
+  void SetCaptureState(MediaCaptureState capture_states);
 
   // views::Button:
   base::string16 GetTooltipText(const gfx::Point& p) const override;
@@ -57,9 +57,8 @@
   ~UserChooserView() override;
 
   // MediaCaptureObserver:
-  void OnMediaCaptureChanged(
-      const base::flat_map<AccountId, mojom::MediaCaptureState>& capture_states)
-      override;
+  void OnMediaCaptureChanged(const base::flat_map<AccountId, MediaCaptureState>&
+                                 capture_states) override;
 
   // views::View:
   const char* GetClassName() const override;
diff --git a/ash/system/update/update_notification_controller.cc b/ash/system/update/update_notification_controller.cc
index 1f2814c6e2..2dc7d2d 100644
--- a/ash/system/update/update_notification_controller.cc
+++ b/ash/system/update/update_notification_controller.cc
@@ -49,7 +49,7 @@
 
   message_center::SystemNotificationWarningLevel warning_level =
       (model_->rollback() ||
-       model_->notification_style() == mojom::NotificationStyle::ADMIN_REQUIRED)
+       model_->notification_style() == NotificationStyle::kAdminRequired)
           ? message_center::SystemNotificationWarningLevel::WARNING
           : message_center::SystemNotificationWarningLevel::NORMAL;
   std::unique_ptr<Notification> notification = ash::CreateSystemNotification(
@@ -68,7 +68,7 @@
       warning_level);
   notification->set_pinned(true);
 
-  if (model_->notification_style() == mojom::NotificationStyle::ADMIN_REQUIRED)
+  if (model_->notification_style() == NotificationStyle::kAdminRequired)
     notification->SetSystemPriority();
 
   if (model_->update_required()) {
@@ -102,7 +102,7 @@
   }
 
   const base::string16 notification_body = model_->notification_body();
-  if (model_->update_type() == mojom::UpdateType::SYSTEM &&
+  if (model_->update_type() == UpdateType::kSystem &&
       !notification_body.empty()) {
     return notification_body;
   }
@@ -113,7 +113,7 @@
 
 base::string16 UpdateNotificationController::GetNotificationTitle() const {
 #if defined(GOOGLE_CHROME_BUILD)
-  if (model_->update_type() == mojom::UpdateType::FLASH) {
+  if (model_->update_type() == UpdateType::kFlash) {
     return l10n_util::GetStringUTF16(
         IDS_UPDATE_NOTIFICATION_TITLE_FLASH_PLAYER);
   }
diff --git a/ash/system/update/update_notification_controller_unittest.cc b/ash/system/update/update_notification_controller_unittest.cc
index 038f593..a477545 100644
--- a/ash/system/update/update_notification_controller_unittest.cc
+++ b/ash/system/update/update_notification_controller_unittest.cc
@@ -83,8 +83,8 @@
   EXPECT_FALSE(HasNotification());
 
   // Simulate an update.
-  Shell::Get()->system_tray_model()->ShowUpdateIcon(
-      mojom::UpdateSeverity::LOW, false, false, mojom::UpdateType::SYSTEM);
+  Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false,
+                                                    false, UpdateType::kSystem);
 
   // The notification is now visible.
   ASSERT_TRUE(HasNotification());
@@ -101,8 +101,8 @@
   EXPECT_FALSE(HasNotification());
 
   // Simulate an update.
-  Shell::Get()->system_tray_model()->ShowUpdateIcon(
-      mojom::UpdateSeverity::LOW, false, false, mojom::UpdateType::FLASH);
+  Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false,
+                                                    false, UpdateType::kFlash);
 
   // The notification is now visible.
   ASSERT_TRUE(HasNotification());
@@ -148,8 +148,8 @@
   EXPECT_FALSE(HasNotification());
 
   // Simulate an update that requires factory reset.
-  Shell::Get()->system_tray_model()->ShowUpdateIcon(
-      mojom::UpdateSeverity::LOW, true, false, mojom::UpdateType::SYSTEM);
+  Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, true,
+                                                    false, UpdateType::kSystem);
 
   // The notification is now visible.
   ASSERT_TRUE(HasNotification());
@@ -167,8 +167,8 @@
   EXPECT_FALSE(HasNotification());
 
   // Simulate a rollback.
-  Shell::Get()->system_tray_model()->ShowUpdateIcon(
-      mojom::UpdateSeverity::LOW, false, true, mojom::UpdateType::SYSTEM);
+  Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false,
+                                                    true, UpdateType::kSystem);
 
   // The notification is now visible.
   ASSERT_TRUE(HasNotification());
@@ -186,8 +186,8 @@
   EXPECT_FALSE(HasNotification());
 
   // Simulate an update.
-  Shell::Get()->system_tray_model()->ShowUpdateIcon(
-      mojom::UpdateSeverity::LOW, false, false, mojom::UpdateType::SYSTEM);
+  Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false,
+                                                    false, UpdateType::kSystem);
 
   // The notification is now visible.
   ASSERT_TRUE(HasNotification());
@@ -204,7 +204,7 @@
 
   // Simulate notification type set to recommended.
   Shell::Get()->system_tray_model()->SetUpdateNotificationState(
-      mojom::NotificationStyle::ADMIN_RECOMMENDED,
+      NotificationStyle::kAdminRecommended,
       base::UTF8ToUTF16(recommended_notification_title),
       base::UTF8ToUTF16(recommended_notification_body));
 
@@ -224,7 +224,7 @@
 
   // Simulate notification type set to required.
   Shell::Get()->system_tray_model()->SetUpdateNotificationState(
-      mojom::NotificationStyle::ADMIN_REQUIRED,
+      NotificationStyle::kAdminRequired,
       base::UTF8ToUTF16(required_notification_title),
       base::UTF8ToUTF16(required_notification_body));
 
@@ -239,7 +239,7 @@
 
   // Simulate notification type set back to default.
   Shell::Get()->system_tray_model()->SetUpdateNotificationState(
-      mojom::NotificationStyle::DEFAULT, base::string16(), base::string16());
+      NotificationStyle::kDefault, base::string16(), base::string16());
 
   // The notification has the default text.
   ASSERT_TRUE(HasNotification());
diff --git a/ash/system/virtual_keyboard/virtual_keyboard_tray.cc b/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
index 56c3628e9..3266ad0 100644
--- a/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
+++ b/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
@@ -8,7 +8,7 @@
 
 #include "ash/accessibility/accessibility_controller.h"
 #include "ash/keyboard/ui/keyboard_controller.h"
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shelf/shelf.h"
diff --git a/ash/system/virtual_keyboard/virtual_keyboard_tray_unittest.cc b/ash/system/virtual_keyboard/virtual_keyboard_tray_unittest.cc
index 22fd00ba..6425d5a 100644
--- a/ash/system/virtual_keyboard/virtual_keyboard_tray_unittest.cc
+++ b/ash/system/virtual_keyboard/virtual_keyboard_tray_unittest.cc
@@ -76,7 +76,12 @@
   void SetUp() override {
     set_start_session(false);
     AshTestBase::SetUp();
-    client_ = BindMockKioskNextShellClient();
+    client_ = std::make_unique<MockKioskNextShellClient>();
+  }
+
+  void TearDown() override {
+    client_.reset();
+    AshTestBase::TearDown();
   }
 
   void EnableVirtualKeyboardForActiveUser() {
diff --git a/ash/test_media_client.cc b/ash/test_media_client.cc
index 21d51d0..7d9d0f4 100644
--- a/ash/test_media_client.cc
+++ b/ash/test_media_client.cc
@@ -4,20 +4,11 @@
 
 #include "ash/test_media_client.h"
 
-#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
-
 namespace ash {
 
-TestMediaClient::TestMediaClient() : binding_(this) {}
-
+TestMediaClient::TestMediaClient() = default;
 TestMediaClient::~TestMediaClient() = default;
 
-mojom::MediaClientAssociatedPtrInfo TestMediaClient::CreateAssociatedPtrInfo() {
-  mojom::MediaClientAssociatedPtr ptr;
-  binding_.Bind(mojo::MakeRequestAssociatedWithDedicatedPipe(&ptr));
-  return ptr.PassInterface();
-}
-
 void TestMediaClient::HandleMediaNextTrack() {
   ++handle_media_next_track_count_;
 }
diff --git a/ash/test_media_client.h b/ash/test_media_client.h
index 9332e8f..265df064 100644
--- a/ash/test_media_client.h
+++ b/ash/test_media_client.h
@@ -5,21 +5,19 @@
 #ifndef ASH_TEST_MEDIA_CLIENT_H_
 #define ASH_TEST_MEDIA_CLIENT_H_
 
-#include "ash/public/interfaces/media.mojom.h"
-#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "ash/public/cpp/media_client.h"
+#include "base/macros.h"
 
 namespace ash {
 
 // Implement MediaClient mojo interface to simulate chrome behavior in tests.
 // This breaks the ash/chrome dependency to allow testing ash code in isolation.
-class TestMediaClient : public mojom::MediaClient {
+class TestMediaClient : public MediaClient {
  public:
   TestMediaClient();
   ~TestMediaClient() override;
 
-  mojom::MediaClientAssociatedPtrInfo CreateAssociatedPtrInfo();
-
-  // mojom::MediaClient:
+  // MediaClient:
   void HandleMediaNextTrack() override;
   void HandleMediaPlayPause() override;
   void HandleMediaPrevTrack() override;
@@ -43,8 +41,6 @@
   int handle_media_prev_track_count_ = 0;
   bool media_sessions_suspended_ = false;
 
-  mojo::AssociatedBinding<mojom::MediaClient> binding_;
-
   DISALLOW_COPY_AND_ASSIGN(TestMediaClient);
 };
 
diff --git a/ash/wm/desks/desk.cc b/ash/wm/desks/desk.cc
index adc51fe..f51beeb 100644
--- a/ash/wm/desks/desk.cc
+++ b/ash/wm/desks/desk.cc
@@ -245,6 +245,12 @@
     MoveWindowToDeskInternal(window, target_desk);
 
     windows_.erase(window);
+
+    // Unminimize the window so that it shows up in the mini_view after it had
+    // been dragged and moved to another desk.
+    auto* window_state = wm::GetWindowState(window);
+    if (window_state->IsMinimized())
+      window_state->Unminimize();
   }
 
   NotifyContentChanged();
diff --git a/ash/wm/desks/desks_controller.h b/ash/wm/desks/desks_controller.h
index 04826a32..8bdbe04 100644
--- a/ash/wm/desks/desks_controller.h
+++ b/ash/wm/desks/desks_controller.h
@@ -93,7 +93,8 @@
   void ActivateDesk(const Desk* desk);
 
   // Moves |window| (which must belong to the currently active desk) to
-  // |target_desk| (which must be a different desk).
+  // |target_desk| (which must be a different desk). If |window| is minimized,
+  // it will be unminimized after it's moved to |target_desk|.
   void MoveWindowFromActiveDeskTo(aura::Window* window, Desk* target_desk);
 
   // Called explicitly by the RootWindowController when a root window has been
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 8694d4b..3763f0a7 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -936,6 +936,57 @@
   EXPECT_TRUE(shadow->layer()->GetTargetVisibility());
 }
 
+TEST_F(DesksTest, DragMinimizedWindowToDesk) {
+  auto* controller = DesksController::Get();
+  controller->NewDesk();
+  ASSERT_EQ(2u, controller->desks().size());
+  const Desk* desk_2 = controller->desks()[1].get();
+
+  auto window = CreateTestWindow(gfx::Rect(0, 0, 250, 100));
+  wm::ActivateWindow(window.get());
+  EXPECT_EQ(window.get(), wm::GetActiveWindow());
+
+  // Minimize the window before entering Overview Mode.
+  auto* window_state = wm::GetWindowState(window.get());
+  window_state->Minimize();
+  ASSERT_TRUE(window_state->IsMinimized());
+
+  auto* overview_controller = Shell::Get()->overview_controller();
+  overview_controller->ToggleOverview();
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
+  auto* overview_session = overview_controller->overview_session();
+  auto* overview_item =
+      overview_session->GetOverviewItemForWindow(window.get());
+  ASSERT_TRUE(overview_item);
+  const auto* desks_bar_view = overview_grid->GetDesksBarViewForTesting();
+  ASSERT_TRUE(desks_bar_view);
+  ASSERT_EQ(2u, desks_bar_view->mini_views().size());
+
+  // Drag the window to desk_2's mini_view and activate desk_2. Expect that the
+  // window will be in an unminimized state and all its visibility and layer
+  // opacity attributes are correct.
+  auto* desk_2_mini_view = desks_bar_view->mini_views()[1].get();
+  EXPECT_EQ(desk_2, desk_2_mini_view->desk());
+  DragItemToPoint(overview_item,
+                  desk_2_mini_view->GetBoundsInScreen().CenterPoint(),
+                  GetEventGenerator());
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(overview_grid->empty());
+  EXPECT_TRUE(desk_2->windows().contains(window.get()));
+  EXPECT_FALSE(overview_grid->drop_target_widget());
+  DeskSwitchAnimationWaiter waiter;
+  ClickOnMiniView(desk_2_mini_view, GetEventGenerator());
+  waiter.Wait();
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(desk_2->is_active());
+
+  EXPECT_FALSE(window_state->IsMinimized());
+  EXPECT_TRUE(window->IsVisible());
+  EXPECT_TRUE(window->layer()->GetTargetVisibility());
+  EXPECT_EQ(1.f, window->layer()->GetTargetOpacity());
+}
+
 TEST_F(DesksTest, DragWindowToNonMiniViewPoints) {
   auto* controller = DesksController::Get();
   controller->NewDesk();
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index a86e5b1..40c73b5 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -8,7 +8,7 @@
 #include <functional>
 #include <utility>
 
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/metrics/histogram_macros.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/fps_counter.h"
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index 1a678ab..5e476278 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -534,7 +534,7 @@
 void OverviewItem::OnMinimizedStateChanged() {
   const bool minimized = transform_window_.IsMinimized();
   caption_container_view_->SetShowPreview(minimized);
-  if (minimized)
+  if (!minimized)
     EnsureVisible();
 
   if (window_surface_cache_observers_) {
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index 75119626..c4c6ec47 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -261,9 +261,7 @@
         CurrentValueBetween(starting_position_, ending_position_);
     split_view_controller_->NotifyDividerPositionChanged();
     split_view_controller_->UpdateSnappedWindowsAndDividerBounds();
-    // Updating the window may stop animation.
-    if (is_animating())
-      split_view_controller_->SetWindowsTransformDuringResizing();
+    split_view_controller_->SetWindowsTransformDuringResizing();
   }
 
   SplitViewController* split_view_controller_;
@@ -674,11 +672,8 @@
   const bool is_divider_animating = IsDividerAnimating();
   if (is_resizing_ || is_divider_animating) {
     is_resizing_ = false;
-    if (is_divider_animating) {
-      // Don't call StopAndShoveAnimatedDivider as it will call observers.
-      divider_snap_animation_->Stop();
-      divider_position_ = divider_snap_animation_->ending_position();
-    }
+    if (is_divider_animating)
+      StopAndShoveAnimatedDivider();
     EndResizeImpl();
   }
 
diff --git a/ash/wm/splitview/split_view_controller_unittest.cc b/ash/wm/splitview/split_view_controller_unittest.cc
index 0480891..0d08de5 100644
--- a/ash/wm/splitview/split_view_controller_unittest.cc
+++ b/ash/wm/splitview/split_view_controller_unittest.cc
@@ -4615,10 +4615,15 @@
 
     SplitViewAppDraggingTest::SetUp();
 
-    client_ = BindMockKioskNextShellClient();
+    client_ = std::make_unique<MockKioskNextShellClient>();
     LogInKioskNextUser(GetSessionControllerClient());
   }
 
+  void TearDown() override {
+    client_.reset();
+    SplitViewAppDraggingTest::TearDown();
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<MockKioskNextShellClient> client_;
diff --git a/ash/wm/splitview/split_view_utils.cc b/ash/wm/splitview/split_view_utils.cc
index b9fb0d5c..0a63faf2 100644
--- a/ash/wm/splitview/split_view_utils.cc
+++ b/ash/wm/splitview/split_view_utils.cc
@@ -6,7 +6,7 @@
 
 #include "ash/accessibility/accessibility_controller.h"
 #include "ash/display/screen_orientation_controller.h"
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/screen_util.h"
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.cc b/ash/wm/tablet_mode/tablet_mode_controller.cc
index cb2e6298..f02a8a8 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller.cc
@@ -8,7 +8,7 @@
 #include <string>
 #include <utility>
 
-#include "ash/kiosk_next/kiosk_next_shell_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/fps_counter.h"
 #include "ash/public/cpp/tablet_mode.h"
diff --git a/ash/wm/window_finder.cc b/ash/wm/window_finder.cc
index bc8a8fb4..e173304 100644
--- a/ash/wm/window_finder.cc
+++ b/ash/wm/window_finder.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/wm/window_finder.h"
+#include "ash/public/cpp/window_finder.h"
 
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
@@ -113,14 +113,13 @@
 }  // namespace
 
 namespace ash {
-namespace wm {
 
 aura::Window* GetTopmostWindowAtPoint(const gfx::Point& screen_point,
                                       const std::set<aura::Window*>& ignore,
                                       aura::Window** real_topmost) {
   if (real_topmost)
     *real_topmost = nullptr;
-  aura::Window* root = GetRootWindowAt(screen_point);
+  aura::Window* root = wm::GetRootWindowAt(screen_point);
   // GetTopmostWindowAtPointWithinWindow() always needs to be called to update
   // |real_topmost| correctly.
   aura::Window* topmost_window = GetTopmostWindowAtPointWithinWindow(
@@ -130,5 +129,4 @@
   return overview_window ? overview_window : topmost_window;
 }
 
-}  // namespace wm
 }  // namespace ash
diff --git a/ash/wm/window_finder_unittest.cc b/ash/wm/window_finder_unittest.cc
index 594a1a4..bfc5a3b2 100644
--- a/ash/wm/window_finder_unittest.cc
+++ b/ash/wm/window_finder_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/wm/window_finder.h"
+#include "ash/public/cpp/window_finder.h"
 
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
@@ -15,7 +15,6 @@
 #include "ui/gfx/geometry/insets.h"
 
 namespace ash {
-namespace wm {
 
 using WindowFinderTest = AshTestBase;
 
@@ -106,5 +105,4 @@
                                                    ignore, &real_topmost));
 }
 
-}  // namespace wm
 }  // namespace ash
diff --git a/ash/wm/window_mirror_view.cc b/ash/wm/window_mirror_view.cc
index fc988f4..588b534 100644
--- a/ash/wm/window_mirror_view.cc
+++ b/ash/wm/window_mirror_view.cc
@@ -77,11 +77,13 @@
 
   gfx::Transform transform;
   gfx::Rect client_area_bounds = GetClientAreaBounds();
-  // Scale down if necessary.
+  // Scale if necessary.
   if (size() != source_->bounds().size()) {
-    const float scale =
+    const float scale_x =
         width() / static_cast<float>(client_area_bounds.width());
-    transform.Scale(scale, scale);
+    const float scale_y =
+        height() / static_cast<float>(client_area_bounds.height());
+    transform.Scale(scale_x, scale_y);
   }
   // Reposition such that the client area is the only part visible.
   transform.Translate(-client_area_bounds.x(), -client_area_bounds.y());
diff --git a/ash/wm/window_preview_view.cc b/ash/wm/window_preview_view.cc
index d30a858..81a6d00f 100644
--- a/ash/wm/window_preview_view.cc
+++ b/ash/wm/window_preview_view.cc
@@ -73,12 +73,11 @@
                        local_bounds.height() / union_rect.height());
   for (auto entry : mirror_views_) {
     const gfx::Rect bounds = entry.first->GetBoundsInScreen();
-    const int top_inset = entry.first->GetProperty(aura::client::kTopViewInset);
     gfx::Rect mirror_bounds;
     mirror_bounds.set_x(
         gfx::ToRoundedInt((bounds.x() - union_origin.x()) * scale.x()));
-    mirror_bounds.set_y(gfx::ToRoundedInt(
-        (bounds.y() + top_inset - union_origin.y()) * scale.y()));
+    mirror_bounds.set_y(
+        gfx::ToRoundedInt((bounds.y() - union_origin.y()) * scale.y()));
     mirror_bounds.set_width(gfx::ToRoundedInt(bounds.width() * scale.x()));
     mirror_bounds.set_height(gfx::ToRoundedInt(bounds.height() * scale.y()));
     entry.second->SetBoundsRect(mirror_bounds);
@@ -130,14 +129,10 @@
 }
 
 gfx::RectF WindowPreviewView::GetUnionRect() const {
-  gfx::RectF bounds;
-  for (auto entry : mirror_views_) {
-    gfx::RectF entry_bounds(entry.first->GetBoundsInScreen());
-    entry_bounds.Inset(0, entry.first->GetProperty(aura::client::kTopViewInset),
-                       0, 0);
-    bounds.Union(entry_bounds);
-  }
-  return bounds;
+  gfx::Rect bounds;
+  for (auto entry : mirror_views_)
+    bounds.Union(entry.first->GetBoundsInScreen());
+  return gfx::RectF(bounds);
 }
 
 }  // namespace wm
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 9d689f8..702655d 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2690,8 +2690,8 @@
     "task/thread_pool/worker_thread_unittest.cc",
     "task_runner_util_unittest.cc",
     "template_util_unittest.cc",
+    "test/gmock_callback_support_unittest.cc",
     "test/launcher/test_launcher_unittest.cc",
-    "test/launcher/test_results_tracker_unittest.cc",
     "test/launcher/unit_test_launcher_unittest.cc",
     "test/metrics/histogram_enum_reader_unittest.cc",
     "test/metrics/histogram_tester_unittest.cc",
@@ -2889,7 +2889,6 @@
       "sync_socket_unittest.cc",
       "synchronization/waitable_event_watcher_unittest.cc",
       "test/launcher/test_launcher_unittest.cc",
-      "test/launcher/test_results_tracker_unittest.cc",
       "test/launcher/unit_test_launcher_unittest.cc",
     ]
 
diff --git a/base/android/library_loader/library_prefetcher_unittest.cc b/base/android/library_loader/library_prefetcher_unittest.cc
index 67ae24c..697cd07 100644
--- a/base/android/library_loader/library_prefetcher_unittest.cc
+++ b/base/android/library_loader/library_prefetcher_unittest.cc
@@ -22,7 +22,7 @@
 const size_t kPageSize = 4096;
 }  // namespace
 
-TEST(NativeLibraryPrefetcherTest, TestPercentageOfResidentCode) {
+TEST(NativeLibraryPrefetcherTest, DISABLED_TestPercentageOfResidentCode) {
   size_t length = 4 * kPageSize;
   auto shared_region = base::WritableSharedMemoryRegion::Create(length);
   ASSERT_TRUE(shared_region.IsValid());
diff --git a/base/bind.h b/base/bind.h
index c2c2d433..6554485 100644
--- a/base/bind.h
+++ b/base/bind.h
@@ -42,7 +42,7 @@
 //   class C : public base::RefCounted<C> { void F(); };
 //   auto instance = base::MakeRefCounted<C>();
 //   auto cb = base::BindOnce(&C::F, instance);
-//   cb.Run();  // Identical to instance->F()
+//   std::move(cb).Run();  // Identical to instance->F()
 //
 // base::Bind is currently a type alias for base::BindRepeating(). In the
 // future, we expect to flip this to default to base::BindOnce().
diff --git a/base/logging.cc b/base/logging.cc
index 36b8bfc..23b8a99 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -480,13 +480,21 @@
   if (severity < g_min_log_level)
     return false;
 
-  // Return true here unless we know ~LogMessage won't do anything. Note that
-  // ~LogMessage writes to stderr if severity_ >= kAlwaysPrintErrorLevel, even
-  // when g_logging_destination is LOG_NONE.
+  // Return true here unless we know ~LogMessage won't do anything.
   return g_logging_destination != LOG_NONE || log_message_handler ||
          severity >= kAlwaysPrintErrorLevel;
 }
 
+// Returns true when logging destination LOG_TO_STDERR flag is set. Also when
+// configured to only LOG_TO_FILE, continue to log higher-severity messages to
+// stderr as well to better detect and diagnose problems with unit tests,
+// especially on the buildbots.
+bool ShouldLogToStderr(int severity) {
+  return ((g_logging_destination & LOG_TO_STDERR) != 0) ||
+         (severity >= kAlwaysPrintErrorLevel &&
+          g_logging_destination == LOG_TO_FILE);
+}
+
 int GetVlogVerbosity() {
   return std::max(-1, LOG_INFO - GetMinLogLevel());
 }
@@ -848,11 +856,7 @@
 #endif  // OS_FUCHSIA
   }
 
-  if ((g_logging_destination & LOG_TO_STDERR) != 0 ||
-      severity_ >= kAlwaysPrintErrorLevel) {
-    // Write logs with destination LOG_TO_STDERR to stderr. Also output to
-    // stderr for logs above a certain log level to better detect and diagnose
-    // problems with unit tests, especially on the buildbots.
+  if (ShouldLogToStderr(severity_)) {
     ignore_result(fwrite(str_newline.data(), str_newline.size(), 1, stderr));
     fflush(stderr);
   }
diff --git a/base/logging_unittest.cc b/base/logging_unittest.cc
index b316176..d5b31b31 100644
--- a/base/logging_unittest.cc
+++ b/base/logging_unittest.cc
@@ -6,6 +6,8 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/compiler_specific.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
@@ -234,6 +236,60 @@
   LOG(INFO) << mock_log_source_stderr.Log();
 }
 
+// Check that when logging to file and message severity is
+// kAlwaysPrintErrorLevel or higher that the message is also written to stderr.
+// When severity is lower, make sure the message is only written to file.
+// Relies on POSIX specific APIs. Though this feature is cross-platform, testing
+// on POSIX platforms is sufficient.
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+TEST_F(LoggingTest, LogToFileAndStderr) {
+  const char kInfoLogMessage[] = "This is an INFO level message";
+  const char kErrorLogMessage[] = "Here we have a message of level ERROR";
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+  // Set up logging to log to a file.
+  base::FilePath file_logs_path = temp_dir.GetPath().Append("file.log");
+  LoggingSettings settings;
+  settings.logging_dest = LOG_TO_FILE;
+  settings.log_file = file_logs_path.value().c_str();
+  InitLogging(settings);
+
+  // Create a file and change stderr to write to that file, to easily check
+  // contents.
+  base::FilePath stderr_logs_path = temp_dir.GetPath().Append("stderr.log");
+  base::File stderr_logs = base::File(
+      stderr_logs_path,
+      base::File::FLAG_CREATE | base::File::FLAG_WRITE | base::File::FLAG_READ);
+  base::ScopedFD stderr_backup = base::ScopedFD(dup(STDERR_FILENO));
+  int dup_result = dup2(stderr_logs.GetPlatformFile(), STDERR_FILENO);
+  ASSERT_GE(dup_result, 0);
+
+  LOG(INFO) << kInfoLogMessage;
+  LOG(ERROR) << kErrorLogMessage;
+
+  // Restore the original stderr logging destination.
+  dup_result = dup2(stderr_backup.get(), STDERR_FILENO);
+  ASSERT_EQ(dup_result, STDERR_FILENO);
+
+  // Check that both log messages are written to "file.log".
+  std::string written_file_logs;
+  ASSERT_TRUE(base::ReadFileToString(file_logs_path, &written_file_logs));
+  std::size_t found_info_log = written_file_logs.find(kInfoLogMessage);
+  EXPECT_FALSE(found_info_log == std::string::npos);
+  std::size_t found_error_log = written_file_logs.find(kErrorLogMessage);
+  EXPECT_FALSE(found_error_log == std::string::npos);
+
+  // Check that the kErrorLogMessage is written to |stderr.log| and
+  // kInfoLogMessage is not.
+  ASSERT_TRUE(base::ReadFileToString(stderr_logs_path, &written_file_logs));
+  found_info_log = written_file_logs.find(kInfoLogMessage);
+  EXPECT_TRUE(found_info_log == std::string::npos);
+  found_error_log = written_file_logs.find(kErrorLogMessage);
+  EXPECT_FALSE(found_error_log == std::string::npos);
+}
+#endif
+
 // Official builds have CHECKs directly call BreakDebugger.
 #if !defined(OFFICIAL_BUILD)
 
diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h
index a50f638..f445125 100644
--- a/base/process/process_metrics.h
+++ b/base/process/process_metrics.h
@@ -98,14 +98,6 @@
   BASE_EXPORT size_t GetResidentSetSize() const;
 #endif
 
-#if defined(OS_CHROMEOS)
-  struct TotalsSummary {
-    size_t swap_kb;
-  };
-  // Returns memory stats for the process.
-  BASE_EXPORT TotalsSummary GetTotalsSummary() const;
-#endif
-
   // Returns the percentage of time spent executing, across all threads of the
   // process, in the interval since the last time the method was called. Since
   // this considers the total execution time across all threads in a process,
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc
index d4dc92c..7ffccec2 100644
--- a/base/process/process_metrics_linux.cc
+++ b/base/process/process_metrics_linux.cc
@@ -307,14 +307,6 @@
 ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process) {}
 #endif
 
-#if defined(OS_CHROMEOS)
-ProcessMetrics::TotalsSummary ProcessMetrics::GetTotalsSummary() const {
-  ProcessMetrics::TotalsSummary summary = {};
-  summary.swap_kb = GetVmSwapBytes() >> 10;
-  return summary;
-}
-#endif
-
 size_t GetSystemCommitCharge() {
   SystemMemoryInfoKB meminfo;
   if (!GetSystemMemoryInfo(&meminfo))
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index b987a4bc3..6fa884a4 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -54,6 +54,7 @@
     "do_nothing_promise.cc",
     "do_nothing_promise.h",
     "fuzzed_data_provider.h",
+    "gmock_callback_support.h",
     "gtest_util.cc",
     "gtest_util.h",
     "gtest_xml_unittest_result_printer.cc",
diff --git a/media/base/gmock_callback_support.h b/base/test/gmock_callback_support.h
similarity index 77%
rename from media/base/gmock_callback_support.h
rename to base/test/gmock_callback_support.h
index 0c0aa8e..2e03a9f 100644
--- a/media/base/gmock_callback_support.h
+++ b/base/test/gmock_callback_support.h
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MEDIA_BASE_GMOCK_CALLBACK_SUPPORT_H_
-#define MEDIA_BASE_GMOCK_CALLBACK_SUPPORT_H_
+#ifndef BASE_TEST_GMOCK_CALLBACK_SUPPORT_H_
+#define BASE_TEST_GMOCK_CALLBACK_SUPPORT_H_
 
 #include <tuple>
 
 #include "testing/gmock/include/gmock/gmock.h"
 
-namespace media {
+namespace base {
+namespace test {
 
 // Matchers for base::Callback and base::Closure.
 
@@ -27,7 +28,7 @@
 ACTION_TEMPLATE(RunClosure,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_0_VALUE_PARAMS()) {
-  std::get<k>(args).Run();
+  ::testing::get<k>(args).Run();
 }
 
 ACTION_P(RunClosure, closure) {
@@ -63,49 +64,49 @@
 ACTION_TEMPLATE(RunCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_0_VALUE_PARAMS()) {
-  return std::get<k>(args).Run();
+  return ::testing::get<k>(args).Run();
 }
 
 ACTION_TEMPLATE(RunCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_1_VALUE_PARAMS(p0)) {
-  return std::get<k>(args).Run(p0);
+  return ::testing::get<k>(args).Run(p0);
 }
 
 ACTION_TEMPLATE(RunCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_2_VALUE_PARAMS(p0, p1)) {
-  return std::get<k>(args).Run(p0, p1);
+  return ::testing::get<k>(args).Run(p0, p1);
 }
 
 ACTION_TEMPLATE(RunCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_3_VALUE_PARAMS(p0, p1, p2)) {
-  return std::get<k>(args).Run(p0, p1, p2);
+  return ::testing::get<k>(args).Run(p0, p1, p2);
 }
 
 ACTION_TEMPLATE(RunCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_4_VALUE_PARAMS(p0, p1, p2, p3)) {
-  return std::get<k>(args).Run(p0, p1, p2, p3);
+  return ::testing::get<k>(args).Run(p0, p1, p2, p3);
 }
 
 ACTION_TEMPLATE(RunCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) {
-  return std::get<k>(args).Run(p0, p1, p2, p3, p4);
+  return ::testing::get<k>(args).Run(p0, p1, p2, p3, p4);
 }
 
 ACTION_TEMPLATE(RunCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) {
-  return std::get<k>(args).Run(p0, p1, p2, p3, p4, p5);
+  return ::testing::get<k>(args).Run(p0, p1, p2, p3, p4, p5);
 }
 
 ACTION_TEMPLATE(RunCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) {
-  return std::get<k>(args).Run(p0, p1, p2, p3, p4, p5, p6);
+  return ::testing::get<k>(args).Run(p0, p1, p2, p3, p4, p5, p6);
 }
 
 // Various overloads for RunOnceClosure and RunOnceCallback<N>(). These are
@@ -115,7 +116,7 @@
 ACTION_TEMPLATE(RunOnceClosure,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_0_VALUE_PARAMS()) {
-  std::move(std::get<k>(args)).Run();
+  std::move(::testing::get<k>(args)).Run();
 }
 
 ACTION_P(RunOnceClosure, closure) {
@@ -125,51 +126,52 @@
 ACTION_TEMPLATE(RunOnceCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_0_VALUE_PARAMS()) {
-  return std::move(std::get<k>(args)).Run();
+  return std::move(::testing::get<k>(args)).Run();
 }
 
 ACTION_TEMPLATE(RunOnceCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_1_VALUE_PARAMS(p0)) {
-  return std::move(std::get<k>(args)).Run(p0);
+  return std::move(::testing::get<k>(args)).Run(p0);
 }
 
 ACTION_TEMPLATE(RunOnceCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_2_VALUE_PARAMS(p0, p1)) {
-  return std::move(std::get<k>(args)).Run(p0, p1);
+  return std::move(::testing::get<k>(args)).Run(p0, p1);
 }
 
 ACTION_TEMPLATE(RunOnceCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_3_VALUE_PARAMS(p0, p1, p2)) {
-  return std::move(std::get<k>(args)).Run(p0, p1, p2);
+  return std::move(::testing::get<k>(args)).Run(p0, p1, p2);
 }
 
 ACTION_TEMPLATE(RunOnceCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_4_VALUE_PARAMS(p0, p1, p2, p3)) {
-  return std::move(std::get<k>(args)).Run(p0, p1, p2, p3);
+  return std::move(::testing::get<k>(args)).Run(p0, p1, p2, p3);
 }
 
 ACTION_TEMPLATE(RunOnceCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) {
-  return std::move(std::get<k>(args)).Run(p0, p1, p2, p3, p4);
+  return std::move(::testing::get<k>(args)).Run(p0, p1, p2, p3, p4);
 }
 
 ACTION_TEMPLATE(RunOnceCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) {
-  return std::move(std::get<k>(args)).Run(p0, p1, p2, p3, p4, p5);
+  return std::move(::testing::get<k>(args)).Run(p0, p1, p2, p3, p4, p5);
 }
 
 ACTION_TEMPLATE(RunOnceCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) {
-  return std::move(std::get<k>(args)).Run(p0, p1, p2, p3, p4, p5, p6);
+  return std::move(::testing::get<k>(args)).Run(p0, p1, p2, p3, p4, p5, p6);
 }
 
-}  // namespace media
+}  // namespace test
+}  // namespace base
 
-#endif  // MEDIA_BASE_GMOCK_CALLBACK_SUPPORT_H_
+#endif  // BASE_TEST_GMOCK_CALLBACK_SUPPORT_H_
diff --git a/media/base/gmock_callback_support_unittest.cc b/base/test/gmock_callback_support_unittest.cc
similarity index 91%
rename from media/base/gmock_callback_support_unittest.cc
rename to base/test/gmock_callback_support_unittest.cc
index fb1beb98..82241a8 100644
--- a/media/base/gmock_callback_support_unittest.cc
+++ b/base/test/gmock_callback_support_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/base/gmock_callback_support.h"
+#include "base/test/gmock_callback_support.h"
 
 #include "base/bind.h"
 #include "base/callback.h"
@@ -12,7 +12,8 @@
 using testing::ByRef;
 using testing::MockFunction;
 
-namespace media {
+namespace base {
+namespace test {
 
 typedef base::Callback<void(const bool& src, bool* dst)> TestCallback;
 
@@ -35,8 +36,7 @@
 TEST(GmockCallbackSupportTest, RunClosure) {
   MockFunction<void(const base::Closure&)> check;
   bool dst = false;
-  EXPECT_CALL(check, Call(IsNotNullCallback()))
-      .WillOnce(RunClosure<0>());
+  EXPECT_CALL(check, Call(IsNotNullCallback())).WillOnce(RunClosure<0>());
   check.Call(base::Bind(&SetBool, true, &dst));
   EXPECT_TRUE(dst);
 }
@@ -81,4 +81,5 @@
   EXPECT_TRUE(dst);
 }
 
-}  // namespace media
+}  // namespace test
+}  // namespace base
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
index b093ea1..56b197c 100644
--- a/base/test/launcher/test_launcher.cc
+++ b/base/test/launcher/test_launcher.cc
@@ -589,6 +589,9 @@
   // Returns test name with PRE_ prefix added, excluding DISABLE_ prefix.
   std::string GetPreName() const;
 
+  // Returns test name excluding DISABLED_ and PRE_ prefixes.
+  std::string GetPrefixStrippedName() const;
+
   const std::string& test_case_name() const { return test_case_name_; }
   const std::string& test_name() const { return test_name_; }
   const std::string& file() const { return file_; }
@@ -636,6 +639,12 @@
   return FormatFullTestName(case_name, kPreTestPrefix + name);
 }
 
+std::string TestLauncher::TestInfo::GetPrefixStrippedName() const {
+  std::string test_name = GetDisabledStrippedName();
+  ReplaceSubstringsAfterOffset(&test_name, 0, kPreTestPrefix, std::string());
+  return test_name;
+}
+
 TestLauncherDelegate::~TestLauncherDelegate() = default;
 
 TestLauncher::LaunchOptions::LaunchOptions() = default;
@@ -656,8 +665,6 @@
       test_broken_count_(0),
       retry_limit_(0),
       force_run_broken_tests_(false),
-      shuffle_(false),
-      shuffle_seed_(0),
       watchdog_timer_(FROM_HERE,
                       kOutputTimeout,
                       this,
@@ -796,7 +803,12 @@
   if (result.status == TestResult::TEST_SUCCESS) {
     ++test_success_count_;
   } else {
-    tests_to_retry_.insert(result.full_name);
+    // Records prefix stripped name to run all dependent tests.
+    std::string test_name(result.full_name);
+    ReplaceSubstringsAfterOffset(&test_name, 0, kPreTestPrefix, std::string());
+    ReplaceSubstringsAfterOffset(&test_name, 0, kDisabledTestPrefix,
+                                 std::string());
+    tests_to_retry_.insert(test_name);
   }
 
   // There are no results for this tests,
@@ -998,38 +1010,6 @@
   force_run_broken_tests_ =
       command_line->HasSwitch(switches::kTestLauncherForceRunBrokenTests);
 
-  // Some of the TestLauncherDelegate implementations don't call into gtest
-  // until they've already split into test-specific processes. This results
-  // in gtest's native shuffle implementation attempting to shuffle one test.
-  // Shuffling the list of tests in the test launcher (before the delegate
-  // gets involved) ensures that the entire shard is shuffled.
-  if (command_line->HasSwitch(kGTestShuffleFlag)) {
-    shuffle_ = true;
-
-    if (command_line->HasSwitch(kGTestRandomSeedFlag)) {
-      const std::string custom_seed_str =
-          command_line->GetSwitchValueASCII(kGTestRandomSeedFlag);
-      uint32_t custom_seed = 0;
-      if (!StringToUint(custom_seed_str, &custom_seed)) {
-        LOG(ERROR) << "Unable to parse seed \"" << custom_seed_str << "\".";
-        return false;
-      }
-      if (custom_seed >= kRandomSeedUpperBound) {
-        LOG(ERROR) << "Seed " << custom_seed << " outside of expected range "
-                   << "[0, " << kRandomSeedUpperBound << ")";
-        return false;
-      }
-      shuffle_seed_ = custom_seed;
-    } else {
-      std::uniform_int_distribution<uint32_t> dist(0, kRandomSeedUpperBound);
-      std::random_device random_dev;
-      shuffle_seed_ = dist(random_dev);
-    }
-  } else if (command_line->HasSwitch(kGTestRandomSeedFlag)) {
-    LOG(ERROR) << kGTestRandomSeedFlag << " requires " << kGTestShuffleFlag;
-    return false;
-  }
-
   fprintf(stdout, "Using %zu parallel jobs.\n", parallel_jobs_);
   fflush(stdout);
 
@@ -1077,7 +1057,10 @@
   if (!InitTests())
     return false;
 
-  if (!ValidateTests())
+  if (!ShuffleTests(command_line))
+    return false;
+
+  if (!ReorderAndValidateTests())
     return false;
 
   if (command_line->HasSwitch(switches::kTestLauncherPrintTestStdio)) {
@@ -1207,19 +1190,56 @@
   return true;
 }
 
-bool TestLauncher::ValidateTests() {
+bool TestLauncher::ShuffleTests(CommandLine* command_line) {
+  if (command_line->HasSwitch(kGTestShuffleFlag)) {
+    uint32_t shuffle_seed;
+    if (command_line->HasSwitch(kGTestRandomSeedFlag)) {
+      const std::string custom_seed_str =
+          command_line->GetSwitchValueASCII(kGTestRandomSeedFlag);
+      uint32_t custom_seed = 0;
+      if (!StringToUint(custom_seed_str, &custom_seed)) {
+        LOG(ERROR) << "Unable to parse seed \"" << custom_seed_str << "\".";
+        return false;
+      }
+      if (custom_seed >= kRandomSeedUpperBound) {
+        LOG(ERROR) << "Seed " << custom_seed << " outside of expected range "
+                   << "[0, " << kRandomSeedUpperBound << ")";
+        return false;
+      }
+      shuffle_seed = custom_seed;
+    } else {
+      std::uniform_int_distribution<uint32_t> dist(0, kRandomSeedUpperBound);
+      std::random_device random_dev;
+      shuffle_seed = dist(random_dev);
+    }
+
+    std::mt19937 randomizer;
+    randomizer.seed(shuffle_seed);
+    std::shuffle(tests_.begin(), tests_.end(), randomizer);
+
+    fprintf(stdout, "Randomizing with seed %u\n", shuffle_seed);
+    fflush(stdout);
+  } else if (command_line->HasSwitch(kGTestRandomSeedFlag)) {
+    LOG(ERROR) << kGTestRandomSeedFlag << " requires " << kGTestShuffleFlag;
+    return false;
+  }
+  return true;
+}
+
+bool TestLauncher::ReorderAndValidateTests() {
   bool result = true;
   std::unordered_set<std::string> disabled_tests;
-  std::unordered_set<std::string> pre_tests;
+  std::unordered_map<std::string, TestInfo> pre_tests;
 
   // Find disabled and pre tests
   for (const TestInfo& test_info : tests_) {
     if (test_info.disabled())
       disabled_tests.insert(test_info.GetDisabledStrippedName());
     if (test_info.pre_test())
-      pre_tests.insert(test_info.GetDisabledStrippedName());
+      pre_tests[test_info.GetDisabledStrippedName()] = test_info;
   }
 
+  std::vector<TestInfo> tests_to_run;
   for (const TestInfo& test_info : tests_) {
     // If any test has a matching disabled test, fail and log for audit.
     if (base::ContainsKey(disabled_tests, test_info.GetFullName())) {
@@ -1227,13 +1247,26 @@
                  << " duplicated by a DISABLED_ test";
       result = false;
     }
-    // Remove pre tests that have a matching subsequent test.
-    pre_tests.erase(test_info.GetPreName());
-  }
 
-  // If any tests remain in pre_tests set, fail and log for audit.
-  for (const std::string& pre_test : pre_tests) {
-    LOG(ERROR) << pre_test << " is an orphaned pre test";
+    // Passes on PRE tests, those will append when final test is found.
+    if (test_info.pre_test())
+      continue;
+
+    std::vector<TestInfo> test_sequence;
+    test_sequence.push_back(test_info);
+    // Move Pre Tests prior to final test in order.
+    while (base::ContainsKey(pre_tests, test_sequence.back().GetPreName())) {
+      test_sequence.push_back(pre_tests[test_sequence.back().GetPreName()]);
+      pre_tests.erase(test_sequence.back().GetDisabledStrippedName());
+    }
+    tests_to_run.insert(tests_to_run.end(), test_sequence.rbegin(),
+                        test_sequence.rend());
+  }
+  tests_ = std::move(tests_to_run);
+
+  // If any tests remain in |pre_tests| map, fail and log for audit.
+  for (const auto& i : pre_tests) {
+    LOG(ERROR) << i.first << " is an orphaned pre test";
     result = false;
   }
   return result;
@@ -1296,14 +1329,14 @@
     // Count tests in the binary, before we apply filter and sharding.
     test_found_count++;
 
-    std::string test_name_no_disabled = test_info.GetDisabledStrippedName();
+    std::string prefix_stripped_name = test_info.GetPrefixStrippedName();
 
     // Skip the test that doesn't match the filter (if given).
     if (has_at_least_one_positive_filter_) {
       bool found = false;
       for (auto filter : positive_test_filter_) {
         if (MatchPattern(test_name, filter) ||
-            MatchPattern(test_name_no_disabled, filter)) {
+            MatchPattern(prefix_stripped_name, filter)) {
           found = true;
           break;
         }
@@ -1316,7 +1349,7 @@
       bool excluded = false;
       for (auto filter : negative_test_filter_) {
         if (MatchPattern(test_name, filter) ||
-            MatchPattern(test_name_no_disabled, filter)) {
+            MatchPattern(prefix_stripped_name, filter)) {
           excluded = true;
           break;
         }
@@ -1328,14 +1361,7 @@
 
     // Tests with the name XYZ will cause tests with the name PRE_XYZ to run. We
     // should bucket all of these tests together.
-    std::string test_name_to_bucket = test_name;
-    size_t index_of_first_period = test_name_to_bucket.find(".");
-    if (index_of_first_period == std::string::npos)
-      index_of_first_period = 0;
-    base::ReplaceSubstringsAfterOffset(
-        &test_name_to_bucket, index_of_first_period, "PRE_", std::string());
-
-    if (Hash(test_name_to_bucket) % total_shards_ !=
+    if (Hash(prefix_stripped_name) % total_shards_ !=
         static_cast<uint32_t>(shard_index_)) {
       continue;
     }
@@ -1344,25 +1370,13 @@
     // locations only for those tests that were run as part of this shard.
     results_tracker_.AddTestLocation(test_name, test_info.file(),
                                      test_info.line());
-
-    bool should_run_test = launcher_delegate_->ShouldRunTest(
-        test_info.test_case_name(), test_info.test_name());
-    if (should_run_test) {
+    if (!test_info.pre_test()) {
       // Only a subset of tests that are run require placeholders -- namely,
       // those that will output results.
       results_tracker_.AddTestPlaceholder(test_name);
-
-      test_names.push_back(test_name);
     }
-  }
 
-  if (shuffle_) {
-    std::mt19937 randomizer;
-    randomizer.seed(shuffle_seed_);
-    std::shuffle(test_names.begin(), test_names.end(), randomizer);
-
-    fprintf(stdout, "Randomizing with seed %u\n", shuffle_seed_);
-    fflush(stdout);
+    test_names.push_back(test_name);
   }
 
   // Save an early test summary in case the launcher crashes or gets killed.
@@ -1381,8 +1395,12 @@
   // Number of retries in this iteration.
   size_t retry_count = 0;
   while (!tests_to_retry_.empty() && retry_count < retry_limit_) {
-    std::vector<std::string> test_names(tests_to_retry_.begin(),
-                                        tests_to_retry_.end());
+    // Retry all tests that depend on a failing test.
+    std::vector<std::string> test_names;
+    for (const TestInfo& test_info : tests_) {
+      if (base::ContainsKey(tests_to_retry_, test_info.GetPrefixStrippedName()))
+        test_names.push_back(test_info.GetFullName());
+    }
     tests_to_retry_.clear();
 
     size_t retry_started_count =
diff --git a/base/test/launcher/test_launcher.h b/base/test/launcher/test_launcher.h
index 91ff42d..364b41c 100644
--- a/base/test/launcher/test_launcher.h
+++ b/base/test/launcher/test_launcher.h
@@ -57,17 +57,13 @@
   virtual bool WillRunTest(const std::string& test_case_name,
                            const std::string& test_name) = 0;
 
-  // Called before a test is considered for running. This method must return
-  // true if the TestLauncher is expected to run the test, provided it is part
-  // of the current shard.
-  virtual bool ShouldRunTest(const std::string& test_case_name,
-                             const std::string& test_name) = 0;
-
   // Called to make the delegate run the specified tests. The delegate must
   // return the number of actual tests it's going to run (can be smaller,
   // equal to, or larger than size of |test_names|). It must also call
   // |test_launcher|'s OnTestFinished method once per every run test,
   // regardless of its success.
+  // If test_names contains PRE_ chained tests, they must be properly ordered
+  // and consecutive.
   virtual size_t RunTests(TestLauncher* test_launcher,
                           const std::vector<std::string>& test_names) = 0;
 
@@ -178,10 +174,18 @@
   // Returns false if delegate fails to return tests.
   bool InitTests();
 
+  // Some of the TestLauncherDelegate implementations don't call into gtest
+  // until they've already split into test-specific processes. This results
+  // in gtest's native shuffle implementation attempting to shuffle one test.
+  // Shuffling the list of tests in the test launcher (before the delegate
+  // gets involved) ensures that the entire shard is shuffled.
+  bool ShuffleTests(CommandLine* command_line);
+
+  // Move all PRE_ tests prior to the final test in order.
   // Validate tests names. This includes no name conflict between tests
   // without DISABLED_ prefix, and orphaned PRE_ tests.
   // Returns false if any violation is found.
-  bool ValidateTests();
+  bool ReorderAndValidateTests();
 
   // Runs all tests in current iteration.
   void RunTests();
@@ -263,11 +267,7 @@
   bool force_run_broken_tests_;
 
   // Tests to retry in this iteration.
-  std::set<std::string> tests_to_retry_;
-
-  // Support for test shuffling, just like gtest does.
-  bool shuffle_;
-  uint32_t shuffle_seed_;
+  std::unordered_set<std::string> tests_to_retry_;
 
   TestResultsTracker results_tracker_;
 
diff --git a/base/test/launcher/test_launcher_unittest.cc b/base/test/launcher/test_launcher_unittest.cc
index 4f27a4eca..ea461b8 100644
--- a/base/test/launcher/test_launcher_unittest.cc
+++ b/base/test/launcher/test_launcher_unittest.cc
@@ -4,8 +4,12 @@
 
 #include <stddef.h>
 
+#include "base/base64.h"
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/json/json_reader.h"
 #include "base/test/launcher/test_launcher.h"
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/scoped_task_environment.h"
@@ -16,6 +20,33 @@
 namespace base {
 namespace {
 
+TestResult GenerateTestResult(
+    const std::string& test_name,
+    TestResult::Status status,
+    TimeDelta elapsed_td = TimeDelta::FromMilliseconds(30),
+    const std::string& output_snippet = "output") {
+  TestResult result;
+  result.full_name = test_name;
+  result.status = status;
+  result.elapsed_time = elapsed_td;
+  result.output_snippet = output_snippet;
+  return result;
+}
+
+TestResultPart GenerateTestResultPart(TestResultPart::Type type,
+                                      const std::string& file_name,
+                                      int line_number,
+                                      const std::string& summary,
+                                      const std::string& message) {
+  TestResultPart test_result_part;
+  test_result_part.type = type;
+  test_result_part.file_name = file_name;
+  test_result_part.line_number = line_number;
+  test_result_part.summary = summary;
+  test_result_part.message = message;
+  return test_result_part;
+}
+
 // Mock TestLauncher to mock CreateAndStartThreadPool,
 // unit test will provide a ScopedTaskEnvironment.
 class MockTestLauncher : public TestLauncher {
@@ -34,9 +65,6 @@
   MOCK_METHOD2(WillRunTest,
                bool(const std::string& test_case_name,
                     const std::string& test_name));
-  MOCK_METHOD2(ShouldRunTest,
-               bool(const std::string& test_case_name,
-                    const std::string& test_name));
   MOCK_METHOD2(RunTests,
                size_t(TestLauncher* test_launcher,
                       const std::vector<std::string>& test_names));
@@ -77,14 +105,22 @@
                                    testing::Return(true)));
     EXPECT_CALL(delegate, WillRunTest(_, _))
         .WillRepeatedly(testing::Return(true));
-    EXPECT_CALL(delegate, ShouldRunTest(_, _))
-        .WillRepeatedly(testing::Return(true));
+  }
+
+  void ReadSummary(FilePath path) {
+    File resultFile(path, File::FLAG_OPEN | File::FLAG_READ);
+    const int size = 2048;
+    std::string json;
+    ASSERT_TRUE(ReadFileToStringWithMaxSize(path, &json, size));
+    root = JSONReader::Read(json);
   }
 
   std::unique_ptr<CommandLine> command_line;
   MockTestLauncher test_launcher;
   MockTestLauncherDelegate delegate;
   base::test::ScopedTaskEnvironment scoped_task_environment;
+  ScopedTempDir dir;
+  Optional<Value> root;
 
  private:
   std::vector<TestIdentifier> tests_;
@@ -92,9 +128,14 @@
 
 // Action to mock delegate invoking OnTestFinish on test launcher.
 ACTION_P2(OnTestResult, full_name, status) {
-  TestResult result;
-  result.full_name = full_name;
-  result.status = status;
+  TestResult result = GenerateTestResult(full_name, status);
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      BindOnce(&TestLauncher::OnTestFinished, Unretained(arg0), result));
+}
+
+// Action to mock delegate invoking OnTestFinish on test launcher.
+ACTION_P(OnTestResult, result) {
   ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       BindOnce(&TestLauncher::OnTestFinished, Unretained(arg0), result));
@@ -148,6 +189,20 @@
   EXPECT_TRUE(test_launcher.Run(command_line.get()));
 }
 
+// Test TestLauncher should reorder PRE_ tests before delegate
+TEST_F(TestLauncherTest, ReorderPreTests) {
+  AddMockedTests("Test", {"firstTest", "PRE_PRE_firstTest", "PRE_firstTest"});
+  SetUpExpectCalls();
+  using ::testing::_;
+  std::vector<std::string> tests_names = {
+      "Test.PRE_PRE_firstTest", "Test.PRE_firstTest", "Test.firstTest"};
+  EXPECT_CALL(delegate,
+              RunTests(_, testing::ElementsAreArray(tests_names.cbegin(),
+                                                    tests_names.cend())))
+      .WillOnce(testing::Return(0));
+  EXPECT_TRUE(test_launcher.Run(command_line.get()));
+}
+
 // Test TestLauncher "gtest_filter" switch.
 TEST_F(TestLauncherTest, UsingCommandLineFilter) {
   AddMockedTests("Test",
@@ -165,6 +220,21 @@
   EXPECT_TRUE(test_launcher.Run(command_line.get()));
 }
 
+// Test TestLauncher gtest filter will include pre tests
+TEST_F(TestLauncherTest, FilterIncludePreTest) {
+  AddMockedTests("Test", {"firstTest", "secondTest", "PRE_firstTest"});
+  SetUpExpectCalls();
+  command_line->AppendSwitchASCII("gtest_filter", "Test.firstTest");
+  using ::testing::_;
+  std::vector<std::string> tests_names = {"Test.PRE_firstTest",
+                                          "Test.firstTest"};
+  EXPECT_CALL(delegate,
+              RunTests(_, testing::ElementsAreArray(tests_names.cbegin(),
+                                                    tests_names.cend())))
+      .WillOnce(testing::Return(0));
+  EXPECT_TRUE(test_launcher.Run(command_line.get()));
+}
+
 // Test TestLauncher "gtest_repeat" switch.
 TEST_F(TestLauncherTest, RunningMultipleIterations) {
   AddMockedTests("Test", {"firstTest"});
@@ -172,10 +242,8 @@
   command_line->AppendSwitchASCII("gtest_repeat", "2");
   using ::testing::_;
   EXPECT_CALL(delegate, RunTests(_, _))
-      .WillOnce(::testing::DoAll(
-          OnTestResult("Test.firstTest", TestResult::TEST_SUCCESS),
-          testing::Return(1)))
-      .WillOnce(::testing::DoAll(
+      .Times(2)
+      .WillRepeatedly(::testing::DoAll(
           OnTestResult("Test.firstTest", TestResult::TEST_SUCCESS),
           testing::Return(1)));
   EXPECT_TRUE(test_launcher.Run(command_line.get()));
@@ -221,6 +289,30 @@
   EXPECT_FALSE(test_launcher.Run(command_line.get()));
 }
 
+// Test TestLauncher should retry all PRE_ chained tests
+TEST_F(TestLauncherTest, RetryPreTests) {
+  AddMockedTests("Test", {"firstTest", "PRE_PRE_firstTest", "PRE_firstTest"});
+  SetUpExpectCalls();
+  using ::testing::_;
+  EXPECT_CALL(delegate, RunTests(_, _))
+      .WillOnce(::testing::DoAll(
+          OnTestResult("Test.PRE_PRE_firstTest", TestResult::TEST_SUCCESS),
+          OnTestResult("Test.PRE_firstTest", TestResult::TEST_FAILURE),
+          OnTestResult("Test.firstTest", TestResult::TEST_SUCCESS),
+          testing::Return(3)));
+  std::vector<std::string> tests_names = {
+      "Test.PRE_PRE_firstTest", "Test.PRE_firstTest", "Test.firstTest"};
+  EXPECT_CALL(delegate,
+              RetryTests(_, testing::ElementsAreArray(tests_names.cbegin(),
+                                                      tests_names.cend())))
+      .WillOnce(::testing::DoAll(
+          OnTestResult("Test.PRE_PRE_firstTest", TestResult::TEST_SUCCESS),
+          OnTestResult("Test.PRE_firstTest", TestResult::TEST_SUCCESS),
+          OnTestResult("Test.firstTest", TestResult::TEST_SUCCESS),
+          testing::Return(3)));
+  EXPECT_TRUE(test_launcher.Run(command_line.get()));
+}
+
 // Test TestLauncher run disabled unit tests switch.
 TEST_F(TestLauncherTest, RunDisabledTests) {
   AddMockedTests("DISABLED_TestDisabled", {"firstTest"});
@@ -268,6 +360,171 @@
   EXPECT_TRUE(test_launcher.Run(command_line.get()));
 }
 
+void ValidateKeyValue(Value* dict_value,
+                      const std::string& key,
+                      const std::string& expected_value) {
+  const std::string* value = dict_value->FindStringKey(key);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(expected_value, *value);
+}
+
+// Validate a json child node for a particular test result.
+void ValidateTestResult(Value* root, TestResult& result) {
+  Value* val = root->FindListKey(result.full_name);
+  ASSERT_TRUE(val);
+  ASSERT_EQ(1u, val->GetList().size());
+  val = &val->GetList().at(0);
+  ASSERT_TRUE(val->is_dict());
+
+  EXPECT_EQ(result.elapsed_time.InMilliseconds(),
+            val->FindIntKey("elapsed_time_ms").value_or(0));
+  EXPECT_EQ(true, val->FindBoolKey("losless_snippet").value_or(false));
+  ValidateKeyValue(val, "output_snippet", result.output_snippet);
+
+  std::string base64_output_snippet;
+  Base64Encode(result.output_snippet, &base64_output_snippet);
+  ValidateKeyValue(val, "output_snippet_base64", base64_output_snippet);
+  ValidateKeyValue(val, "status", result.StatusAsString());
+
+  Value* value = val->FindListKey("result_parts");
+  ASSERT_TRUE(value);
+  EXPECT_EQ(result.test_result_parts.size(), value->GetList().size());
+  for (unsigned i = 0; i < result.test_result_parts.size(); i++) {
+    TestResultPart result_part = result.test_result_parts.at(0);
+    Value* part_dict = &(value->GetList().at(i));
+    ASSERT_TRUE(part_dict);
+    ASSERT_TRUE(part_dict->is_dict());
+    ValidateKeyValue(part_dict, "type", result_part.TypeAsString());
+    ValidateKeyValue(part_dict, "file", result_part.file_name);
+    EXPECT_EQ(result_part.line_number,
+              part_dict->FindIntKey("line").value_or(0));
+    ValidateKeyValue(part_dict, "summary", result_part.summary);
+    ValidateKeyValue(part_dict, "message", result_part.message);
+  }
+}
+
+void ValidateStringList(Optional<Value>& root,
+                        const std::string& key,
+                        std::vector<const char*> values) {
+  Value* val = root->FindListKey(key);
+  ASSERT_TRUE(val);
+  ASSERT_EQ(values.size(), val->GetList().size());
+  for (unsigned i = 0; i < values.size(); i++) {
+    ASSERT_TRUE(val->GetList().at(i).is_string());
+    EXPECT_EQ(values.at(i), val->GetList().at(i).GetString());
+  }
+}
+
+void ValidateTestLocation(Value* root,
+                          const std::string& key,
+                          const std::string& file,
+                          int line) {
+  Value* val = root->FindDictKey(key);
+  ASSERT_TRUE(val);
+  EXPECT_EQ(2u, val->DictSize());
+  ValidateKeyValue(val, "file", file);
+  EXPECT_EQ(line, val->FindIntKey("line").value_or(0));
+}
+
+// Unit tests to validate TestLauncher outputs the correct JSON file.
+TEST_F(TestLauncherTest, JsonSummary) {
+  AddMockedTests("DISABLED_TestDisabled", {"firstTest"});
+  AddMockedTests("Test",
+                 {"firstTest", "secondTest", "DISABLED_firstTestDisabled"});
+  SetUpExpectCalls();
+
+  ASSERT_TRUE(dir.CreateUniqueTempDir());
+  FilePath path = dir.GetPath().AppendASCII("SaveSummaryResult.json");
+  command_line->AppendSwitchPath("test-launcher-summary-output", path);
+  command_line->AppendSwitchASCII("gtest_repeat", "2");
+
+  // Setup results to be returned by the test launcher delegate.
+  TestResult first_result =
+      GenerateTestResult("Test.firstTest", TestResult::TEST_SUCCESS,
+                         TimeDelta::FromMilliseconds(30), "output_first");
+  first_result.test_result_parts.push_back(GenerateTestResultPart(
+      TestResultPart::kSuccess, "TestFile", 110, "summary", "message"));
+  TestResult second_result =
+      GenerateTestResult("Test.secondTest", TestResult::TEST_SUCCESS,
+                         TimeDelta::FromMilliseconds(50), "output_second");
+
+  using ::testing::_;
+  EXPECT_CALL(delegate, RunTests(_, _))
+      .Times(2)
+      .WillRepeatedly(::testing::DoAll(OnTestResult(first_result),
+                                       OnTestResult(second_result),
+                                       testing::Return(2)));
+  EXPECT_TRUE(test_launcher.Run(command_line.get()));
+
+  // Validate the resulting JSON file is the expected output.
+  ReadSummary(path);
+  ValidateStringList(root, "all_tests",
+                     {"Test.firstTest", "Test.firstTestDisabled",
+                      "Test.secondTest", "TestDisabled.firstTest"});
+  ValidateStringList(root, "disabled_tests",
+                     {"Test.firstTestDisabled", "TestDisabled.firstTest"});
+
+  Value* val = root->FindDictKey("test_locations");
+  ASSERT_TRUE(val);
+  EXPECT_EQ(2u, val->DictSize());
+  ValidateTestLocation(val, "Test.firstTest", "File", 100);
+  ValidateTestLocation(val, "Test.secondTest", "File", 100);
+
+  val = root->FindListKey("per_iteration_data");
+  ASSERT_TRUE(val);
+  ASSERT_EQ(2u, val->GetList().size());
+  for (size_t i = 0; i < val->GetList().size(); i++) {
+    Value* iteration_val = &(val->GetList().at(i));
+    ASSERT_TRUE(iteration_val);
+    ASSERT_TRUE(iteration_val->is_dict());
+    EXPECT_EQ(2u, iteration_val->DictSize());
+    ValidateTestResult(iteration_val, first_result);
+    ValidateTestResult(iteration_val, second_result);
+  }
+}
+
+// Validate TestLauncher outputs the correct JSON file
+// when running disabled tests.
+TEST_F(TestLauncherTest, JsonSummaryWithDisabledTests) {
+  AddMockedTests("Test", {"DISABLED_Test"});
+  SetUpExpectCalls();
+
+  ASSERT_TRUE(dir.CreateUniqueTempDir());
+  FilePath path = dir.GetPath().AppendASCII("SaveSummaryResult.json");
+  command_line->AppendSwitchPath("test-launcher-summary-output", path);
+  command_line->AppendSwitch("gtest_also_run_disabled_tests");
+
+  // Setup results to be returned by the test launcher delegate.
+  TestResult test_result =
+      GenerateTestResult("Test.DISABLED_Test", TestResult::TEST_SUCCESS,
+                         TimeDelta::FromMilliseconds(50), "output_second");
+
+  using ::testing::_;
+  EXPECT_CALL(delegate, RunTests(_, _))
+      .WillOnce(
+          ::testing::DoAll(OnTestResult(test_result), testing::Return(1)));
+  EXPECT_TRUE(test_launcher.Run(command_line.get()));
+
+  // Validate the resulting JSON file is the expected output.
+  ReadSummary(path);
+  Value* val = root->FindDictKey("test_locations");
+  ASSERT_TRUE(val);
+  EXPECT_EQ(1u, val->DictSize());
+  ValidateTestLocation(val, "Test.DISABLED_Test", "File", 100);
+
+  val = root->FindListKey("per_iteration_data");
+  ASSERT_TRUE(val);
+  ASSERT_EQ(1u, val->GetList().size());
+
+  Value* iteration_val = &(val->GetList().at(0));
+  ASSERT_TRUE(iteration_val);
+  ASSERT_TRUE(iteration_val->is_dict());
+  EXPECT_EQ(1u, iteration_val->DictSize());
+  // We expect the result to be stripped of disabled prefix.
+  test_result.full_name = "Test.Test";
+  ValidateTestResult(iteration_val, test_result);
+}
+
 }  // namespace
 
 }  // namespace base
diff --git a/base/test/launcher/test_results_tracker.cc b/base/test/launcher/test_results_tracker.cc
index f1ee0d8..da153181 100644
--- a/base/test/launcher/test_results_tracker.cc
+++ b/base/test/launcher/test_results_tracker.cc
@@ -276,10 +276,6 @@
   for (auto& full_test_name : test_placeholders_) {
     std::string test_name = TestNameWithoutDisabledPrefix(full_test_name);
 
-    // Ignore disabled tests.
-    if (disabled_tests_.find(test_name) != disabled_tests_.end())
-      continue;
-
     TestResult test_result;
     test_result.full_name = test_name;
     test_result.status = TestResult::TEST_NOT_RUN;
diff --git a/base/test/launcher/test_results_tracker_unittest.cc b/base/test/launcher/test_results_tracker_unittest.cc
deleted file mode 100644
index 0dad2e5..0000000
--- a/base/test/launcher/test_results_tracker_unittest.cc
+++ /dev/null
@@ -1,305 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stddef.h>
-
-#include "base/base64.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/json/json_reader.h"
-#include "base/logging.h"
-#include "base/test/launcher/test_result.h"
-#include "base/test/launcher/test_results_tracker.h"
-#include "base/values.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-namespace {
-
-// Unit tests to validate TestResultsTracker outputs the correct JSON file
-// given the correct setup.
-class TestResultsTrackerTester : public testing::Test {
- protected:
-  void ValidateKeyValue(Value* dict_value,
-                        const std::string& key,
-                        int64_t expected_value) {
-    base::Optional<int> value = dict_value->FindIntKey(key);
-    ASSERT_TRUE(value);
-    EXPECT_EQ(expected_value, value.value());
-  }
-
-  void ValidateKeyValue(Value* dict_value,
-                        const std::string& key,
-                        int expected_value) {
-    base::Optional<int> value = dict_value->FindIntKey(key);
-    ASSERT_TRUE(value);
-    EXPECT_EQ(expected_value, value.value());
-  }
-
-  void ValidateKeyValue(Value* dict_value,
-                        const std::string& key,
-                        bool expected_value) {
-    base::Optional<bool> value = dict_value->FindBoolKey(key);
-    ASSERT_TRUE(value);
-    EXPECT_EQ(expected_value, value.value());
-  }
-
-  void ValidateKeyValue(Value* dict_value,
-                        const std::string& key,
-                        const std::string& expected_value) {
-    const std::string* value = dict_value->FindStringKey(key);
-    ASSERT_TRUE(value);
-    EXPECT_EQ(expected_value, *value);
-  }
-
-  // Validate a json child node for a particular test result.
-  void ValidateTestResult(Value* root, TestResult& result) {
-    Value* val = root->FindListKey(result.full_name);
-    ASSERT_TRUE(val);
-    ASSERT_EQ(1u, val->GetList().size());
-    val = &val->GetList().at(0);
-    ASSERT_TRUE(val->is_dict());
-
-    ValidateKeyValue(val, "elapsed_time_ms",
-                     result.elapsed_time.InMilliseconds());
-    ValidateKeyValue(val, "losless_snippet", true);
-    ValidateKeyValue(val, "output_snippet", result.output_snippet);
-
-    std::string base64_output_snippet;
-    Base64Encode(result.output_snippet, &base64_output_snippet);
-    ValidateKeyValue(val, "output_snippet_base64", base64_output_snippet);
-    ValidateKeyValue(val, "status", result.StatusAsString());
-
-    Value* value = val->FindListKey("result_parts");
-    ASSERT_TRUE(value);
-    EXPECT_EQ(result.test_result_parts.size(), value->GetList().size());
-    for (unsigned i = 0; i < result.test_result_parts.size(); i++) {
-      TestResultPart result_part = result.test_result_parts.at(0);
-      Value* part_dict = &(value->GetList().at(i));
-      ASSERT_TRUE(part_dict);
-      ASSERT_TRUE(part_dict->is_dict());
-      ValidateKeyValue(part_dict, "type", result_part.TypeAsString());
-      ValidateKeyValue(part_dict, "file", result_part.file_name);
-      ValidateKeyValue(part_dict, "line", result_part.line_number);
-      ValidateKeyValue(part_dict, "summary", result_part.summary);
-      ValidateKeyValue(part_dict, "message", result_part.message);
-    }
-  }
-
-  void ValidateStringList(Optional<Value>& root,
-                          const std::string& key,
-                          std::vector<const char*> values) {
-    Value* val = root->FindListKey(key);
-    ASSERT_TRUE(val);
-    ASSERT_EQ(values.size(), val->GetList().size());
-    for (unsigned i = 0; i < values.size(); i++) {
-      ASSERT_TRUE(val->GetList().at(i).is_string());
-      EXPECT_EQ(values.at(i), val->GetList().at(i).GetString());
-    }
-  }
-
-  void ValidateTestLocation(Value* root,
-                            const std::string& key,
-                            const std::string& file,
-                            int line) {
-    Value* val = root->FindDictKey(key);
-    ASSERT_TRUE(val);
-    EXPECT_EQ(2u, val->DictSize());
-    ValidateKeyValue(val, "file", file);
-    ValidateKeyValue(val, "line", line);
-  }
-
-  TestResult GenerateTestResult(const std::string& test_name,
-                                TestResult::Status status,
-                                TimeDelta elapsed_td,
-                                const std::string& output_snippet) {
-    TestResult result;
-    result.full_name = test_name;
-    result.status = status;
-    result.elapsed_time = elapsed_td;
-    result.output_snippet = output_snippet;
-    return result;
-  }
-
-  TestResultPart GenerateTestResultPart(TestResultPart::Type type,
-                                        const std::string& file_name,
-                                        int line_number,
-                                        const std::string& summary,
-                                        const std::string& message) {
-    TestResultPart test_result_part;
-    test_result_part.type = type;
-    test_result_part.file_name = file_name;
-    test_result_part.line_number = line_number;
-    test_result_part.summary = summary;
-    test_result_part.message = message;
-    return test_result_part;
-  }
-
-  Optional<Value> SaveAndReadSummary() {
-    ScopedTempDir dir;
-    CHECK(dir.CreateUniqueTempDir());
-    FilePath path = dir.GetPath().AppendASCII("SaveSummaryResult.json");
-    CHECK(tracker.SaveSummaryAsJSON(path, std::vector<std::string>()));
-    File resultFile(path, File::FLAG_OPEN | File::FLAG_READ);
-    const int size = 2048;
-    std::string json;
-    CHECK(ReadFileToStringWithMaxSize(path, &json, size));
-    return JSONReader::Read(json);
-  }
-
-  TestResultsTracker tracker;
-};
-
-// Validate JSON result file is saved with the correct structure.
-TEST_F(TestResultsTrackerTester, JsonSummaryEmptyResult) {
-  tracker.OnTestIterationStarting();
-
-  Optional<Value> root = SaveAndReadSummary();
-
-  ASSERT_TRUE(root);
-  ASSERT_TRUE(root->is_dict());
-  EXPECT_EQ(5u, root->DictSize());
-}
-
-// Validate global tags are saved correctly.
-TEST_F(TestResultsTrackerTester, JsonSummaryRootTags) {
-  tracker.OnTestIterationStarting();
-  tracker.AddTest("Test2");  // Test should appear in alphabetical order.
-  tracker.AddTest("Test1");
-  tracker.AddTest("Test1");  // Test should only appear once.
-  // DISABLED_ prefix should be removed.
-  tracker.AddTest("DISABLED_Test3");
-  tracker.AddDisabledTest("DISABLED_Test3");
-  tracker.AddGlobalTag("global1");
-
-  Optional<Value> root = SaveAndReadSummary();
-
-  ValidateStringList(root, "global_tags", {"global1"});
-  ValidateStringList(root, "all_tests", {"Test1", "Test2", "Test3"});
-  ValidateStringList(root, "disabled_tests", {"Test3"});
-}
-
-// Validate test locations are saved correctly.
-TEST_F(TestResultsTrackerTester, JsonSummaryTestLocation) {
-  tracker.OnTestIterationStarting();
-  tracker.AddTestLocation("Test1", "Test1File", 100);
-  tracker.AddTestLocation("Test2", "Test2File", 200);
-
-  Optional<Value> root = SaveAndReadSummary();
-
-  Value* val = root->FindDictKey("test_locations");
-
-  ASSERT_TRUE(val);
-  ASSERT_TRUE(val->is_dict());
-  EXPECT_EQ(2u, val->DictSize());
-
-  ValidateTestLocation(val, "Test1", "Test1File", 100);
-  ValidateTestLocation(val, "Test2", "Test2File", 200);
-}
-
-// Validate test results are saved correctly.
-TEST_F(TestResultsTrackerTester, JsonSummaryTestResults) {
-  TestResult test_result =
-      GenerateTestResult("Test", TestResult::TEST_SUCCESS,
-                         TimeDelta::FromMilliseconds(30), "output");
-  test_result.test_result_parts.push_back(GenerateTestResultPart(
-      TestResultPart::kSuccess, "TestFile", 110, "summary", "message"));
-  tracker.AddTestPlaceholder("Test");
-
-  tracker.OnTestIterationStarting();
-  tracker.GeneratePlaceholderIteration();
-  tracker.AddTestResult(test_result);
-
-  Optional<Value> root = SaveAndReadSummary();
-
-  Value* val = root->FindListKey("per_iteration_data");
-  ASSERT_TRUE(val);
-  ASSERT_EQ(1u, val->GetList().size());
-
-  Value* iteration_val = &(val->GetList().at(0));
-  ASSERT_TRUE(iteration_val);
-  ASSERT_TRUE(iteration_val->is_dict());
-  EXPECT_EQ(1u, iteration_val->DictSize());
-  ValidateTestResult(iteration_val, test_result);
-}
-
-// Validate test results without a placeholder.
-TEST_F(TestResultsTrackerTester, JsonSummaryTestWithoutPlaceholder) {
-  TestResult test_result =
-      GenerateTestResult("Test", TestResult::TEST_SUCCESS,
-                         TimeDelta::FromMilliseconds(30), "output");
-
-  tracker.OnTestIterationStarting();
-  tracker.GeneratePlaceholderIteration();
-  tracker.AddTestResult(test_result);
-
-  Optional<Value> root = SaveAndReadSummary();
-
-  Value* val = root->FindListKey("per_iteration_data");
-  ASSERT_TRUE(val);
-  ASSERT_EQ(1u, val->GetList().size());
-
-  Value* iteration_val = &(val->GetList().at(0));
-  // No result is saved since a placeholder was not specified.
-  EXPECT_EQ(0u, iteration_val->DictSize());
-}
-
-// Validate test results are saved correctly based on setup.
-TEST_F(TestResultsTrackerTester, JsonSummaryTestPlaceholderOrder) {
-  TestResult test_result =
-      GenerateTestResult("Test", TestResult::TEST_SUCCESS,
-                         TimeDelta::FromMilliseconds(30), "output");
-  TestResult test_result_empty = GenerateTestResult(
-      "Test", TestResult::TEST_NOT_RUN, TimeDelta::FromMilliseconds(0), "");
-
-  tracker.AddTestPlaceholder("Test");
-  // Test added before GeneratePlaceholderIteration, will not record.
-  tracker.OnTestIterationStarting();
-  tracker.AddTestResult(test_result);
-  tracker.GeneratePlaceholderIteration();
-  // This is the correct order of operations.
-  tracker.OnTestIterationStarting();
-  tracker.GeneratePlaceholderIteration();
-  tracker.AddTestResult(test_result);
-
-  Optional<Value> root = SaveAndReadSummary();
-  Value* val = root->FindListKey("per_iteration_data");
-  ASSERT_TRUE(val);
-  ASSERT_EQ(2u, val->GetList().size());
-
-  Value* iteration_val = &(val->GetList().at(0));
-  ValidateTestResult(iteration_val, test_result_empty);
-
-  iteration_val = &(val->GetList().at(1));
-  ValidateTestResult(iteration_val, test_result);
-}
-
-// Disabled tests results are not saved in json summary.
-TEST_F(TestResultsTrackerTester, JsonSummaryDisabledTestResults) {
-  tracker.AddDisabledTest("Test");
-  TestResult test_result =
-      GenerateTestResult("Test", TestResult::TEST_SUCCESS,
-                         TimeDelta::FromMilliseconds(30), "output");
-  test_result.test_result_parts.push_back(GenerateTestResultPart(
-      TestResultPart::kSuccess, "TestFile", 110, "summary", "message"));
-  tracker.AddTestPlaceholder("Test");
-
-  tracker.OnTestIterationStarting();
-  tracker.GeneratePlaceholderIteration();
-  tracker.AddTestResult(test_result);
-
-  Optional<Value> root = SaveAndReadSummary();
-
-  Value* val = root->FindListKey("per_iteration_data");
-  ASSERT_TRUE(val);
-  ASSERT_EQ(1u, val->GetList().size());
-
-  Value* iteration_val = &(val->GetList().at(0));
-  // No result is saved since a placeholder was not specified.
-  EXPECT_EQ(0u, iteration_val->DictSize());
-}
-
-}  // namespace
-
-}  // namespace base
diff --git a/base/test/launcher/unit_test_launcher.cc b/base/test/launcher/unit_test_launcher.cc
index b965736..cfd517b3 100644
--- a/base/test/launcher/unit_test_launcher.cc
+++ b/base/test/launcher/unit_test_launcher.cc
@@ -676,14 +676,6 @@
   return true;
 }
 
-bool UnitTestLauncherDelegate::ShouldRunTest(const std::string& test_case_name,
-                                             const std::string& test_name) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  // There is no additional logic to disable specific tests.
-  return true;
-}
-
 size_t UnitTestLauncherDelegate::RunTests(
     TestLauncher* test_launcher,
     const std::vector<std::string>& test_names) {
diff --git a/base/test/launcher/unit_test_launcher.h b/base/test/launcher/unit_test_launcher.h
index 595923ac..44c1e0a 100644
--- a/base/test/launcher/unit_test_launcher.h
+++ b/base/test/launcher/unit_test_launcher.h
@@ -136,8 +136,6 @@
   bool GetTests(std::vector<TestIdentifier>* output) override;
   bool WillRunTest(const std::string& test_case_name,
                    const std::string& test_name) override;
-  bool ShouldRunTest(const std::string& test_case_name,
-                     const std::string& test_name) override;
   size_t RunTests(TestLauncher* test_launcher,
                   const std::vector<std::string>& test_names) override;
   size_t RetryTests(TestLauncher* test_launcher,
diff --git a/base/test/scoped_task_environment.cc b/base/test/scoped_task_environment.cc
index 0c14e1ac..ee46b4e 100644
--- a/base/test/scoped_task_environment.cc
+++ b/base/test/scoped_task_environment.cc
@@ -149,13 +149,13 @@
   // sequence_manager::TimeDomain:
 
   sequence_manager::LazyNow CreateLazyNow() const override {
-    base::AutoLock lock(now_ticks_lock_);
+    AutoLock lock(now_ticks_lock_);
     return sequence_manager::LazyNow(now_ticks_);
   }
 
   TimeTicks Now() const override {
     // This can be called from any thread.
-    base::AutoLock lock(now_ticks_lock_);
+    AutoLock lock(now_ticks_lock_);
     return now_ticks_;
   }
 
@@ -177,50 +177,22 @@
 
     // The next task is a future delayed task. Since we're using mock time, we
     // don't want an actual OS level delayed wake up scheduled, so pretend we
-    // have no more work. This will result in MaybeFastForwardToNextTask getting
-    // called which lets us advance |now_ticks_|.
+    // have no more work. This will result in appearing idle,
+    // ScopedTaskEnvironment will decide what to do based on that (return to
+    // caller or fast-forward time).
     return base::nullopt;
   }
 
   // This method is called when the underlying message pump has run out of
-  // non-delayed work.
+  // non-delayed work. Advances time to the next task unless
+  // |quit_when_idle_requested| or ScopedTaskEnvironment controls mock time.
   bool MaybeFastForwardToNextTask(bool quit_when_idle_requested) override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    // If we're being externally controlled by a RunLoop in client code, check
-    // if the RunLoop is due to quit when idle, if so we don't want to advance
-    // mock time.
-    if (stop_when_message_pump_is_idle_ && quit_when_idle_requested)
+    if (quit_when_idle_requested)
+      return false;
+    if (!auto_advance_on_idle_)
       return false;
 
-    // We don't need to call ReclaimMemory here because
-    // DelayTillNextTask will have dealt with cancelled delayed tasks for us.
-    Optional<TimeTicks> run_time = NextScheduledRunTime();
-    // If an immediate task came in racily from another thread, resume work
-    // without advancing time. This can happen regardless of whether the main
-    // thread has more delayed tasks scheduled before |allow_advance_until_|. If
-    // there are such tasks, auto-advancing time all the way would be incorrect.
-    // In both cases, resuming is fine.
-    if (run_time == now_ticks_)
-      return true;
-
-    if (!run_time) {
-      // We've run out of tasks. ScopedTaskEnvironment::FastForwardBy requires
-      // the remaining virtual time to be consumed upon reaching idle.
-      if (now_ticks_ < allow_advance_until_ && !allow_advance_until_.is_max())
-        SetTime(allow_advance_until_);
-      return false;
-    }
-
-    // Don't advance past |allow_advance_until_|.
-    DCHECK_GT(*run_time, now_ticks_);
-    TimeTicks time_to_advance_to = std::min(allow_advance_until_, *run_time);
-    if (time_to_advance_to <= now_ticks_)
-      return false;
-
-    SetTime(time_to_advance_to);
-
-    // Make sure a DoWork is scheduled.
-    return true;
+    return FastForwardToNextTaskOrCap(TimeTicks::Max());
   }
 
   const char* GetName() const override { return "MockTimeDomain"; }
@@ -228,44 +200,51 @@
   // TickClock implementation:
   TimeTicks NowTicks() const override { return Now(); }
 
-  // Allows time to advance when reaching idle, until
-  // |now_ticks_ == advance_until|. No-op if |advance_until <= now_ticks_|.
-  // Doesn't schedule work by itself.
-  void SetAllowTimeToAutoAdvanceUntil(TimeTicks advance_until) {
+  // Advances time to the next task or to |fast_forward_cap| (if it's not
+  // Max()). Returns true if there's additional immediate work as a result
+  // of this call.
+  bool FastForwardToNextTaskOrCap(TimeTicks fast_forward_cap) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    allow_advance_until_ = advance_until;
+
+    // We don't need to call ReclaimMemory here because
+    // DelayTillNextTask will have dealt with cancelled delayed tasks for us.
+    Optional<TimeTicks> next_task_time = NextScheduledRunTime();
+
+    if (next_task_time && *next_task_time <= fast_forward_cap) {
+      AutoLock lock(now_ticks_lock_);
+      now_ticks_ = *next_task_time;
+      return true;
+    }
+
+    if (!fast_forward_cap.is_max()) {
+      AutoLock lock(now_ticks_lock_);
+      now_ticks_ = fast_forward_cap;
+    }
+
+    return false;
   }
 
-  void SetStopWhenMessagePumpIsIdle(bool stop_when_message_pump_is_idle) {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    stop_when_message_pump_is_idle_ = stop_when_message_pump_is_idle;
+  void set_auto_advance_on_idle(bool auto_advance_on_idle) {
+    auto_advance_on_idle_ = auto_advance_on_idle;
   }
 
  private:
-  void SetTime(TimeTicks time) {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    DCHECK_LE(time, allow_advance_until_);
-
-    base::AutoLock lock(now_ticks_lock_);
-    now_ticks_ = time;
-  }
-
   SEQUENCE_CHECKER(sequence_checker_);
 
+  // Whether MaybeFastForwardToNextTask() should auto advance time. Set to true
+  // by default so RunLoop::Run() auto-advances time. Set to false when
+  // ScopedTaskEnvironment controls mock time (i.e. in FastForwardBy()).
+  bool auto_advance_on_idle_ = true;
+
   sequence_manager::SequenceManager* const sequence_manager_;
 
   std::unique_ptr<subtle::ScopedTimeClockOverrides> time_overrides_;
 
-  // By default we want RunLoop.Run() to advance virtual time due to the API
-  // contract.
-  TimeTicks allow_advance_until_ = TimeTicks::Max();
-  bool stop_when_message_pump_is_idle_ = true;
-
   // Protects |now_ticks_|
   mutable Lock now_ticks_lock_;
 
   // Only ever written to from the main sequence.
-  // TODO(alexclarke): We can't override now by default with now_ticks_ staring
+  // TODO(alexclarke): We can't override now by default with now_ticks_ starting
   // from zero because many tests have checks that TimeTicks::Now() returns a
   // non-zero result.
   TimeTicks now_ticks_;
@@ -460,11 +439,6 @@
                            : sequence_manager_->GetRealTimeDomain();
 }
 
-void ScopedTaskEnvironment::SetAllowTimeToAutoAdvanceUntilForTesting(
-    TimeTicks advance_until) {
-  mock_time_domain_->SetAllowTimeToAutoAdvanceUntil(advance_until);
-}
-
 sequence_manager::SequenceManager* ScopedTaskEnvironment::sequence_manager()
     const {
   DCHECK(subclass_creates_default_taskrunner_);
@@ -506,10 +480,6 @@
 }
 
 void ScopedTaskEnvironment::RunUntilIdle() {
-  // Prevent virtual time from advancing while within this call.
-  if (mock_time_domain_)
-    mock_time_domain_->SetAllowTimeToAutoAdvanceUntil(TimeTicks());
-
   // TODO(gab): This can be heavily simplified to essentially:
   //     bool HasMainThreadTasks() {
   //      if (message_loop_)
@@ -547,7 +517,7 @@
     // ThreadPool task synchronously block on a main thread task
     // (ThreadPoolInstance::FlushForTesting() can't be used here for that
     // reason).
-    RunLoop().RunUntilIdle();
+    RunLoop(RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
 
     // Then halt ThreadPool. DisallowRunTasks() failing indicates that there
     // were ThreadPool tasks currently running. In that case, try again from
@@ -562,7 +532,7 @@
     // Note: this assumes that no main thread task synchronously blocks on a
     // ThreadPool tasks (it certainly shouldn't); this call could otherwise
     // hang.
-    RunLoop().RunUntilIdle();
+    RunLoop(RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
 
     // The above RunUntilIdle() guarantees there are no remaining main thread
     // tasks (the ThreadPool being halted during the last RunUntilIdle() is
@@ -589,19 +559,18 @@
   // ThreadPoolExecutionMode::QUEUED.
   if (thread_pool_execution_mode_ != ThreadPoolExecutionMode::QUEUED)
     task_tracker_->AllowRunTasks();
-
-  if (mock_time_domain_)
-    mock_time_domain_->SetAllowTimeToAutoAdvanceUntil(TimeTicks::Max());
 }
 
 void ScopedTaskEnvironment::FastForwardBy(TimeDelta delta) {
   DCHECK(mock_time_domain_);
-  mock_time_domain_->SetStopWhenMessagePumpIsIdle(false);
-  mock_time_domain_->SetAllowTimeToAutoAdvanceUntil(mock_time_domain_->Now() +
-                                                    delta);
-  RunLoop{RunLoop::Type::kNestableTasksAllowed}.RunUntilIdle();
-  mock_time_domain_->SetStopWhenMessagePumpIsIdle(true);
-  mock_time_domain_->SetAllowTimeToAutoAdvanceUntil(TimeTicks::Max());
+  mock_time_domain_->set_auto_advance_on_idle(false);
+
+  const TimeTicks fast_forward_until = mock_time_domain_->NowTicks() + delta;
+  do {
+    RunUntilIdle();
+  } while (mock_time_domain_->FastForwardToNextTaskOrCap(fast_forward_until));
+
+  mock_time_domain_->set_auto_advance_on_idle(true);
 }
 
 void ScopedTaskEnvironment::FastForwardUntilNoTasksRemain() {
diff --git a/base/test/scoped_task_environment.h b/base/test/scoped_task_environment.h
index 4e5103df..a7d3363 100644
--- a/base/test/scoped_task_environment.h
+++ b/base/test/scoped_task_environment.h
@@ -261,9 +261,6 @@
   // Returns the TimeDomain driving this ScopedTaskEnvironment.
   sequence_manager::TimeDomain* GetTimeDomain() const;
 
-  // For testing the MockTimeDomain.
-  void SetAllowTimeToAutoAdvanceUntilForTesting(TimeTicks advance_until);
-
   sequence_manager::SequenceManager* sequence_manager() const;
 
   void DeferredInitFromSubclass(
@@ -291,7 +288,7 @@
                         trait_helpers::NotATraitTag tag);
 
   const MainThreadType main_thread_type_;
-  const ThreadPoolExecutionMode thread_pool_execution_mode_;
+  ThreadPoolExecutionMode thread_pool_execution_mode_;
   const bool subclass_creates_default_taskrunner_;
 
   std::unique_ptr<sequence_manager::SequenceManager> sequence_manager_;
diff --git a/base/test/scoped_task_environment_unittest.cc b/base/test/scoped_task_environment_unittest.cc
index 16aed69d..cb6d5f39 100644
--- a/base/test/scoped_task_environment_unittest.cc
+++ b/base/test/scoped_task_environment_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "base/test/scoped_task_environment.h"
 
+#include <atomic>
 #include <memory>
 
 #include "base/atomicops.h"
@@ -50,20 +51,6 @@
 using ::testing::Not;
 using ::testing::Return;
 
-class ScopedTaskEnvironmentForTest : public ScopedTaskEnvironment {
- public:
-  template <
-      class... ArgTypes,
-      class CheckArgumentsAreValid = std::enable_if_t<
-          base::trait_helpers::AreValidTraits<ScopedTaskEnvironment::ValidTrait,
-                                              ArgTypes...>::value>>
-  NOINLINE ScopedTaskEnvironmentForTest(const ArgTypes... args)
-      : ScopedTaskEnvironment(args...) {}
-
-  using ScopedTaskEnvironment::GetTimeDomain;
-  using ScopedTaskEnvironment::SetAllowTimeToAutoAdvanceUntilForTesting;
-};
-
 class ScopedTaskEnvironmentTest
     : public testing::TestWithParam<ScopedTaskEnvironment::MainThreadType> {};
 
@@ -156,11 +143,8 @@
   ScopedTaskEnvironment scoped_task_environment(
       GetParam(), ScopedTaskEnvironment::ThreadPoolExecutionMode::ASYNC);
 
-  WaitableEvent task_ran(WaitableEvent::ResetPolicy::MANUAL,
-                         WaitableEvent::InitialState::NOT_SIGNALED);
-  PostTask(FROM_HERE,
-           BindOnce([](WaitableEvent* task_ran) { task_ran->Signal(); },
-                    Unretained(&task_ran)));
+  WaitableEvent task_ran;
+  PostTask(FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&task_ran)));
   task_ran.Wait();
 }
 
@@ -174,11 +158,8 @@
 
   scoped_task_environment.RunUntilIdle();
 
-  WaitableEvent task_ran(WaitableEvent::ResetPolicy::MANUAL,
-                         WaitableEvent::InitialState::NOT_SIGNALED);
-  PostTask(FROM_HERE,
-           BindOnce([](WaitableEvent* task_ran) { task_ran->Signal(); },
-                    Unretained(&task_ran)));
+  WaitableEvent task_ran;
+  PostTask(FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&task_ran)));
   task_ran.Wait();
 }
 
@@ -284,7 +265,7 @@
 }
 
 TEST_P(ScopedTaskEnvironmentTest, SingleThreadShouldNotInitializeThreadPool) {
-  ScopedTaskEnvironmentForTest scoped_task_environment(
+  ScopedTaskEnvironment scoped_task_environment(
       ScopedTaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY);
   EXPECT_THAT(ThreadPoolInstance::Get(), IsNull());
 }
@@ -404,145 +385,97 @@
   EXPECT_EQ(start_time + kDelay, base::TimeTicks::Now());
 }
 
-TEST_F(ScopedTaskEnvironmentTest, MockTimeDomain_MaybeFastForwardToNextTask) {
+// Verify that FastForwardBy() runs existing immediate tasks before advancing,
+// then advances to the next delayed task, runs it, then advances the remainder
+// of time when out of tasks.
+TEST_F(ScopedTaskEnvironmentTest, FastForwardOnlyAdvancesWhenIdle) {
+  ScopedTaskEnvironment scoped_task_environment(
+      ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
+      ScopedTaskEnvironment::NowSource::MAIN_THREAD_MOCK_TIME);
+
+  const TimeTicks start_time = base::TimeTicks::Now();
+
   constexpr base::TimeDelta kDelay = TimeDelta::FromSeconds(42);
-  ScopedTaskEnvironmentForTest scoped_task_environment(
-      ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
-      ScopedTaskEnvironment::NowSource::MAIN_THREAD_MOCK_TIME);
-  const TimeTicks start_time = base::TimeTicks::Now();
-  EXPECT_FALSE(
-      scoped_task_environment.GetTimeDomain()->MaybeFastForwardToNextTask(
-          false));
-  EXPECT_EQ(start_time, base::TimeTicks::Now());
-
-  ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, base::DoNothing(),
-                                                 kDelay);
-  EXPECT_TRUE(
-      scoped_task_environment.GetTimeDomain()->MaybeFastForwardToNextTask(
-          false));
-  EXPECT_EQ(start_time + kDelay, base::TimeTicks::Now());
+  constexpr base::TimeDelta kFastForwardUntil = TimeDelta::FromSeconds(100);
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindLambdaForTesting(
+                     [&]() { EXPECT_EQ(start_time, base::TimeTicks::Now()); }));
+  ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, BindLambdaForTesting([&]() {
+        EXPECT_EQ(start_time + kDelay, base::TimeTicks::Now());
+      }),
+      kDelay);
+  scoped_task_environment.FastForwardBy(kFastForwardUntil);
+  EXPECT_EQ(start_time + kFastForwardUntil, base::TimeTicks::Now());
 }
 
-TEST_F(ScopedTaskEnvironmentTest,
-       MockTimeDomain_MaybeFastForwardToNextTask_ImmediateTaskPending) {
-  ScopedTaskEnvironmentForTest scoped_task_environment(
-      ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
-      ScopedTaskEnvironment::NowSource::MAIN_THREAD_MOCK_TIME);
-  const TimeTicks start_time = base::TimeTicks::Now();
-  scoped_task_environment.SetAllowTimeToAutoAdvanceUntilForTesting(
-      start_time + TimeDelta::FromSeconds(100));
-
-  ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, base::DoNothing(),
-                                                 TimeDelta::FromSeconds(42));
-  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::DoNothing());
-  EXPECT_TRUE(
-      scoped_task_environment.GetTimeDomain()->MaybeFastForwardToNextTask(
-          false));
-  EXPECT_EQ(start_time, base::TimeTicks::Now());
-}
-
-TEST_F(ScopedTaskEnvironmentTest,
-       MockTimeDomain_MaybeFastForwardToNextTask_TaskAfterAutoAdvanceUntil) {
-  constexpr base::TimeDelta kDelay = TimeDelta::FromSeconds(42);
-  ScopedTaskEnvironmentForTest scoped_task_environment(
-      ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
-      ScopedTaskEnvironment::NowSource::MAIN_THREAD_MOCK_TIME);
-  const TimeTicks start_time = base::TimeTicks::Now();
-  scoped_task_environment.SetAllowTimeToAutoAdvanceUntilForTesting(start_time +
-                                                                   kDelay);
-
-  ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, base::DoNothing(),
-                                                 TimeDelta::FromSeconds(100));
-  EXPECT_TRUE(
-      scoped_task_environment.GetTimeDomain()->MaybeFastForwardToNextTask(
-          false));
-  EXPECT_EQ(start_time + kDelay, base::TimeTicks::Now());
-}
-
-TEST_F(ScopedTaskEnvironmentTest,
-       MockTimeDomain_MaybeFastForwardToNextTask_NoTasksPending) {
-  constexpr base::TimeDelta kDelay = TimeDelta::FromSeconds(42);
-  ScopedTaskEnvironmentForTest scoped_task_environment(
-      ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
-      ScopedTaskEnvironment::NowSource::MAIN_THREAD_MOCK_TIME);
-  const TimeTicks start_time = base::TimeTicks::Now();
-  scoped_task_environment.SetAllowTimeToAutoAdvanceUntilForTesting(start_time +
-                                                                   kDelay);
-
-  EXPECT_FALSE(
-      scoped_task_environment.GetTimeDomain()->MaybeFastForwardToNextTask(
-          false));
-  EXPECT_EQ(start_time + kDelay, base::TimeTicks::Now());
-}
-
+// FastForwardBy(0) should be equivalent of RunUntilIdle().
 TEST_F(ScopedTaskEnvironmentTest, FastForwardZero) {
   ScopedTaskEnvironment scoped_task_environment(
       ScopedTaskEnvironment::MainThreadType::MOCK_TIME);
 
-  int run_count = 0;
-  ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, BindLambdaForTesting([&]() { run_count++; }));
-  ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, BindLambdaForTesting([&]() { run_count++; }));
-  ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, BindLambdaForTesting([&]() { run_count++; }));
+  std::atomic_int run_count{0};
+
+  for (int i = 0; i < 1000; ++i) {
+    ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, BindLambdaForTesting([&]() {
+          run_count.fetch_add(1, std::memory_order_relaxed);
+        }));
+    base::PostTask(FROM_HERE, BindLambdaForTesting([&]() {
+                     run_count.fetch_add(1, std::memory_order_relaxed);
+                   }));
+  }
 
   scoped_task_environment.FastForwardBy(base::TimeDelta());
 
-  EXPECT_EQ(3, run_count);
+  EXPECT_EQ(2000, run_count.load(std::memory_order_relaxed));
 }
 
-#if defined(OS_IOS)
-// This test flakily times out on iOS.
-#define MAYBE_CrossThreadTaskPostingDoesntAffectMockTime \
-  DISABLED_CrossThreadTaskPostingDoesntAffectMockTime
-#else
-#define MAYBE_CrossThreadTaskPostingDoesntAffectMockTime \
-  CrossThreadTaskPostingDoesntAffectMockTime
-#endif
-
-TEST_F(ScopedTaskEnvironmentTest,
-       MAYBE_CrossThreadTaskPostingDoesntAffectMockTime) {
+TEST_F(ScopedTaskEnvironmentTest, CrossThreadTaskPostingDoesntAffectMockTime) {
   ScopedTaskEnvironment scoped_task_environment(
-      ScopedTaskEnvironment::MainThreadType::MOCK_TIME);
-  scoped_refptr<SingleThreadTaskRunner> main_thread =
-      ThreadTaskRunnerHandle::Get();
+      ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
+      ScopedTaskEnvironment::NowSource::MAIN_THREAD_MOCK_TIME);
 
-  // Start a thread that will spam the main thread with uninteresting tasks
-  // which shouldn't interfere with main thread MOCK_TIME.
-  Thread spamming_thread("test thread");
-  spamming_thread.Start();
-  AtomicFlag stop_spamming;
+  // ThreadPool WorkerThreads don't like when TimeTicks::Now() evaluates to 0.
+  // TODO(gab): Make ScopedTaskEnvironment not start at 0.
+  scoped_task_environment.FastForwardBy(TimeDelta::FromMilliseconds(100));
 
-  RepeatingClosure repeating_spam_task = BindLambdaForTesting([&]() {
-    if (stop_spamming.IsSet())
-      return;
-    // We don't want to completely drown out main thread tasks so we rate limit
-    // how fast we post to the main thread to at most 1 per 50 microseconds.
-    spamming_thread.task_runner()->PostDelayedTask(
-        FROM_HERE, repeating_spam_task, TimeDelta::FromMicroseconds(50));
-    main_thread->PostTask(FROM_HERE, DoNothing());
-  });
-  spamming_thread.task_runner()->PostTask(FROM_HERE, repeating_spam_task);
-
-  // Start a repeating delayed task.
   int count = 0;
-  RepeatingClosure repeating_delayed_task = BindLambdaForTesting([&]() {
-    main_thread->PostDelayedTask(FROM_HERE, repeating_delayed_task,
-                                 TimeDelta::FromSeconds(1));
 
-    count++;
-  });
-  main_thread->PostDelayedTask(FROM_HERE, repeating_delayed_task,
-                               TimeDelta::FromSeconds(1));
+  // Post tasks delayd between 0 and 999 seconds.
+  for (int i = 0; i < 1000; ++i) {
+    const TimeDelta delay = TimeDelta::FromSeconds(i);
+    ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE,
+        BindOnce(
+            [](TimeTicks expected_run_time, int* count) {
+              EXPECT_EQ(expected_run_time, TimeTicks::Now());
+              ++*count;
+            },
+            TimeTicks::Now() + delay, &count),
+        delay);
+  }
 
-  scoped_task_environment.FastForwardBy(TimeDelta::FromSeconds(10000));
+  // Having a bunch of tasks running in parallel and replying to the main thread
+  // shouldn't affect the rest of this test. Wait for the first task to run
+  // before proceeding with the test to increase the likelihood of exercising
+  // races.
+  base::WaitableEvent first_reply_is_incoming;
+  for (int i = 0; i < 1000; ++i) {
+    base::PostTaskAndReply(
+        FROM_HERE,
+        BindOnce(&WaitableEvent::Signal, Unretained(&first_reply_is_incoming)),
+        DoNothing());
+  }
+  first_reply_is_incoming.Wait();
+
+  scoped_task_environment.FastForwardBy(TimeDelta::FromSeconds(1000));
 
   // If this test flakes it's because there's an error with MockTimeDomain.
-  EXPECT_EQ(count, 10000);
+  EXPECT_EQ(count, 1000);
 
-  stop_spamming.Set();
-  spamming_thread.Stop();
+  // Flush any remaining asynchronous tasks with Unretained() state.
+  scoped_task_environment.RunUntilIdle();
 }
 
 #if defined(OS_WIN)
diff --git a/build/android/gradle/root.jinja b/build/android/gradle/root.jinja
index c7bd7b0c..513db63 100644
--- a/build/android/gradle/root.jinja
+++ b/build/android/gradle/root.jinja
@@ -10,7 +10,7 @@
     }
     dependencies {
 {% if channel == 'canary' %}
-        classpath "com.android.tools.build:gradle:3.5.0-beta01"
+        classpath "com.android.tools.build:gradle:3.6.0-alpha02"
 {% elif channel == 'beta' %}
         classpath "com.android.tools.build:gradle:3.1.0-beta4"
 {% else %}
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index ed0f8c32..193c9cb 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -357,6 +357,10 @@
     <ignore regexp="The resource `R.string..*lite_mode` appears to be unused"/>
     <!-- Module titles may only be used by the Play Store. -->
     <ignore regexp="The resource `R.string.*_module_title` appears to be unused"/>
+    <!-- Old-style and new-style WebAPKs use same resources for simplicity. Old-style WebAPKs do
+         not use R.style.SplashTheme but new-style WebAPKs do.
+         TODO(crbug.com/971254): Remove suppression once old-style WebAPKs are deprecated. -->
+    <ignore regexp="The resource `R.style.SplashTheme` appears to be unused"/>
     <!-- Endnote: Please specify number of suppressions when adding more -->
   </issue>
   <issue id="UseCompoundDrawables">
diff --git a/build/config/allocator.gni b/build/config/allocator.gni
index f2a42d4..611b43e7 100644
--- a/build/config/allocator.gni
+++ b/build/config/allocator.gni
@@ -34,7 +34,7 @@
   use_partition_alloc = !is_ios
 
   # Use the new tcmalloc. It's relevant only when use_allocator == "tcmalloc".
-  use_new_tcmalloc = !is_chromecast
+  use_new_tcmalloc = true
 }
 
 if (is_nacl) {
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 947f88af..eb2e8d4 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8911486701208682448
\ No newline at end of file
+8911432991615498112
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 2a4ef79..9974916 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8911489232856453328
\ No newline at end of file
+8911431548849094000
\ No newline at end of file
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 97de94d..772e7b9 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -775,13 +775,13 @@
       "//gpu/vulkan:test_support",
       "//gpu/vulkan/init",
     ]
-  }
 
-  # TODO(samans): Support more configurations.
-  # CFI issue: https://crbug.com/967819
-  # x86 issue: https://crbug.com/967831
-  if (use_x11 && target_cpu != "x86" && !is_cfi) {
-    defines = [ "ENABLE_CC_VULKAN_TESTS" ]
+    # TODO(samans): Support more configurations.
+    # CFI issue: https://crbug.com/967819
+    # x86 issue: https://crbug.com/967831
+    if (use_x11 && target_cpu != "x86" && !is_cfi) {
+      defines = [ "ENABLE_CC_VULKAN_TESTS" ]
+    }
   }
 }
 
diff --git a/chrome/VERSION b/chrome/VERSION
index 61fd67e..3a448d02 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=77
 MINOR=0
-BUILD=3816
+BUILD=3817
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 2e72c71..b6445d8 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -2606,6 +2606,7 @@
     "java/src/org/chromium/chrome/browser/tabmodel/TabModelObserverJniBridge.java",
     "java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java",
     "java/src/org/chromium/chrome/browser/translate/TranslateBridge.java",
+    "java/src/org/chromium/chrome/browser/usage_stats/NotificationSuspender.java",
     "java/src/org/chromium/chrome/browser/usage_stats/UsageStatsBridge.java",
     "java/src/org/chromium/chrome/browser/util/ChromeContextUtil.java",
     "java/src/org/chromium/chrome/browser/util/FeatureUtilities.java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 4b7e2668..281fb054 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1216,6 +1216,7 @@
   "java/src/org/chromium/chrome/browser/preferences/ChromeBaseCheckBoxPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/ChromeBasePreference.java",
+  "java/src/org/chromium/chrome/browser/preferences/ChromeBasePreferenceCompat.java",
   "java/src/org/chromium/chrome/browser/preferences/ChromeImageViewPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java",
   "java/src/org/chromium/chrome/browser/preferences/ChromeSwitchPreference.java",
@@ -1229,6 +1230,7 @@
   "java/src/org/chromium/chrome/browser/preferences/LocationSettings.java",
   "java/src/org/chromium/chrome/browser/preferences/MainPreferences.java",
   "java/src/org/chromium/chrome/browser/preferences/ManagedPreferenceDelegate.java",
+  "java/src/org/chromium/chrome/browser/preferences/ManagedPreferenceDelegateCompat.java",
   "java/src/org/chromium/chrome/browser/preferences/ManagedPreferencesUtils.java",
   "java/src/org/chromium/chrome/browser/preferences/ManageSyncPreferences.java",
   "java/src/org/chromium/chrome/browser/preferences/NotificationsPreferences.java",
@@ -1251,6 +1253,7 @@
   "java/src/org/chromium/chrome/browser/preferences/SyncedAccountPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/TextAndButtonPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/TextMessagePreference.java",
+  "java/src/org/chromium/chrome/browser/preferences/TextMessagePreferenceCompat.java",
   "java/src/org/chromium/chrome/browser/preferences/TextScalePreference.java",
   "java/src/org/chromium/chrome/browser/preferences/autofill/AndroidPaymentAppPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/autofill/AndroidPaymentAppsFragment.java",
@@ -1623,6 +1626,7 @@
   "java/src/org/chromium/chrome/browser/upgrade/PackageReplacedBroadcastReceiver.java",
   "java/src/org/chromium/chrome/browser/usage_stats/DigitalWellbeingClient.java",
   "java/src/org/chromium/chrome/browser/usage_stats/EventTracker.java",
+  "java/src/org/chromium/chrome/browser/usage_stats/NotificationSuspender.java",
   "java/src/org/chromium/chrome/browser/usage_stats/PageViewObserver.java",
   "java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java",
   "java/src/org/chromium/chrome/browser/usage_stats/SuspensionTracker.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index bca0154..f906c04 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -49,7 +49,6 @@
   "javatests/src/org/chromium/chrome/browser/UsbChooserDialogTest.java",
   "javatests/src/org/chromium/chrome/browser/VideoFullscreenOrientationLockChromeTest.java",
   "javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java",
-  "javatests/src/org/chromium/chrome/browser/WebShareTest.java",
   "javatests/src/org/chromium/chrome/browser/accessibility/FontSizePrefsTest.java",
   "javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java",
   "javatests/src/org/chromium/chrome/browser/appmenu/DataSaverAppMenuTest.java",
@@ -494,6 +493,7 @@
   "javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenThemeColorTest.java",
   "javatests/src/org/chromium/chrome/browser/webapps/WebappVisibilityTest.java",
   "javatests/src/org/chromium/chrome/browser/webauth/AuthenticatorTest.java",
+  "javatests/src/org/chromium/chrome/browser/webshare/WebShareTest.java",
   "javatests/src/org/chromium/chrome/browser/widget/DualControlLayoutTest.java",
   "javatests/src/org/chromium/chrome/browser/widget/GridTabSwitcherLayoutPerfTest.java",
   "javatests/src/org/chromium/chrome/browser/widget/OverviewListLayoutTest.java",
diff --git a/chrome/android/features/keyboard_accessory/internal/BUILD.gn b/chrome/android/features/keyboard_accessory/internal/BUILD.gn
index 4dafa94..08ca7def 100644
--- a/chrome/android/features/keyboard_accessory/internal/BUILD.gn
+++ b/chrome/android/features/keyboard_accessory/internal/BUILD.gn
@@ -64,8 +64,9 @@
     "java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetCoordinator.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/PasswordAccessorySheetViewBinder.java",
     "java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewBinder.java",
+    "java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetViewBinder.java",
+    "java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/TouchToFillSheetCoordinator.java",
     "java/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryTabLayoutCoordinator.java",
     "java/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryTabLayoutMediator.java",
     "java/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryTabLayoutProperties.java",
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
index ebb306c0..4bcfb405 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
@@ -7,6 +7,7 @@
 import static org.chromium.chrome.browser.ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY;
 import static org.chromium.chrome.browser.ChromeFeatureList.AUTOFILL_MANUAL_FALLBACK_ANDROID;
 import static org.chromium.chrome.browser.ChromeFeatureList.PASSWORDS_KEYBOARD_ACCESSORY;
+import static org.chromium.chrome.browser.ChromeFeatureList.TOUCH_TO_FILL_ANDROID;
 import static org.chromium.chrome.browser.keyboard_accessory.ManualFillingProperties.KEYBOARD_EXTENSION_STATE;
 import static org.chromium.chrome.browser.keyboard_accessory.ManualFillingProperties.KeyboardExtensionState.EXTENDING_KEYBOARD;
 import static org.chromium.chrome.browser.keyboard_accessory.ManualFillingProperties.KeyboardExtensionState.FLOATING_BAR;
@@ -48,6 +49,7 @@
 import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.AddressAccessorySheetCoordinator;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.CreditCardAccessorySheetCoordinator;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.PasswordAccessorySheetCoordinator;
+import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.TouchToFillSheetCoordinator;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.Tab.TabHidingType;
@@ -615,7 +617,9 @@
                 if (!ChromeFeatureList.isEnabled(AUTOFILL_MANUAL_FALLBACK_ANDROID)) return false;
                 // Intentional fallthrough. The restrictions for passwords apply to other tabs.
             case AccessoryTabType.PASSWORDS:
-                if (!ChromeFeatureList.isEnabled(PASSWORDS_KEYBOARD_ACCESSORY)) return false;
+                return ChromeFeatureList.isEnabled(PASSWORDS_KEYBOARD_ACCESSORY);
+            case AccessoryTabType.TOUCH_TO_FILL:
+                return ChromeFeatureList.isEnabled(TOUCH_TO_FILL_ANDROID);
         }
         return true;
     }
@@ -631,6 +635,9 @@
             case AccessoryTabType.PASSWORDS:
                 return new PasswordAccessorySheetCoordinator(
                         mActivity, mAccessorySheet.getScrollListener());
+            case AccessoryTabType.TOUCH_TO_FILL:
+                return new TouchToFillSheetCoordinator(
+                        mActivity, mAccessorySheet.getScrollListener());
             case AccessoryTabType.ALL: // Intentional fallthrough.
             case AccessoryTabType.COUNT: // Intentional fallthrough.
         }
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingState.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingState.java
index de256de..450d2e78b 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingState.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingState.java
@@ -25,7 +25,11 @@
  */
 class ManualFillingState {
     private final static int[] TAB_ORDER = {
-            AccessoryTabType.PASSWORDS, AccessoryTabType.CREDIT_CARDS, AccessoryTabType.ADDRESSES};
+            AccessoryTabType.PASSWORDS,
+            AccessoryTabType.CREDIT_CARDS,
+            AccessoryTabType.ADDRESSES,
+            AccessoryTabType.TOUCH_TO_FILL,
+    };
     private final WebContents mWebContents;
     private final SparseArray<SheetState> mSheetStates = new SparseArray<>();
     private @Nullable CachedProviderAdapter<KeyboardAccessoryData.Action[]> mActionsProvider;
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AccessorySheetTabModel.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AccessorySheetTabModel.java
index ce084b8..e37796c 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AccessorySheetTabModel.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AccessorySheetTabModel.java
@@ -24,7 +24,8 @@
      * organized in a {@link ListModel}. A specific ViewHolder is defined for each piece.
      */
     static class AccessorySheetDataPiece {
-        @IntDef({Type.TITLE, Type.PASSWORD_INFO, Type.ADDRESS_INFO, Type.FOOTER_COMMAND})
+        @IntDef({Type.TITLE, Type.PASSWORD_INFO, Type.ADDRESS_INFO, Type.CREDIT_CARD_INFO,
+                Type.TOUCH_TO_FILL_INFO, Type.FOOTER_COMMAND})
         @Retention(RetentionPolicy.SOURCE)
         @interface Type {
             /**
@@ -44,9 +45,13 @@
              */
             int CREDIT_CARD_INFO = 4;
             /**
+             * A section containing touch to fill information.
+             */
+            int TOUCH_TO_FILL_INFO = 5;
+            /**
              * A command at the end of the accessory sheet tab.
              */
-            int FOOTER_COMMAND = 5;
+            int FOOTER_COMMAND = 6;
         }
 
         private Object mDataPiece;
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/TouchToFillSheetCoordinator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/TouchToFillSheetCoordinator.java
new file mode 100644
index 0000000..3121ca0
--- /dev/null
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/TouchToFillSheetCoordinator.java
@@ -0,0 +1,53 @@
+// 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.content.Context;
+import android.support.annotation.Nullable;
+import android.support.v7.content.res.AppCompatResources;
+import android.support.v7.widget.RecyclerView;
+
+import org.chromium.chrome.browser.keyboard_accessory.AccessoryTabType;
+import org.chromium.chrome.browser.keyboard_accessory.R;
+import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.AccessorySheetTabModel.AccessorySheetDataPiece.Type;
+
+/**
+ * This component is a tab that can be added to the ManualFillingCoordinator. This tab
+ * allows selecting credentials from a sheet below the keyboard accessory which will be filled when
+ * tapped.
+ */
+public class TouchToFillSheetCoordinator extends AccessorySheetTabCoordinator {
+    private AccessorySheetTabModel mModel = new AccessorySheetTabModel();
+    private final AccessorySheetTabMediator mMediator = new AccessorySheetTabMediator(
+            mModel, AccessoryTabType.TOUCH_TO_FILL, Type.TOUCH_TO_FILL_INFO);
+
+    /**
+     * Creates the touch to fill tab.
+     *
+     * @param context        The {@link Context} containing resources like icons and layouts for
+     *                       this tab.
+     * @param scrollListener An optional listener that will be bound to an inflated recycler
+     *                       view.
+     */
+    public TouchToFillSheetCoordinator(
+            Context context, @Nullable RecyclerView.OnScrollListener scrollListener) {
+        super(
+                // TODO(crbug.com/957532): Add an appropriate title, icon, and restructure this
+                // class to use an Icon Provider with a static instance of the resource.
+                "Touch To Fill",
+                AppCompatResources.getDrawable(context, R.drawable.ic_error_grey800_24dp_filled),
+                // TODO(crbug.com/957532): Add strings and resources properly.
+                // We pass an empty contentDescription, since the Touch to Fill sheet should not be
+                // triggered by clicking an icon.
+                "", "Touch to Fill sheet is open",
+                // TODO(crbug.com/957532): Add a new layout, or generalize the existing one.
+                R.layout.password_accessory_sheet, AccessoryTabType.TOUCH_TO_FILL, scrollListener);
+    }
+
+    @Override
+    AccessorySheetTabMediator getMediator() {
+        return mMediator;
+    }
+}
diff --git a/chrome/android/features/keyboard_accessory/internal/java/strings/translations/android_keyboard_accessory_strings_id.xtb b/chrome/android/features/keyboard_accessory/internal/java/strings/translations/android_keyboard_accessory_strings_id.xtb
index f96c5f0..09d5abc6 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/strings/translations/android_keyboard_accessory_strings_id.xtb
+++ b/chrome/android/features/keyboard_accessory/internal/java/strings/translations/android_keyboard_accessory_strings_id.xtb
@@ -1,7 +1,7 @@
 <?xml version="1.0" ?>
 <!DOCTYPE translationbundle>
 <translationbundle lang="id">
-<translation id="1492646418094134664">Tap untuk mengisi kartu kredit dengan Chrome</translation>
+<translation id="1492646418094134664">Tap untuk mengisi info kartu kredit dengan Chrome</translation>
 <translation id="2610239185026711824">Sarankan sandi</translation>
 <translation id="2803478378562657435">Menampilkan opsi sandi dan sandi yang disimpan</translation>
 <translation id="2903493209154104877">Alamat</translation>
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index 8683c792..e85ea34 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -25,10 +25,12 @@
     "java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediator.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java",
+    "java/src/org/chromium/chrome/browser/tasks/tab_management/SelectableTabGridViewHolder.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinder.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java",
+    "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetMediator.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetContent.java",
diff --git a/chrome/android/features/tab_ui/java/res/drawable/tab_grid_selection_list_icon.xml b/chrome/android/features/tab_ui/java/res/drawable/tab_grid_selection_list_icon.xml
new file mode 100644
index 0000000..16fd1378
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/res/drawable/tab_grid_selection_list_icon.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:maxLevel="@integer/list_item_level_default">
+        <shape android:shape="oval">
+            <stroke
+                android:width="2dp"
+                android:color="@android:color/black"/>
+        </shape>
+    </item>
+    <item android:maxLevel="@integer/list_item_level_selected">
+        <shape android:shape="oval">
+            <solid android:color="@color/light_active_color" />
+        </shape>
+    </item>
+</level-list>
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/java/res/values/dimens.xml b/chrome/android/features/tab_ui/java/res/values/dimens.xml
index d31dd44..fcd70d2 100644
--- a/chrome/android/features/tab_ui/java/res/values/dimens.xml
+++ b/chrome/android/features/tab_ui/java/res/values/dimens.xml
@@ -12,6 +12,7 @@
     <dimen name="tab_grid_close_button_size">18dp</dimen>
     <dimen name="tab_grid_dialog_side_margin">16dp</dimen>
     <dimen name="tab_grid_dialog_top_margin">85dp</dimen>
+    <dimen name="tab_grid_merge_threshold">45dp</dimen>
     <dimen name="tab_grid_thumbnail_card_default_size">152dp</dimen>
     <dimen name="tab_grid_thumbnail_favicon_frame_padding">16dp</dimen>
     <dimen name="tab_grid_thumbnail_favicon_padding">24dp</dimen>
@@ -19,4 +20,5 @@
     <dimen name="tab_grid_thumbnail_favicon_background_padding">2dp</dimen>
     <dimen name="tab_grid_thumbnail_favicon_background_down_shift">2dp</dimen>
     <dimen name="swipe_to_dismiss_threshold">72dp</dimen>
+    <dimen name="selection_tab_grid_toggle_button_inset">12dp</dimen>
 </resources>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtils.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtils.java
index 928efe1..3ab34d8e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtils.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtils.java
@@ -12,6 +12,7 @@
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
 import org.chromium.chrome.browser.tasks.tabgroup.TabGroupModelFilter;
@@ -110,25 +111,25 @@
 
     /**
      * This method gets the index in TabModel of the first tab in {@code tabs}.
-     * @param selector   The selector that owns the {@code tab}.
+     * @param tabModel   The tabModel that owns the {@code tab}.
      * @param tabs       The list of tabs among which we need to find the first tab index.
      * @return The index in TabModel of the first tab in {@code tabs}
      */
-    public static int getFirstTabModelIndexForList(TabModelSelector selector, List<Tab> tabs) {
+    public static int getFirstTabModelIndexForList(TabModel tabModel, List<Tab> tabs) {
         assert tabs != null && tabs.size() != 0;
 
-        return selector.getCurrentModel().indexOf(tabs.get(0));
+        return tabModel.indexOf(tabs.get(0));
     }
 
     /**
      * This method gets the index in TabModel of the last tab in {@code tabs}.
-     * @param selector   The selector that owns the {@code tab}.
+     * @param tabModel   The tabModel that owns the {@code tab}.
      * @param tabs       The list of tabs among which we need to find the last tab index.
      * @return The index in TabModel of the last tab in {@code tabs}
      */
-    public static int getLastTabModelIndexForList(TabModelSelector selector, List<Tab> tabs) {
+    public static int getLastTabModelIndexForList(TabModel tabModel, List<Tab> tabs) {
         assert tabs != null && tabs.size() != 0;
 
-        return selector.getCurrentModel().indexOf(tabs.get(tabs.size() - 1));
+        return tabModel.indexOf(tabs.get(tabs.size() - 1));
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
index b54c308..d5a222ec 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
@@ -152,6 +152,11 @@
     }
 
     @Override
+    public void softCleanup() {
+        mTabGridCoordinator.softCleanup();
+    }
+
+    @Override
     public void destroy() {
         mTabGridCoordinator.destroy();
         mContainerViewChangeProcessor.destroy();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediator.java
index 168e7a0d..3954aba 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediator.java
@@ -13,6 +13,7 @@
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.TOP_PADDING;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.VISIBILITY_LISTENER;
 
+import android.graphics.Bitmap;
 import android.os.Handler;
 import android.support.annotation.Nullable;
 
@@ -58,10 +59,14 @@
     private static final int DEFAULT_TOP_PADDING = 0;
 
     /** Field trial parameter for the {@link TabListRecyclerView} cleanup delay. */
+    private static final String SOFT_CLEANUP_DELAY_PARAM = "soft-cleanup-delay";
+    private static final int DEFAULT_SOFT_CLEANUP_DELAY_MS = 3_000;
     private static final String CLEANUP_DELAY_PARAM = "cleanup-delay";
-    private static final int DEFAULT_CLEANUP_DELAY_MS = 10_000;
+    private static final int DEFAULT_CLEANUP_DELAY_MS = 30_000;
+    private Integer mSoftCleanupDelayMsForTesting;
     private Integer mCleanupDelayMsForTesting;
     private final Handler mHandler;
+    private final Runnable mSoftClearTabListRunnable;
     private final Runnable mClearTabListRunnable;
 
     private final ResetHandler mResetHandler;
@@ -106,6 +111,11 @@
          * @return Whether the {@link TabListRecyclerView} can be shown quickly.
          */
         boolean resetWithTabList(TabList tabList);
+
+        /**
+         * Release the thumbnail {@link Bitmap} but keep the {@link TabGridViewHolder}.
+         */
+        void softCleanup();
     }
 
     /**
@@ -184,10 +194,23 @@
         mCompositorViewHolder = compositorViewHolder;
         mTabGridDialogResetHandler = tabGridDialogResetHandler;
 
+        mSoftClearTabListRunnable = mResetHandler::softCleanup;
         mClearTabListRunnable = () -> mResetHandler.resetWithTabList(null);
         mHandler = new Handler();
     }
 
+    private int getSoftCleanupDelay() {
+        if (mSoftCleanupDelayMsForTesting != null) return mSoftCleanupDelayMsForTesting;
+
+        String delay = ChromeFeatureList.getFieldTrialParamByFeature(
+                ChromeFeatureList.TAB_TO_GTS_ANIMATION, SOFT_CLEANUP_DELAY_PARAM);
+        try {
+            return Integer.valueOf(delay);
+        } catch (NumberFormatException e) {
+            return DEFAULT_SOFT_CLEANUP_DELAY_MS;
+        }
+    }
+
     private int getCleanupDelay() {
         if (mCleanupDelayMsForTesting != null) return mCleanupDelayMsForTesting;
 
@@ -237,6 +260,7 @@
     }
 
     boolean prepareOverview() {
+        mHandler.removeCallbacks(mSoftClearTabListRunnable);
         mHandler.removeCallbacks(mClearTabListRunnable);
         boolean quick = mResetHandler.resetWithTabList(
                 mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter());
@@ -290,10 +314,18 @@
      * @see GridTabSwitcher#postHiding
      */
     void postHiding() {
+        mHandler.postDelayed(mSoftClearTabListRunnable, getSoftCleanupDelay());
         mHandler.postDelayed(mClearTabListRunnable, getCleanupDelay());
     }
 
     /**
+     * Set the delay for soft cleanup.
+     */
+    void setSoftCleanupDelayForTesting(int timeoutMs) {
+        mSoftCleanupDelayMsForTesting = timeoutMs;
+    }
+
+    /**
      * Set the delay for lazy cleanup.
      */
     void setCleanupDelayForTesting(int timeoutMs) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SelectableTabGridViewHolder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SelectableTabGridViewHolder.java
new file mode 100644
index 0000000..f8d40101
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SelectableTabGridViewHolder.java
@@ -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.
+
+package org.chromium.chrome.browser.tasks.tab_management;
+
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
+import android.support.graphics.drawable.AnimatedVectorDrawableCompat;
+import android.support.v4.content.res.ResourcesCompat;
+import android.support.v7.content.res.AppCompatResources;
+
+import org.chromium.chrome.R;
+
+/**
+ * A {@link TabGridViewHolder} with a checkable button. This is used in the manual selection mode.
+ */
+class SelectableTabGridViewHolder extends TabGridViewHolder {
+    public final int defaultLevel;
+    public final int selectedLevel;
+    public AnimatedVectorDrawableCompat mCheckDrawable;
+    public ColorStateList iconColorList;
+
+    SelectableTabGridViewHolder(TabGridView itemView) {
+        super(itemView);
+
+        defaultLevel = itemView.getResources().getInteger(
+                org.chromium.chrome.R.integer.list_item_level_default);
+        selectedLevel = itemView.getResources().getInteger(
+                org.chromium.chrome.R.integer.list_item_level_selected);
+        mCheckDrawable = AnimatedVectorDrawableCompat.create(
+                itemView.getContext(), R.drawable.ic_check_googblue_24dp_animated);
+        iconColorList = AppCompatResources.getColorStateList(
+                itemView.getContext(), org.chromium.chrome.R.color.default_icon_color_inverse);
+
+        Drawable selectedDrawable = new InsetDrawable(
+                ResourcesCompat.getDrawable(itemView.getResources(),
+                        org.chromium.chrome.tab_ui.R.drawable.tab_grid_selection_list_icon,
+                        itemView.getContext().getTheme()),
+                (int) itemView.getResources().getDimension(
+                        org.chromium.chrome.tab_ui.R.dimen.selection_tab_grid_toggle_button_inset));
+        actionButton.setBackground(selectedDrawable);
+        actionButton.getBackground().setLevel(defaultLevel);
+    }
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SilenceLintErrors.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SilenceLintErrors.java
index 4417758..f183a1e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SilenceLintErrors.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SilenceLintErrors.java
@@ -26,6 +26,13 @@
             R.string.accessibility_bottom_tab_strip_expand_tab_sheet,
             R.string.accessibility_bottom_tab_grid_close_tab_sheet,
             R.string.tabswitcher_create_group,
+            R.string.tab_selection_editor_group,
+            R.string.accessibility_group_selected_tabs,
+            R.string.tab_selection_editor_toolbar_select_tabs,
+            R.string.accessibility_select_tab,
+            R.string.accessibility_unselect_tab,
+            R.string.undo_bar_group_tabs_message,
+            R.string.menu_group_tabs,
     };
 
     private SilenceLintErrors() {}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
new file mode 100644
index 0000000..f2d0b17
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
@@ -0,0 +1,210 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks.tab_management;
+
+import android.graphics.Canvas;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
+
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.EmptyTabModelFilter;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelFilter;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.tasks.tab_groups.TabGroupUtils;
+import org.chromium.chrome.browser.tasks.tabgroup.TabGroupModelFilter;
+
+import java.util.List;
+
+/**
+ * A {@link ItemTouchHelper.SimpleCallback} implementation to host the logic for swipe and drag
+ * related actions in grid related layouts.
+ * TODO(yuezhanggg): Get rid of using notifyDataSetChanged in adapter.
+ */
+public class TabGridItemTouchHelperCallback extends ItemTouchHelper.SimpleCallback {
+    /**
+     * An interface to update information in {@link TabListModel}.
+     */
+    public interface UpdateTabInfoHandler {
+        void updateTabInfo(int index, Tab tab, boolean isSelected);
+    }
+
+    private final TabListModel mModel;
+    private final TabModelSelector mTabModelSelector;
+    private final TabListMediator.TabActionListener mTabClosedListener;
+    private final String mComponentName;
+    private final UpdateTabInfoHandler mUpdateTabHandler;
+    private float mSwipeToDismissThreshold;
+    private float mMergeThreshold;
+    private boolean mActionsOnAllRelatedTabs;
+    private int mDragFlags;
+    private int mSelectedTabIndex = TabModel.INVALID_TAB_INDEX;
+    private int mHoveredTabIndex = TabModel.INVALID_TAB_INDEX;
+    private RecyclerView mRecyclerView;
+
+    public TabGridItemTouchHelperCallback(TabListModel tabListModel,
+            TabModelSelector tabModelSelector, TabListMediator.TabActionListener tabClosedListener,
+            UpdateTabInfoHandler updateTabHandler, String componentName,
+            boolean actionsOnAllRelatedTabs) {
+        super(0, 0);
+        mModel = tabListModel;
+        mTabModelSelector = tabModelSelector;
+        mTabClosedListener = tabClosedListener;
+        mUpdateTabHandler = updateTabHandler;
+        mComponentName = componentName;
+        mActionsOnAllRelatedTabs = actionsOnAllRelatedTabs;
+    }
+
+    /**
+     * This method sets up parameters that are used by the {@link ItemTouchHelper} to make decisions
+     * about user actions.
+     * @param swipeToDismissThreshold   Defines the threshold that user needs to swipe in order to
+     *         be considered as a remove operation.
+     * @param mergeThreshold            Defines the threshold of how much two items need to be
+     *         overlapped in order to be considered as a merge operation.
+     * @param isDragEnabled             Whether drag related behavior should handled in this
+     *         callback.
+     */
+    void setupCallback(float swipeToDismissThreshold, float mergeThreshold, boolean isDragEnabled) {
+        mSwipeToDismissThreshold = swipeToDismissThreshold;
+        mMergeThreshold = mergeThreshold;
+        mDragFlags = isDragEnabled ? ItemTouchHelper.START | ItemTouchHelper.END
+                        | ItemTouchHelper.UP | ItemTouchHelper.DOWN
+                                   : 0;
+    }
+
+    @Override
+    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
+        final int dragFlags = mDragFlags;
+        final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
+        mRecyclerView = recyclerView;
+        return makeMovementFlags(dragFlags, swipeFlags);
+    }
+
+    @Override
+    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder fromViewHolder,
+            RecyclerView.ViewHolder toViewHolder) {
+        assert fromViewHolder instanceof TabGridViewHolder;
+        assert toViewHolder instanceof TabGridViewHolder;
+
+        RecordUserAction.record("TabGrid.DragToReorder." + mComponentName);
+        mSelectedTabIndex = toViewHolder.getAdapterPosition();
+        if (mHoveredTabIndex != -1) {
+            mModel.updateHoveredTabForMergeToGroup(mHoveredTabIndex, false);
+            mHoveredTabIndex = -1;
+        }
+
+        int currentTabId = ((TabGridViewHolder) fromViewHolder).getTabId();
+        int destinationTabId = ((TabGridViewHolder) toViewHolder).getTabId();
+        int distance = toViewHolder.getAdapterPosition() - fromViewHolder.getAdapterPosition();
+        TabModelFilter filter =
+                mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter();
+        TabModel tabModel = mTabModelSelector.getCurrentModel();
+        if (filter instanceof EmptyTabModelFilter) {
+            tabModel.moveTab(currentTabId,
+                    mModel.indexFromId(currentTabId) + (distance > 0 ? distance + 1 : distance));
+        } else if (!mActionsOnAllRelatedTabs) {
+            int destinationIndex = tabModel.indexOf(mTabModelSelector.getTabById(destinationTabId));
+            tabModel.moveTab(currentTabId, distance > 0 ? destinationIndex + 1 : destinationIndex);
+        } else {
+            List<Tab> destinationTabGroup = getRelatedTabsForId(destinationTabId);
+            int newIndex = distance >= 0
+                    ? TabGroupUtils.getLastTabModelIndexForList(tabModel, destinationTabGroup) + 1
+                    : TabGroupUtils.getFirstTabModelIndexForList(tabModel, destinationTabGroup);
+            ((TabGroupModelFilter) filter).moveRelatedTabs(currentTabId, newIndex);
+        }
+        return true;
+    }
+
+    @Override
+    public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
+        assert viewHolder instanceof TabGridViewHolder;
+
+        mTabClosedListener.run(((TabGridViewHolder) viewHolder).getTabId());
+        RecordUserAction.record("MobileStackViewSwipeCloseTab." + mComponentName);
+    }
+
+    @Override
+    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
+        if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
+            mSelectedTabIndex = viewHolder.getAdapterPosition();
+            mModel.updateSelectedTabForMergeToGroup(mSelectedTabIndex, true);
+        } else if (actionState == ItemTouchHelper.ACTION_STATE_IDLE) {
+            if (mHoveredTabIndex != -1 && mActionsOnAllRelatedTabs) {
+                RecordUserAction.record("GridTabSwitcher.DropTabToMerge");
+                onTabMergeToGroup(mSelectedTabIndex, mHoveredTabIndex);
+                mRecyclerView.getAdapter().notifyDataSetChanged();
+            }
+            if (mHoveredTabIndex == -1) {
+                mModel.updateSelectedTabForMergeToGroup(mSelectedTabIndex, false);
+            }
+            mHoveredTabIndex = -1;
+            mSelectedTabIndex = -1;
+        }
+    }
+
+    @Override
+    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
+            float dX, float dY, int actionState, boolean isCurrentlyActive) {
+        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
+        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
+            float alpha = Math.max(0.2f, 1f - 0.8f * Math.abs(dX) / mSwipeToDismissThreshold);
+            int index = mModel.indexFromId(((TabGridViewHolder) viewHolder).getTabId());
+            if (index == -1) return;
+
+            mModel.get(index).set(TabProperties.ALPHA, alpha);
+        } else if (actionState == ItemTouchHelper.ACTION_STATE_DRAG && mActionsOnAllRelatedTabs) {
+            int prev_hovered = mHoveredTabIndex;
+            mHoveredTabIndex = TabListRecyclerView.getHoveredTabIndex(
+                    recyclerView, viewHolder.itemView, dX, dY, mMergeThreshold);
+            mModel.updateHoveredTabForMergeToGroup(mHoveredTabIndex, true);
+            if (prev_hovered != mHoveredTabIndex) {
+                mModel.updateHoveredTabForMergeToGroup(prev_hovered, false);
+            }
+        }
+    }
+
+    @Override
+    public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
+        return mSwipeToDismissThreshold / viewHolder.itemView.getWidth();
+    }
+
+    private List<Tab> getRelatedTabsForId(int id) {
+        return mTabModelSelector.getTabModelFilterProvider()
+                .getCurrentTabModelFilter()
+                .getRelatedTabList(id);
+    }
+
+    private void onTabMergeToGroup(int selectedCardIndex, int hoveredCardIndex) {
+        mModel.updateHoveredTabForMergeToGroup(hoveredCardIndex, false);
+        TabGroupModelFilter filter =
+                (TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider()
+                        .getCurrentTabModelFilter();
+        filter.mergeTabsToGroup(filter.getTabAt(selectedCardIndex).getId(),
+                filter.getTabAt(hoveredCardIndex).getId());
+        int destinationIndex =
+                selectedCardIndex > hoveredCardIndex ? hoveredCardIndex : hoveredCardIndex - 1;
+        Tab newSelectedTab = filter.getTabAt(destinationIndex);
+        boolean isSelected = newSelectedTab.getId() == mTabModelSelector.getCurrentTabId();
+        mUpdateTabHandler.updateTabInfo(destinationIndex, newSelectedTab, isSelected);
+    }
+
+    @VisibleForTesting
+    void setActionsOnAllRelatedTabsForTest(boolean flag) {
+        mActionsOnAllRelatedTabs = flag;
+    }
+
+    @VisibleForTesting
+    void setHoveredTabIndexForTest(int index) {
+        mHoveredTabIndex = index;
+    }
+
+    @VisibleForTesting
+    void setSelectedTabIndexForTest(int index) {
+        mSelectedTabIndex = index;
+    }
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
index 2340e8c2..a055db2 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
@@ -14,6 +14,7 @@
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.view.View;
 
+import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.ui.modelutil.PropertyKey;
@@ -58,19 +59,12 @@
                 holder.itemView.setForeground(
                         item.get(TabProperties.IS_SELECTED) ? drawable : null);
             }
+        } else if (TabProperties.IS_HIDDEN == propertyKey) {
+            updateThumbnail(holder, item);
         } else if (TabProperties.FAVICON == propertyKey) {
             holder.favicon.setImageDrawable(item.get(TabProperties.FAVICON));
         } else if (TabProperties.THUMBNAIL_FETCHER == propertyKey) {
-            TabListMediator.ThumbnailFetcher fetcher = item.get(TabProperties.THUMBNAIL_FETCHER);
-            if (fetcher == null) return;
-            Callback<Bitmap> callback = result -> {
-                if (result == null) {
-                    holder.resetThumbnail();
-                } else {
-                    holder.thumbnail.setImageBitmap(result);
-                }
-            };
-            fetcher.fetch(callback);
+            updateThumbnail(holder, item);
         } else if (TabProperties.TAB_ID == propertyKey) {
             holder.setTabId(item.get(TabProperties.TAB_ID));
         }
@@ -118,11 +112,53 @@
         } else if (TabProperties.IPH_PROVIDER == propertyKey) {
             TabListMediator.IphProvider provider = item.get(TabProperties.IPH_PROVIDER);
             if (provider != null) provider.showIPH(holder.thumbnail);
+        } else if (TabProperties.CARD_ANIMATION_STATUS == propertyKey) {
+            TabListRecyclerView.scaleTabGridCardView(
+                    holder.itemView, item.get(TabProperties.CARD_ANIMATION_STATUS));
         }
     }
 
     private static void onBindSelectableTab(
             TabGridViewHolder holder, PropertyModel item, PropertyKey propertyKey) {
-        // TODO(meiliang): Bind SELECTABLE_TAB properties
+        assert holder instanceof SelectableTabGridViewHolder;
+
+        SelectableTabGridViewHolder selectionHolder = (SelectableTabGridViewHolder) holder;
+        if (TabProperties.IS_SELECTED == propertyKey) {
+            boolean isSelected = item.get(TabProperties.IS_SELECTED);
+            selectionHolder.actionButton.getBackground().setLevel(
+                    isSelected ? selectionHolder.selectedLevel : selectionHolder.defaultLevel);
+            selectionHolder.actionButton.setImageDrawable(
+                    isSelected ? selectionHolder.mCheckDrawable : null);
+            ApiCompatibilityUtils.setImageTintList(selectionHolder.actionButton,
+                    isSelected ? selectionHolder.iconColorList : null);
+            if (isSelected) selectionHolder.mCheckDrawable.start();
+        } else if (TabProperties.SELECTABLE_TAB_CLICKED_LISTENER == propertyKey) {
+            selectionHolder.itemView.setOnClickListener(view -> {
+                item.get(TabProperties.SELECTABLE_TAB_CLICKED_LISTENER).run(holder.getTabId());
+            });
+        } else if (TabProperties.TITLE == propertyKey) {
+            String title = item.get(TabProperties.TITLE);
+            holder.actionButton.setContentDescription(holder.itemView.getResources().getString(
+                    org.chromium.chrome.R.string.accessibility_tabstrip_btn_close_tab, title));
+        }
+    }
+
+    private static void updateThumbnail(TabGridViewHolder holder, PropertyModel item) {
+        if (item.get(TabProperties.IS_HIDDEN)) {
+            // Release the thumbnail to save memory.
+            holder.resetThumbnail();
+            return;
+        }
+
+        TabListMediator.ThumbnailFetcher fetcher = item.get(TabProperties.THUMBNAIL_FETCHER);
+        if (fetcher == null) return;
+        Callback<Bitmap> callback = result -> {
+            if (result == null) {
+                holder.resetThumbnail();
+            } else {
+                holder.thumbnail.setImageBitmap(result);
+            }
+        };
+        fetcher.fetch(callback);
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java
index d3141fc3..597c6d6 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java
@@ -60,8 +60,7 @@
         if (itemViewType == TabGridViewItemType.CLOSABLE_TAB) {
             return new ClosableTabGridViewHolder(view);
         } else {
-            // TODO(meiliang): Return SelectableTabGridViewHolder
-            return new TabGridViewHolder(view);
+            return new SelectableTabGridViewHolder(view);
         }
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
index 73e27002..3b518212 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -22,6 +22,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupUtils;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.ui.modelutil.PropertyKey;
@@ -77,7 +78,7 @@
      */
     TabListCoordinator(@TabListMode int mode, Context context, TabModelSelector tabModelSelector,
             @Nullable TabListMediator.ThumbnailProvider thumbnailProvider,
-            @Nullable TabListMediator.TitleProvider titleProvider, boolean closeRelatedTabs,
+            @Nullable TabListMediator.TitleProvider titleProvider, boolean actionOnRelatedTabs,
             @Nullable TabListMediator.CreateGroupButtonProvider createGroupButtonProvider,
             @Nullable TabListMediator
                     .GridCardOnClickListenerProvider gridCardOnClickListenerProvider,
@@ -140,18 +141,21 @@
                 new TabListFaviconProvider(context, Profile.getLastUsedProfile());
 
         mMediator = new TabListMediator(tabListModel, tabModelSelector, thumbnailProvider,
-                titleProvider, tabListFaviconProvider, closeRelatedTabs, createGroupButtonProvider,
-                gridCardOnClickListenerProvider, componentName);
+                titleProvider, tabListFaviconProvider, actionOnRelatedTabs,
+                createGroupButtonProvider, gridCardOnClickListenerProvider, componentName);
 
         if (mMode == TabListMode.GRID) {
             ItemTouchHelper touchHelper = new ItemTouchHelper(mMediator.getItemTouchHelperCallback(
-                    context.getResources().getDimension(R.dimen.swipe_to_dismiss_threshold)));
+                    context.getResources().getDimension(R.dimen.swipe_to_dismiss_threshold),
+                    context.getResources().getDimension(R.dimen.tab_grid_merge_threshold),
+                    !FeatureUtilities.isTabGroupsAndroidEnabled()
+                            || FeatureUtilities.isTabGroupsAndroidUiImprovementsEnabled()));
             touchHelper.attachToRecyclerView(mRecyclerView);
             mMediator.registerOrientationListener(
                     (GridLayoutManager) mRecyclerView.getLayoutManager());
         }
 
-        if (closeRelatedTabs) {
+        if (actionOnRelatedTabs) {
             // Only do this for Grid Tab Switcher.
             // TODO(crbug.com/964406): unregister the listener when we don't need it.
             mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(
@@ -192,6 +196,10 @@
         return mMediator.resetWithListOfTabs(tabs);
     }
 
+    void softCleanup() {
+        mMediator.softCleanup();
+    }
+
     void prepareOverview() {
         mRecyclerView.prepareOverview();
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index edbf6234..13aa911 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -7,20 +7,18 @@
 import android.content.ComponentCallbacks;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.support.v7.widget.GridLayoutManager;
-import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.helper.ItemTouchHelper;
+import android.util.Pair;
 import android.view.View;
 
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
-import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.native_page.NativePageFactory;
@@ -31,14 +29,12 @@
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabList;
 import org.chromium.chrome.browser.tabmodel.TabModel;
-import org.chromium.chrome.browser.tabmodel.TabModelFilter;
 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.tabmodel.TabSelectionType;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupUtils;
 import org.chromium.chrome.browser.tasks.tabgroup.TabGroupModelFilter;
-import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.content_public.browser.NavigationHandle;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -160,8 +156,9 @@
     private final CreateGroupButtonProvider mCreateGroupButtonProvider;
     private final GridCardOnClickListenerProvider mGridCardOnClickListenerProvider;
     private final String mComponentName;
-    private boolean mCloseAllRelatedTabs;
+    private boolean mActionsOnAllRelatedTabs;
     private ComponentCallbacks mComponentCallbacks;
+    private TabGridItemTouchHelperCallback mTabGridItemTouchHelperCallback;
 
     private final TabActionListener mTabSelectedListener = new TabActionListener() {
         @Override
@@ -203,6 +200,16 @@
         }
     };
 
+    private final TabActionListener mSelectableTabOnClickListener = new TabActionListener() {
+        @Override
+        public void run(int tabId) {
+            int index = mModel.indexFromId(tabId);
+            if (index == TabModel.INVALID_TAB_INDEX) return;
+            boolean selected = mModel.get(index).get(TabProperties.IS_SELECTED);
+            mModel.get(index).set(TabProperties.IS_SELECTED, !selected);
+        }
+    };
+
     private final TabObserver mTabObserver = new EmptyTabObserver() {
         @Override
         public void onDidStartNavigation(Tab tab, NavigationHandle navigationHandle) {
@@ -249,8 +256,8 @@
      * @param thumbnailProvider {@link ThumbnailProvider} to provide screenshot related details.
      * @param titleProvider {@link TitleProvider} for a given tab's title to show.
      * @param tabListFaviconProvider Provider for all favicon related drawables.
-     * @param closeRelatedTabs Whether all related tabs should be closed in {@link
-     *         TabProperties#TAB_CLOSED_LISTENER}.
+     * @param actionOnRelatedTabs Whether tab-related actions should be operated on all related
+     *         tabs.
      * @param createGroupButtonProvider {@link CreateGroupButtonProvider} to provide "Create group"
      *                                   button information. It's null when "Create group" is not
      *                                   possible.
@@ -258,7 +265,7 @@
      */
     public TabListMediator(TabListModel model, TabModelSelector tabModelSelector,
             @Nullable ThumbnailProvider thumbnailProvider, @Nullable TitleProvider titleProvider,
-            TabListFaviconProvider tabListFaviconProvider, boolean closeRelatedTabs,
+            TabListFaviconProvider tabListFaviconProvider, boolean actionOnRelatedTabs,
             @Nullable CreateGroupButtonProvider createGroupButtonProvider,
             @Nullable GridCardOnClickListenerProvider gridCardOnClickListenerProvider,
             String componentName) {
@@ -270,7 +277,7 @@
         mTitleProvider = titleProvider != null ? titleProvider : Tab::getTitle;
         mCreateGroupButtonProvider = createGroupButtonProvider;
         mGridCardOnClickListenerProvider = gridCardOnClickListenerProvider;
-        mCloseAllRelatedTabs = closeRelatedTabs;
+        mActionsOnAllRelatedTabs = actionOnRelatedTabs;
 
         mTabModelObserver = new EmptyTabModelObserver() {
             @Override
@@ -288,7 +295,7 @@
 
             @Override
             public void tabClosureUndone(Tab tab) {
-                onTabAdded(tab, !mCloseAllRelatedTabs);
+                onTabAdded(tab, !mActionsOnAllRelatedTabs);
                 if (sTabClosedFromMapTabClosedFromMap.containsKey(tab.getId())) {
                     @TabClosedFrom
                     int from = sTabClosedFromMapTabClosedFromMap.get(tab.getId());
@@ -315,7 +322,7 @@
 
             @Override
             public void didAddTab(Tab tab, int type) {
-                onTabAdded(tab, !mCloseAllRelatedTabs);
+                onTabAdded(tab, !mActionsOnAllRelatedTabs);
             }
 
             @Override
@@ -327,10 +334,9 @@
             @Override
             public void didMoveTab(Tab tab, int newIndex, int curIndex) {
                 if (mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter()
-                                instanceof TabGroupModelFilter) {
+                                instanceof TabGroupModelFilter)
                     return;
-                }
-                onTabMoved(tab, newIndex, curIndex);
+                onTabMoved(newIndex, curIndex);
             }
         };
 
@@ -340,9 +346,6 @@
                         instanceof TabGroupModelFilter) {
             mTabGroupObserver = new TabGroupModelFilter.Observer() {
                 @Override
-                public void didMergeTabToGroup(Tab movedTab, int selectedTabIdInGroup) {}
-
-                @Override
                 public void didMoveWithinGroup(
                         Tab movedTab, int tabModelOldIndex, int tabModelNewIndex) {
                     int curPosition = mModel.indexFromId(movedTab.getId());
@@ -360,13 +363,36 @@
                 }
 
                 @Override
+                public void didMergeTabToGroup(Tab movedTab, int selectedTabIdInGroup) {
+                    if (!mActionsOnAllRelatedTabs) return;
+                    Pair<Integer, Integer> positions =
+                            mModel.getIndexesForMergeToGroup(mTabModelSelector.getCurrentModel(),
+                                    getRelatedTabsForId(movedTab.getId()));
+                    int srcIndex = positions.second;
+                    int desIndex = positions.first;
+
+                    if (!isValidMovePosition(srcIndex) || !isValidMovePosition(desIndex)) return;
+                    mModel.removeAt(srcIndex);
+                }
+
+                @Override
                 public void didMoveTabGroup(
                         Tab movedTab, int tabModelOldIndex, int tabModelNewIndex) {
+                    if (!mActionsOnAllRelatedTabs) return;
+                    TabGroupModelFilter filter =
+                            (TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider()
+                                    .getCurrentTabModelFilter();
                     List<Tab> relatedTabs = getRelatedTabsForId(movedTab.getId());
                     Tab currentGroupSelectedTab =
                             TabGroupUtils.getSelectedTabInGroupForTab(mTabModelSelector, movedTab);
                     TabModel tabModel = mTabModelSelector.getCurrentModel();
                     int curPosition = mModel.indexFromId(currentGroupSelectedTab.getId());
+                    if (curPosition == TabModel.INVALID_TAB_INDEX) {
+                        // Sync TabListModel with updated TabGroupModelFilter.
+                        int indexToUpdate = filter.indexOf(tabModel.getTabAt(tabModelOldIndex));
+                        mModel.updateTabListModelIdForGroup(currentGroupSelectedTab, indexToUpdate);
+                        curPosition = mModel.indexFromId(currentGroupSelectedTab.getId());
+                    }
                     if (!isValidMovePosition(curPosition)) return;
 
                     // Find the tab which was in the destination index before this move. Use that
@@ -378,6 +404,13 @@
                     Tab destinationGroupSelectedTab = TabGroupUtils.getSelectedTabInGroupForTab(
                             mTabModelSelector, destinationTab);
                     int newPosition = mModel.indexFromId(destinationGroupSelectedTab.getId());
+                    if (newPosition == TabModel.INVALID_TAB_INDEX) {
+                        int indexToUpdate = filter.indexOf(destinationTab)
+                                + (tabModelNewIndex > tabModelOldIndex ? 1 : -1);
+                        mModel.updateTabListModelIdForGroup(
+                                destinationGroupSelectedTab, indexToUpdate);
+                        newPosition = mModel.indexFromId(destinationGroupSelectedTab.getId());
+                    }
                     if (!isValidMovePosition(newPosition)) return;
 
                     mModel.move(curPosition, newPosition);
@@ -399,7 +432,7 @@
             public void run(int tabId) {
                 RecordUserAction.record("MobileTabClosed." + mComponentName);
 
-                if (mCloseAllRelatedTabs) {
+                if (mActionsOnAllRelatedTabs) {
                     List<Tab> related = getRelatedTabsForId(tabId);
                     if (related.size() > 1) {
                         onGroupClosedFrom(tabId);
@@ -436,6 +469,10 @@
                 return TabModelUtils.getTabById(mTabModelSelector.getCurrentModel(), nextTabId);
             }
         };
+
+        mTabGridItemTouchHelperCallback =
+                new TabGridItemTouchHelperCallback(mModel, mTabModelSelector, mTabClosedListener,
+                        this::updateTab, mComponentName, mActionsOnAllRelatedTabs);
     }
 
     private void onTabClosedFrom(int tabId, String fromComponent) {
@@ -458,8 +495,8 @@
         sTabClosedFromMapTabClosedFromMap.put(tabId, TabClosedFrom.GRID_TAB_SWITCHER_GROUP);
     }
 
-    public void setCloseAllRelatedTabsForTest(boolean closeAllRelatedTabs) {
-        mCloseAllRelatedTabs = closeAllRelatedTabs;
+    public void setActionOnAllRelatedTabsForTest(boolean actionOnAllRelatedTabs) {
+        mActionsOnAllRelatedTabs = actionOnAllRelatedTabs;
     }
 
     private List<Tab> getRelatedTabsForId(int id) {
@@ -485,7 +522,7 @@
         addTabInfoToModel(tab, index, mTabModelSelector.getCurrentTab() == tab);
     }
 
-    private void onTabMoved(Tab tab, int newIndex, int curIndex) {
+    private void onTabMoved(int newIndex, int curIndex) {
         // Handle move without groups enabled.
         if (mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter()
                         instanceof EmptyTabModelFilter) {
@@ -521,6 +558,8 @@
             for (int i = 0; i < tabs.size(); i++) {
                 Tab tab = tabs.get(i);
 
+                mModel.get(i).set(TabProperties.IS_HIDDEN, false);
+
                 boolean isSelected = mTabModelSelector.getCurrentTab() == tab;
                 mModel.get(i).set(TabProperties.IS_SELECTED, isSelected);
 
@@ -548,85 +587,40 @@
     }
 
     /**
+     * @see GridTabSwitcherMediator.ResetHandler#softCleanup
+     */
+    void softCleanup() {
+        for (int i = 0; i < mModel.size(); i++) {
+            mModel.get(i).set(TabProperties.IS_HIDDEN, true);
+        }
+    }
+
+    private void updateTab(int index, Tab tab, boolean isSelected) {
+        if (index < 0 || index >= mModel.size()) return;
+        mModel.get(index).set(TabProperties.TAB_ID, tab.getId());
+        TabActionListener tabSelectedListener;
+        if (mGridCardOnClickListenerProvider == null
+                || getRelatedTabsForId(tab.getId()).size() == 1) {
+            tabSelectedListener = mTabSelectedListener;
+        } else {
+            tabSelectedListener = mGridCardOnClickListenerProvider.getGridCardOnClickListener(tab);
+        }
+        mModel.get(index).set(TabProperties.TAB_SELECTED_LISTENER, tabSelectedListener);
+        mModel.get(index).set(TabProperties.CREATE_GROUP_LISTENER,
+                isSelected ? mCreateGroupButtonProvider.getCreateGroupButtonOnClickListener(tab)
+                           : null);
+        mModel.get(index).set(TabProperties.IS_SELECTED, isSelected);
+        mModel.get(index).set(TabProperties.TITLE, mTitleProvider.getTitle(tab));
+    }
+
+    /**
      * @return The callback that hosts the logic for swipe and drag related actions.
      */
-    ItemTouchHelper.SimpleCallback getItemTouchHelperCallback(final float swipeToDismissThreshold) {
-        return new ItemTouchHelper.SimpleCallback(0, 0) {
-            @Override
-            public int getMovementFlags(
-                    RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
-                final int dragFlags = FeatureUtilities.isTabGroupsAndroidEnabled()
-                                && !FeatureUtilities.isTabGroupsAndroidUiImprovementsEnabled()
-                        ? 0
-                        : ItemTouchHelper.START | ItemTouchHelper.END | ItemTouchHelper.UP
-                                | ItemTouchHelper.DOWN;
-                final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
-                return makeMovementFlags(dragFlags, swipeFlags);
-            }
-
-            @Override
-            public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder fromViewHolder,
-                    RecyclerView.ViewHolder toViewHolder) {
-                assert fromViewHolder instanceof TabGridViewHolder;
-                assert toViewHolder instanceof TabGridViewHolder;
-
-                int currentTabId = ((TabGridViewHolder) fromViewHolder).getTabId();
-                int destinationTabId = ((TabGridViewHolder) toViewHolder).getTabId();
-                int distance =
-                        toViewHolder.getAdapterPosition() - fromViewHolder.getAdapterPosition();
-                TabModelFilter filter =
-                        mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter();
-                TabModel tabModel = mTabModelSelector.getCurrentModel();
-                if (filter instanceof EmptyTabModelFilter) {
-                    tabModel.moveTab(currentTabId,
-                            mModel.indexFromId(currentTabId)
-                                    + (distance > 0 ? distance + 1 : distance));
-                } else if (!mCloseAllRelatedTabs) {
-                    int destinationIndex =
-                            tabModel.indexOf(mTabModelSelector.getTabById(destinationTabId));
-                    tabModel.moveTab(
-                            currentTabId, distance > 0 ? destinationIndex + 1 : destinationIndex);
-                } else {
-                    List<Tab> destinationTabGroup = getRelatedTabsForId(destinationTabId);
-                    int newIndex = distance >= 0 ? TabGroupUtils.getLastTabModelIndexForList(
-                                                           mTabModelSelector, destinationTabGroup)
-                                    + 1
-                                                 : TabGroupUtils.getFirstTabModelIndexForList(
-                                                         mTabModelSelector, destinationTabGroup);
-                    ((TabGroupModelFilter) filter).moveRelatedTabs(currentTabId, newIndex);
-                }
-                return true;
-            }
-
-            @Override
-            public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
-                assert viewHolder instanceof TabGridViewHolder;
-
-                mTabClosedListener.run(((TabGridViewHolder) viewHolder).getTabId());
-                RecordUserAction.record("MobileStackViewSwipeCloseTab." + mComponentName);
-            }
-
-            @Override
-            public void onChildDraw(Canvas c, RecyclerView recyclerView,
-                    RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState,
-                    boolean isCurrentlyActive) {
-                super.onChildDraw(
-                        c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
-                if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
-                    float alpha =
-                            Math.max(0.2f, 1f - 0.8f * Math.abs(dX) / swipeToDismissThreshold);
-                    int index = mModel.indexFromId(((TabGridViewHolder) viewHolder).getTabId());
-                    if (index == -1) return;
-
-                    mModel.get(index).set(TabProperties.ALPHA, alpha);
-                }
-            }
-
-            @Override
-            public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
-                return swipeToDismissThreshold / viewHolder.itemView.getWidth();
-            }
-        };
+    ItemTouchHelper.SimpleCallback getItemTouchHelperCallback(final float swipeToDismissThreshold,
+            final float mergeThreshold, final boolean isDragEnabled) {
+        mTabGridItemTouchHelperCallback.setupCallback(
+                swipeToDismissThreshold, mergeThreshold, isDragEnabled);
+        return mTabGridItemTouchHelperCallback;
     }
 
     void registerOrientationListener(GridLayoutManager manager) {
@@ -646,11 +640,6 @@
         ContextUtils.getApplicationContext().registerComponentCallbacks(mComponentCallbacks);
     }
 
-    @VisibleForTesting
-    void setCloseAllRelatedTabs(boolean flag) {
-        mCloseAllRelatedTabs = flag;
-    }
-
     /**
      * Destroy any members that needs clean up.
      */
@@ -680,7 +669,7 @@
 
     private void addTabInfoToModel(final Tab tab, int index, boolean isSelected) {
         boolean showIPH = false;
-        if (mCloseAllRelatedTabs && !mShownIPH) {
+        if (mActionsOnAllRelatedTabs && !mShownIPH) {
             showIPH = getRelatedTabsForId(tab.getId()).size() > 1;
         }
         TabActionListener tabSelectedListener;
@@ -698,12 +687,17 @@
                         .with(TabProperties.FAVICON,
                                 mTabListFaviconProvider.getDefaultFaviconDrawable())
                         .with(TabProperties.IS_SELECTED, isSelected)
+                        .with(TabProperties.IS_HIDDEN, false)
                         .with(TabProperties.IPH_PROVIDER, showIPH ? mIphProvider : null)
                         .with(TabProperties.TAB_SELECTED_LISTENER, tabSelectedListener)
                         .with(TabProperties.TAB_CLOSED_LISTENER, mTabClosedListener)
                         .with(TabProperties.CREATE_GROUP_LISTENER,
                                 getCreateGroupButtonListener(tab, isSelected))
                         .with(TabProperties.ALPHA, 1f)
+                        .with(TabProperties.CARD_ANIMATION_STATUS,
+                                TabListRecyclerView.ANIMATION_STATUS_RESTORE)
+                        .with(TabProperties.SELECTABLE_TAB_CLICKED_LISTENER,
+                                mSelectableTabOnClickListener)
                         .build();
 
         if (index >= mModel.size()) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java
index 513265c..17dde61 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java
@@ -6,11 +6,16 @@
 
 import static org.chromium.chrome.browser.tasks.tab_management.TabProperties.TAB_ID;
 
+import android.util.Pair;
+
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyListModel;
 import org.chromium.ui.modelutil.PropertyModel;
 
+import java.util.List;
+
 /**
  * A {@link PropertyListModel} implementation to keep information about a list of
  * {@link org.chromium.chrome.browser.tab.Tab}s.
@@ -27,4 +32,77 @@
         }
         return TabModel.INVALID_TAB_INDEX;
     }
+
+    /**
+     * Sync the {@link TabListModel} with updated information. Update tab id of
+     * the item in {@code index} with the current selected {@code tab} of the group.
+     * @param selectedTab   The current selected tab in the group.
+     * @param index         The index of the item in {@link TabListModel} that needs to be updated.
+     */
+    void updateTabListModelIdForGroup(Tab selectedTab, int index) {
+        get(index).set(TabProperties.TAB_ID, selectedTab.getId());
+    }
+
+    /**
+     * This method gets indexes in the {@link TabListModel} of the two tabs that are merged into one
+     * group.
+     * @param tabModel   The tabModel that owns the tabs.
+     * @param tabs       The list that contains tabs of the newly merged group.
+     * @return A Pair with its first member as the index of the tab that is selected to merge and
+     * the second member as the index of the tab that is being merged into.
+     */
+    Pair<Integer, Integer> getIndexesForMergeToGroup(TabModel tabModel, List<Tab> tabs) {
+        int desIndex = TabModel.INVALID_TAB_INDEX;
+        int srcIndex = TabModel.INVALID_TAB_INDEX;
+        int lastTabModelIndex = tabModel.indexOf(tabs.get(tabs.size() - 1));
+        for (int i = lastTabModelIndex; i >= 0; i--) {
+            Tab curTab = tabModel.getTabAt(i);
+            if (!tabs.contains(curTab)) break;
+            int index = indexFromId(curTab.getId());
+            if (index != TabModel.INVALID_TAB_INDEX && srcIndex == TabModel.INVALID_TAB_INDEX) {
+                srcIndex = index;
+            } else if (index != TabModel.INVALID_TAB_INDEX
+                    && desIndex == TabModel.INVALID_TAB_INDEX) {
+                desIndex = index;
+            }
+        }
+        return new Pair<>(desIndex, srcIndex);
+    }
+
+    /**
+     * This method updates the information in {@link TabListModel} of the selected tab when a merge
+     * related operation happens.
+     * @param index         The index of the item in {@link TabListModel} that needs to be updated.
+     * @param isSelected    Whether the tab is selected or not in a merge related operation. If
+     *         selected, update the corresponding item in {@link TabListModel} to the selected
+     *         state. If not, restore it to original state.
+     */
+    void updateSelectedTabForMergeToGroup(int index, boolean isSelected) {
+        int status = isSelected ? TabListRecyclerView.ANIMATION_STATUS_ZOOM_IN
+                                : TabListRecyclerView.ANIMATION_STATUS_ZOOM_OUT;
+        if (index < 0 || index >= size()
+                || get(index).get(TabProperties.CARD_ANIMATION_STATUS) == status)
+            return;
+
+        get(index).set(TabProperties.CARD_ANIMATION_STATUS, status);
+        get(index).set(TabProperties.ALPHA, isSelected ? 0.8f : 1f);
+    }
+
+    /**
+     * This method updates the information in {@link TabListModel} of the hovered tab when a merge
+     * related operation happens.
+     * @param index         The index of the item in {@link TabListModel} that needs to be updated.
+     * @param isHovered     Whether the tab is hovered or not in a merge related operation. If
+     *         hovered, update the corresponding item in {@link TabListModel} to the hovered state.
+     *         If not, restore it to original state.
+     */
+    void updateHoveredTabForMergeToGroup(int index, boolean isHovered) {
+        int status = isHovered ? TabListRecyclerView.ANIMATION_STATUS_ZOOM_IN
+                               : TabListRecyclerView.ANIMATION_STATUS_ZOOM_OUT;
+        if (index < 0 || index >= size()
+                || get(index).get(TabProperties.CARD_ANIMATION_STATUS) == status)
+            return;
+
+        get(index).set(TabProperties.CARD_ANIMATION_STATUS, status);
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
index de1dffc..0ab2c6c 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
@@ -6,6 +6,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
@@ -28,6 +29,10 @@
  */
 class TabListRecyclerView extends RecyclerView {
     public static final long BASE_ANIMATION_DURATION_MS = 218;
+    public static final long RESTORE_ANIMATION_DURATION_MS = 10;
+    public static final int ANIMATION_STATUS_RESTORE = 0;
+    public static final int ANIMATION_STATUS_ZOOM_OUT = 1;
+    public static final int ANIMATION_STATUS_ZOOM_IN = 2;
 
     /**
      * Field trial parameter for downsampling scaling factor.
@@ -235,4 +240,46 @@
         rect.bottom -= loc[1];
         return rect;
     }
+
+    static void scaleTabGridCardView(View view, int status) {
+        AnimatorSet scaleAnimator = new AnimatorSet();
+        float scale = status == ANIMATION_STATUS_ZOOM_IN ? 0.8f : 1f;
+        ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", scale);
+        ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", scale);
+        scaleX.setDuration(status == ANIMATION_STATUS_RESTORE ? RESTORE_ANIMATION_DURATION_MS
+                                                              : BASE_ANIMATION_DURATION_MS);
+        scaleY.setDuration(status == ANIMATION_STATUS_RESTORE ? RESTORE_ANIMATION_DURATION_MS
+                                                              : BASE_ANIMATION_DURATION_MS);
+        scaleAnimator.play(scaleX).with(scaleY);
+        scaleAnimator.start();
+    }
+
+    /**
+     * This method finds out the index of the hovered tab's viewHolder in {@code recyclerView}.
+     * @param recyclerView   The recyclerview that owns the tabs' viewHolders.
+     * @param view           The view of the selected tab.
+     * @param dX             The X offset of the selected tab.
+     * @param dY             The Y offset of the selected tab.
+     * @param threshold      The threshold to judge whether two tabs are overlapped.
+     * @return The index of the hovered tab.
+     */
+    static int getHoveredTabIndex(
+            RecyclerView recyclerView, View view, float dX, float dY, float threshold) {
+        for (int i = 0; i < recyclerView.getChildCount(); i++) {
+            View child = recyclerView.getChildAt(i);
+            if (child.getLeft() == view.getLeft() && child.getTop() == view.getTop()) {
+                continue;
+            }
+            if (isOverlap(child.getLeft(), child.getTop(), view.getLeft() + dX, view.getTop() + dY,
+                        threshold)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private static boolean isOverlap(
+            float left1, float top1, float left2, float top2, float threshold) {
+        return Math.abs(left1 - left2) < threshold && Math.abs(top1 - top2) < threshold;
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java
index a6f68238..96b6cda 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java
@@ -8,7 +8,6 @@
 
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
-import org.chromium.ui.modelutil.PropertyModel.ReadableIntPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
@@ -16,7 +15,8 @@
  * List of properties to designate information about a single tab.
  */
 public class TabProperties {
-    public static final PropertyModel.ReadableIntPropertyKey TAB_ID = new ReadableIntPropertyKey();
+    public static final PropertyModel.WritableIntPropertyKey TAB_ID =
+            new PropertyModel.WritableIntPropertyKey();
 
     public static final WritableObjectPropertyKey<TabListMediator.TabActionListener>
             TAB_SELECTED_LISTENER = new WritableObjectPropertyKey<>();
@@ -37,15 +37,24 @@
 
     public static final WritableBooleanPropertyKey IS_SELECTED = new WritableBooleanPropertyKey();
 
+    public static final WritableBooleanPropertyKey IS_HIDDEN = new WritableBooleanPropertyKey();
+
     public static final WritableObjectPropertyKey<TabListMediator.TabActionListener>
             CREATE_GROUP_LISTENER = new WritableObjectPropertyKey<>();
 
     public static final PropertyModel.WritableFloatPropertyKey ALPHA =
             new PropertyModel.WritableFloatPropertyKey();
 
+    public static final PropertyModel.WritableIntPropertyKey CARD_ANIMATION_STATUS =
+            new PropertyModel.WritableIntPropertyKey();
+
+    public static final PropertyModel.WritableObjectPropertyKey<TabListMediator.TabActionListener>
+            SELECTABLE_TAB_CLICKED_LISTENER = new PropertyModel.WritableObjectPropertyKey<>();
+
     public static final PropertyKey[] ALL_KEYS_TAB_GRID = new PropertyKey[] {TAB_ID,
             TAB_SELECTED_LISTENER, TAB_CLOSED_LISTENER, FAVICON, THUMBNAIL_FETCHER, IPH_PROVIDER,
-            TITLE, IS_SELECTED, CREATE_GROUP_LISTENER, ALPHA};
+            TITLE, IS_SELECTED, IS_HIDDEN, CREATE_GROUP_LISTENER, ALPHA, CARD_ANIMATION_STATUS,
+            SELECTABLE_TAB_CLICKED_LISTENER};
 
     public static final PropertyKey[] ALL_KEYS_TAB_STRIP = new PropertyKey[] {
             TAB_ID, TAB_SELECTED_LISTENER, TAB_CLOSED_LISTENER, FAVICON, IS_SELECTED, TITLE};
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
index 8c6c3d4..3519a0d 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
@@ -34,6 +34,7 @@
 
 import java.lang.ref.WeakReference;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Tests for the {@link android.support.v7.widget.RecyclerView.ViewHolder} classes for {@link
@@ -49,6 +50,10 @@
     private PropertyModel mStripModel;
     private PropertyModelChangeProcessor mStripMCP;
 
+    private TabGridViewHolder mSelectableTabGridViewHolder;
+    private PropertyModel mSelectableModel;
+    private PropertyModelChangeProcessor mSelectableMCP;
+
     private TabListMediator.ThumbnailFetcher mMockThumbnailProvider =
             new TabListMediator.ThumbnailFetcher(new TabListMediator.ThumbnailProvider() {
                 @Override
@@ -58,8 +63,10 @@
                             ? Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
                             : null;
                     callback.onResult(bitmap);
+                    mThumbnailFetchedCount.incrementAndGet();
                 }
             }, null, false);
+    private AtomicInteger mThumbnailFetchedCount = new AtomicInteger();
 
     private TabListMediator.TabActionListener mMockCloseListener =
             new TabListMediator.TabActionListener() {
@@ -90,11 +97,15 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             getActivity().setContentView(view, params);
 
-            mTabGridViewHolder = TabGridViewHolder.create(view, 0);
+            mTabGridViewHolder = TabGridViewHolder.create(
+                    view, TabGridViewHolder.TabGridViewItemType.CLOSABLE_TAB);
             mTabStripViewHolder = TabStripViewHolder.create(view, 0);
+            mSelectableTabGridViewHolder = TabGridViewHolder.create(
+                    view, TabGridViewHolder.TabGridViewItemType.SELECTABLE_TAB);
 
             view.addView(mTabGridViewHolder.itemView);
             view.addView(mTabStripViewHolder.itemView);
+            view.addView(mSelectableTabGridViewHolder.itemView);
         });
 
         mGridModel = new PropertyModel.Builder(TabProperties.ALL_KEYS_TAB_GRID)
@@ -105,32 +116,34 @@
                               .with(TabProperties.TAB_SELECTED_LISTENER, mMockSelectedListener)
                               .with(TabProperties.TAB_CLOSED_LISTENER, mMockCloseListener)
                               .build();
+        mSelectableModel =
+                new PropertyModel.Builder(TabProperties.ALL_KEYS_TAB_GRID)
+                        .with(TabProperties.SELECTABLE_TAB_CLICKED_LISTENER, mMockSelectedListener)
+                        .build();
 
         mGridMCP = PropertyModelChangeProcessor.create(mGridModel, mTabGridViewHolder,
                 new TestRecyclerViewSimpleViewBinder<>(TabGridViewBinder::onBindViewHolder));
         mStripMCP = PropertyModelChangeProcessor.create(mStripModel, mTabStripViewHolder,
                 new TestRecyclerViewSimpleViewBinder<>(TabStripViewBinder::onBindViewHolder));
+        mSelectableMCP = PropertyModelChangeProcessor.create(mSelectableModel,
+                mSelectableTabGridViewHolder,
+                new TestRecyclerViewSimpleViewBinder<>(TabGridViewBinder::onBindViewHolder));
     }
 
-    @Test
-    @MediumTest
-    @UiThreadTest
-    public void testSelected() throws Exception {
+    private void testGridSelected(TabGridViewHolder holder, PropertyModel model) {
         if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
-            mGridModel.set(TabProperties.IS_SELECTED, true);
-            Assert.assertTrue(
-                    ((FrameLayout) (mTabGridViewHolder.itemView)).getForeground() != null);
-            mGridModel.set(TabProperties.IS_SELECTED, false);
-            Assert.assertFalse(
-                    ((FrameLayout) (mTabGridViewHolder.itemView)).getForeground() != null);
+            model.set(TabProperties.IS_SELECTED, true);
+            Assert.assertTrue(((FrameLayout) (holder.itemView)).getForeground() != null);
+            model.set(TabProperties.IS_SELECTED, false);
+            Assert.assertFalse(((FrameLayout) (holder.itemView)).getForeground() != null);
         } else {
-            mGridModel.set(TabProperties.IS_SELECTED, true);
+            model.set(TabProperties.IS_SELECTED, true);
             Drawable selectedDrawable =
-                    mTabGridViewHolder.itemView.findViewById(R.id.background_view).getBackground();
+                    holder.itemView.findViewById(R.id.background_view).getBackground();
             Assert.assertTrue(selectedDrawable != null);
-            mGridModel.set(TabProperties.IS_SELECTED, false);
+            model.set(TabProperties.IS_SELECTED, false);
             Drawable elevationDrawable =
-                    mTabGridViewHolder.itemView.findViewById(R.id.background_view).getBackground();
+                    holder.itemView.findViewById(R.id.background_view).getBackground();
             Assert.assertTrue(elevationDrawable != null);
             Assert.assertNotSame(selectedDrawable, elevationDrawable);
         }
@@ -143,10 +156,36 @@
     @Test
     @MediumTest
     @UiThreadTest
+    public void testSelected() throws Exception {
+        testGridSelected(mTabGridViewHolder, mGridModel);
+
+        mStripModel.set(TabProperties.IS_SELECTED, true);
+        Assert.assertTrue(((FrameLayout) (mTabStripViewHolder.itemView)).getForeground() != null);
+        mStripModel.set(TabProperties.IS_SELECTED, false);
+        Assert.assertFalse(((FrameLayout) (mTabStripViewHolder.itemView)).getForeground() != null);
+
+        testGridSelected(mSelectableTabGridViewHolder, mSelectableModel);
+        mSelectableModel.set(TabProperties.IS_SELECTED, true);
+        Assert.assertTrue(
+                mSelectableTabGridViewHolder.actionButton.getBackground().getLevel() == 1);
+        Assert.assertTrue(mSelectableTabGridViewHolder.actionButton.getDrawable() != null);
+
+        mSelectableModel.set(TabProperties.IS_SELECTED, false);
+        Assert.assertTrue(
+                mSelectableTabGridViewHolder.actionButton.getBackground().getLevel() == 0);
+        Assert.assertTrue(mSelectableTabGridViewHolder.actionButton.getDrawable() == null);
+    }
+
+    @Test
+    @MediumTest
+    @UiThreadTest
     public void testTitle() throws Exception {
         final String title = "Surf the cool webz";
         mGridModel.set(TabProperties.TITLE, title);
         Assert.assertEquals(mTabGridViewHolder.title.getText(), title);
+
+        mSelectableModel.set(TabProperties.TITLE, title);
+        Assert.assertEquals(mSelectableTabGridViewHolder.title.getText(), title);
     }
 
     @Test
@@ -160,6 +199,7 @@
         mShouldReturnBitmap = true;
         mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
         assertThat(mTabGridViewHolder.thumbnail.getDrawable(), instanceOf(BitmapDrawable.class));
+        Assert.assertEquals(2, mThumbnailFetchedCount.get());
     }
 
     @Test
@@ -179,6 +219,7 @@
         mShouldReturnBitmap = false;
         mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
         Assert.assertTrue(canBeGarbageCollected(ref));
+        Assert.assertEquals(2, mThumbnailFetchedCount.get());
     }
 
     @Test
@@ -197,6 +238,7 @@
 
         mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
         Assert.assertTrue(canBeGarbageCollected(ref));
+        Assert.assertEquals(2, mThumbnailFetchedCount.get());
     }
 
     @Test
@@ -219,6 +261,71 @@
     @Test
     @MediumTest
     @UiThreadTest
+    public void testHiddenGC() throws Exception {
+        mShouldReturnBitmap = true;
+        mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
+        assertThat(mTabGridViewHolder.thumbnail.getDrawable(), instanceOf(BitmapDrawable.class));
+        Bitmap bitmap = ((BitmapDrawable) mTabGridViewHolder.thumbnail.getDrawable()).getBitmap();
+        WeakReference<Bitmap> ref = new WeakReference<>(bitmap);
+        bitmap = null;
+
+        Assert.assertFalse(canBeGarbageCollected(ref));
+
+        mGridModel.set(TabProperties.IS_HIDDEN, true);
+        Assert.assertTrue(canBeGarbageCollected(ref));
+        Assert.assertNull(mTabGridViewHolder.thumbnail.getDrawable());
+        Assert.assertEquals(1, mThumbnailFetchedCount.get());
+    }
+
+    @Test
+    @MediumTest
+    @UiThreadTest
+    public void testHiddenThenShow() throws Exception {
+        mShouldReturnBitmap = true;
+        mGridModel.set(TabProperties.IS_HIDDEN, false);
+        mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
+        assertThat(mTabGridViewHolder.thumbnail.getDrawable(), instanceOf(BitmapDrawable.class));
+        Assert.assertEquals(1, mThumbnailFetchedCount.get());
+
+        mGridModel.set(TabProperties.IS_HIDDEN, true);
+        Assert.assertNull(mTabGridViewHolder.thumbnail.getDrawable());
+        Assert.assertEquals(1, mThumbnailFetchedCount.get());
+
+        mGridModel.set(TabProperties.IS_HIDDEN, false);
+        assertThat(mTabGridViewHolder.thumbnail.getDrawable(), instanceOf(BitmapDrawable.class));
+        Assert.assertEquals(2, mThumbnailFetchedCount.get());
+    }
+
+    @Test
+    @MediumTest
+    @UiThreadTest
+    public void testSkipFetchingWhenHidden() throws Exception {
+        mShouldReturnBitmap = true;
+        mGridModel.set(TabProperties.IS_HIDDEN, true);
+        mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
+        mGridModel.set(TabProperties.THUMBNAIL_FETCHER, null);
+        mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
+        mGridModel.set(TabProperties.THUMBNAIL_FETCHER, null);
+        mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
+        Assert.assertNull(mTabGridViewHolder.thumbnail.getDrawable());
+        Assert.assertEquals(0, mThumbnailFetchedCount.get());
+
+        mGridModel.set(TabProperties.IS_HIDDEN, false);
+        assertThat(mTabGridViewHolder.thumbnail.getDrawable(), instanceOf(BitmapDrawable.class));
+        Assert.assertEquals(1, mThumbnailFetchedCount.get());
+
+        mGridModel.set(TabProperties.THUMBNAIL_FETCHER, null);
+        mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
+        mGridModel.set(TabProperties.THUMBNAIL_FETCHER, null);
+        mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
+        mGridModel.set(TabProperties.THUMBNAIL_FETCHER, null);
+        mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
+        Assert.assertEquals(4, mThumbnailFetchedCount.get());
+    }
+
+    @Test
+    @MediumTest
+    @UiThreadTest
     public void testClickToSelect() throws Exception {
         mTabGridViewHolder.itemView.performClick();
         Assert.assertTrue(mSelectClicked.get());
@@ -232,6 +339,16 @@
         mStripModel.set(TabProperties.IS_SELECTED, true);
         mTabStripViewHolder.button.performClick();
         Assert.assertFalse(mSelectClicked.get());
+        mSelectClicked.set(false);
+
+        mSelectableModel.set(TabProperties.IS_SELECTED, false);
+        mSelectableTabGridViewHolder.itemView.performClick();
+        Assert.assertTrue(mSelectClicked.get());
+        mSelectClicked.set(false);
+
+        mSelectableModel.set(TabProperties.IS_SELECTED, true);
+        mSelectableTabGridViewHolder.itemView.performClick();
+        Assert.assertTrue(mSelectClicked.get());
     }
 
     @Test
@@ -256,6 +373,7 @@
     public void tearDownTest() throws Exception {
         mStripMCP.destroy();
         mGridMCP.destroy();
+        mSelectableMCP.destroy();
         super.tearDownTest();
     }
 }
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java
index d4d87ca..1cd2264 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java
@@ -282,8 +282,10 @@
     @Test
     public void resetsToNullAfterHidingFinishes() {
         initAndAssertAllProperties();
+        mMediator.setSoftCleanupDelayForTesting(0);
         mMediator.setCleanupDelayForTesting(0);
         mMediator.postHiding();
+        verify(mResetHandler).softCleanup();
         verify(mResetHandler).resetWithTabList(eq(null));
     }
 
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
index 954cef6..f7a692db 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -24,6 +24,7 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
 import android.view.View;
 
 import org.junit.After;
@@ -89,6 +90,8 @@
     @Mock
     RecyclerView mRecyclerView;
     @Mock
+    RecyclerView.Adapter mAdapter;
+    @Mock
     TabGroupModelFilter mTabGroupModelFilter;
     @Mock
     EmptyTabModelFilter mEmptyTabModelFilter;
@@ -225,7 +228,8 @@
 
         doReturn(mEmptyTabModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
 
-        mMediator.getItemTouchHelperCallback(0f).onMove(mRecyclerView, mViewHolder1, mViewHolder2);
+        mMediator.getItemTouchHelperCallback(0f, 0f, true)
+                .onMove(mRecyclerView, mViewHolder1, mViewHolder2);
 
         verify(mTabModel).moveTab(eq(TAB1_ID), eq(2));
     }
@@ -233,11 +237,13 @@
     @Test
     public void sendsMoveTabSignalCorrectlyWithGroup() {
         initAndAssertAllProperties();
+        TabGridItemTouchHelperCallback itemTouchHelperCallback =
+                (TabGridItemTouchHelperCallback) mMediator.getItemTouchHelperCallback(0f, 0f, true);
+        itemTouchHelperCallback.setActionsOnAllRelatedTabsForTest(true);
 
-        mMediator.setCloseAllRelatedTabs(true);
         doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
 
-        mMediator.getItemTouchHelperCallback(0f).onMove(mRecyclerView, mViewHolder1, mViewHolder2);
+        itemTouchHelperCallback.onMove(mRecyclerView, mViewHolder1, mViewHolder2);
 
         verify(mTabGroupModelFilter).moveRelatedTabs(eq(TAB1_ID), eq(2));
     }
@@ -248,12 +254,33 @@
 
         doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
 
-        mMediator.getItemTouchHelperCallback(0f).onMove(mRecyclerView, mViewHolder1, mViewHolder2);
+        mMediator.getItemTouchHelperCallback(0f, 0f, true)
+                .onMove(mRecyclerView, mViewHolder1, mViewHolder2);
 
         verify(mTabModel).moveTab(eq(TAB1_ID), eq(2));
     }
 
     @Test
+    public void sendsMergeTabSignalCorrectly() {
+        initAndAssertAllProperties();
+        mMediator.setActionOnAllRelatedTabsForTest(true);
+        TabGridItemTouchHelperCallback itemTouchHelperCallback =
+                (TabGridItemTouchHelperCallback) mMediator.getItemTouchHelperCallback(0f, 0f, true);
+        itemTouchHelperCallback.setActionsOnAllRelatedTabsForTest(true);
+        itemTouchHelperCallback.setHoveredTabIndexForTest(POSITION1);
+        itemTouchHelperCallback.setSelectedTabIndexForTest(POSITION2);
+        itemTouchHelperCallback.getMovementFlags(mRecyclerView, mViewHolder1);
+
+        doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
+        doReturn(mAdapter).when(mRecyclerView).getAdapter();
+
+        // Simulate the drop action.
+        itemTouchHelperCallback.onSelectedChanged(mViewHolder1, ItemTouchHelper.ACTION_STATE_IDLE);
+
+        verify(mTabGroupModelFilter).mergeTabsToGroup(eq(TAB2_ID), eq(TAB1_ID));
+    }
+
+    @Test
     public void tabClosure() {
         initAndAssertAllProperties();
 
@@ -277,7 +304,7 @@
     @Test
     public void tabAddition_GTS() {
         initAndAssertAllProperties();
-        mMediator.setCloseAllRelatedTabsForTest(true);
+        mMediator.setActionOnAllRelatedTabsForTest(true);
 
         Tab newTab = prepareTab(TAB3_ID, TAB3_TITLE);
         doReturn(mTab1).when(mTabModelFilter).getTabAt(0);
@@ -298,7 +325,7 @@
     @Test
     public void tabAddition_GTS_Skip() {
         initAndAssertAllProperties();
-        mMediator.setCloseAllRelatedTabsForTest(true);
+        mMediator.setActionOnAllRelatedTabsForTest(true);
 
         // Add a new tab to the group with mTab2.
         Tab newTab = prepareTab(TAB3_ID, TAB3_TITLE);
@@ -317,7 +344,7 @@
     @Test
     public void tabAddition_GTS_Middle() {
         initAndAssertAllProperties();
-        mMediator.setCloseAllRelatedTabsForTest(true);
+        mMediator.setActionOnAllRelatedTabsForTest(true);
 
         Tab newTab = prepareTab(TAB3_ID, TAB3_TITLE);
         doReturn(mTab1).when(mTabModelFilter).getTabAt(0);
@@ -399,6 +426,34 @@
     }
 
     @Test
+    public void tabMergeIntoGroup() {
+        setUpForTabGroupOperation();
+        mMediator.setActionOnAllRelatedTabsForTest(true);
+
+        // Assume that moveTab in TabModel is finished.
+        doReturn(mTab1).when(mTabModel).getTabAt(POSITION2);
+        doReturn(mTab2).when(mTabModel).getTabAt(POSITION1);
+        doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
+
+        // Assume that reset in TabGroupModelFilter is finished.
+        doReturn(new ArrayList<>(Arrays.asList(mTab1, mTab2)))
+                .when(mTabGroupModelFilter)
+                .getRelatedTabList(TAB1_ID);
+
+        assertThat(mModel.size(), equalTo(2));
+        assertThat(mModel.get(1).get(TabProperties.TAB_ID), equalTo(TAB2_ID));
+        assertThat(mModel.get(1).get(TabProperties.TITLE), equalTo(TAB2_TITLE));
+        assertThat(mModel.indexFromId(TAB1_ID), equalTo(POSITION1));
+        assertThat(mModel.indexFromId(TAB2_ID), equalTo(POSITION2));
+
+        mTabGroupModelFilterObserverCaptor.getValue().didMergeTabToGroup(mTab1, TAB2_ID);
+
+        assertThat(mModel.size(), equalTo(1));
+        assertThat(mModel.get(0).get(TabProperties.TAB_ID), equalTo(TAB2_ID));
+        assertThat(mModel.get(0).get(TabProperties.TITLE), equalTo(TAB2_TITLE));
+    }
+
+    @Test
     public void tabMovementWithoutGroup_Forward() {
         initAndAssertAllProperties();
 
@@ -585,8 +640,8 @@
 
         mMediator = new TabListMediator(mModel, mTabModelSelector,
                 mTabContentManager::getTabThumbnailWithCallback, null, mTabListFaviconProvider,
-                false, null, null, getClass().getSimpleName());
+                true, null, null, getClass().getSimpleName());
 
         initAndAssertAllProperties();
     }
-}
+}
\ No newline at end of file
diff --git a/chrome/android/feed/core/DEPS b/chrome/android/feed/core/DEPS
index 062b9e0..4e70c352 100644
--- a/chrome/android/feed/core/DEPS
+++ b/chrome/android/feed/core/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+components/background_task_scheduler",
+  "+components/feature_engagement",
   "+components/feed",
   "+content/public/android/java/src/org/chromium/content_public",
   "+third_party/feed"
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedAppLifecycle.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedAppLifecycle.java
index 63e184207..8cb541a 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedAppLifecycle.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedAppLifecycle.java
@@ -11,9 +11,14 @@
 
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationStatus;
+import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.task.TaskTraits;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.signin.SigninManager;
+import org.chromium.content_public.browser.UiThreadTaskTraits;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -47,9 +52,30 @@
     private AppLifecycleListener mAppLifecycleListener;
     private FeedLifecycleBridge mLifecycleBridge;
     private FeedScheduler mFeedScheduler;
+    private TaskDelegate mTaskDelegate;
 
     private int mTabbedActivityCount;
     private boolean mInitializeCalled;
+    private boolean mDelayedInitializeStarted;
+
+    /** Abstraction for posting delayed tasks. */
+    interface TaskDelegate {
+        /**
+         * @param taskTraits The TaskTraits that describe the desired TaskRunner.
+         * @param task The task to be run with the specified traits.
+         * @param delay The delay in milliseconds before the task can be run.
+         */
+        void postDelayedTask(TaskTraits taskTraits, Runnable task, long delay);
+    }
+
+    /** The implementation used at runtime that calls into {@link PostTask}. */
+    @VisibleForTesting
+    static class DefaultTaskDelegate implements TaskDelegate {
+        @Override
+        public void postDelayedTask(TaskTraits taskTraits, Runnable task, long delay) {
+            PostTask.postDelayedTask(taskTraits, task, delay);
+        }
+    }
 
     /**
      * Create a FeedAppLifecycle instance. In normal use, this should only be called by {@link
@@ -58,12 +84,21 @@
      *        interface that we will call into.
      * @param lifecycleBridge FeedLifecycleBridge JNI bridge over which native lifecycle events are
      *        delivered.
+     * @param feedScheduler Scheduler to be notified of several events.
      */
     public FeedAppLifecycle(AppLifecycleListener appLifecycleListener,
             FeedLifecycleBridge lifecycleBridge, FeedScheduler feedScheduler) {
+        this(appLifecycleListener, lifecycleBridge, feedScheduler, new DefaultTaskDelegate());
+    }
+
+    /** Package private constructor used directly by tests to inject a mock TaskDelegate. */
+    @VisibleForTesting
+    FeedAppLifecycle(AppLifecycleListener appLifecycleListener, FeedLifecycleBridge lifecycleBridge,
+            FeedScheduler feedScheduler, TaskDelegate taskDelegate) {
         mAppLifecycleListener = appLifecycleListener;
         mLifecycleBridge = lifecycleBridge;
         mFeedScheduler = feedScheduler;
+        mTaskDelegate = taskDelegate;
 
         int resumedActivityCount = 0;
         for (Activity activity : ApplicationStatus.getRunningActivities()) {
@@ -128,6 +163,7 @@
         mLifecycleBridge = null;
         mAppLifecycleListener = null;
         mFeedScheduler = null;
+        mTaskDelegate = null;
     }
 
     @Override
@@ -170,6 +206,23 @@
     private void onEnterForeground() {
         reportEvent(AppLifecycleEvent.ENTER_FOREGROUND);
         mAppLifecycleListener.onEnterForeground();
+
+        if (!mDelayedInitializeStarted) {
+            mDelayedInitializeStarted = true;
+            int disableByDefault = -1;
+            int delayMs = ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
+                    ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS, "init_feed_after_delay_ms",
+                    disableByDefault);
+            if (delayMs >= 0) {
+                mTaskDelegate.postDelayedTask(UiThreadTaskTraits.BEST_EFFORT, () -> {
+                    // Since this is being run asynchronously, it's possible #destroy() is called
+                    // before the delay finishes. Must guard against this.
+                    if (mLifecycleBridge != null) {
+                        initialize();
+                    }
+                }, delayMs);
+            }
+        }
     }
 
     private void onEnterBackground() {
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
index 37d420f..77a6c64a 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
@@ -27,8 +27,6 @@
 import com.google.android.libraries.feed.api.host.stream.SnackbarCallbackApi;
 import com.google.android.libraries.feed.api.host.stream.StreamConfiguration;
 import com.google.android.libraries.feed.api.host.stream.TooltipApi;
-import com.google.android.libraries.feed.api.host.stream.TooltipCallbackApi;
-import com.google.android.libraries.feed.api.host.stream.TooltipInfo;
 import com.google.android.libraries.feed.api.internal.scope.FeedProcessScope;
 
 import org.chromium.base.ApiCompatibilityUtils;
@@ -37,6 +35,7 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeApplication;
 import org.chromium.chrome.browser.feed.action.FeedActionHandler;
+import org.chromium.chrome.browser.feed.tooltip.BasicTooltipApi;
 import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.native_page.NativePageHost;
@@ -105,14 +104,6 @@
         }
     }
 
-    private static class BasicTooltipApi implements TooltipApi {
-        @Override
-        public boolean maybeShowHelpUi(
-                TooltipInfo tooltipInfo, View view, TooltipCallbackApi tooltipCallback) {
-            return false;
-        }
-    }
-
     private static class BasicStreamConfiguration implements StreamConfiguration {
         public BasicStreamConfiguration() {}
 
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
index 7981ad1..0c94490 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
@@ -11,13 +11,12 @@
 import com.google.android.libraries.feed.api.host.config.Configuration;
 import com.google.android.libraries.feed.api.host.config.DebugBehavior;
 import com.google.android.libraries.feed.api.host.network.NetworkClient;
-import com.google.android.libraries.feed.api.host.stream.TooltipSupportedApi;
 import com.google.android.libraries.feed.api.internal.scope.FeedProcessScope;
-import com.google.android.libraries.feed.common.functional.Consumer;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.browser.feed.tooltip.BasicTooltipSupportedApi;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefChangeRegistrar;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
@@ -140,7 +139,7 @@
         sFeedProcessScope = (FeedProcessScope) new ProcessScopeBuilder(configHostApi,
                 Executors.newSingleThreadExecutor(), sFeedLoggingBridge, networkClient,
                 schedulerBridge, DebugBehavior.SILENT, ContextUtils.getApplicationContext(),
-                applicationInfo, new StubFeedTooltiSupportedApi())
+                applicationInfo, new BasicTooltipSupportedApi())
                                     .setContentStorage(contentStorage)
                                     .setJournalStorage(journalStorage)
                                     .build();
@@ -178,7 +177,7 @@
         sFeedProcessScope = (FeedProcessScope) new ProcessScopeBuilder(configHostApi,
                 Executors.newSingleThreadExecutor(), sFeedLoggingBridge, networkClient,
                 sFeedScheduler, DebugBehavior.SILENT, ContextUtils.getApplicationContext(),
-                applicationInfo, new StubFeedTooltiSupportedApi())
+                applicationInfo, new BasicTooltipSupportedApi())
                                     .build();
     }
 
@@ -257,9 +256,4 @@
             sFeedLoggingBridge = null;
         }
     }
-
-    private static class StubFeedTooltiSupportedApi implements TooltipSupportedApi {
-        @Override
-        public void wouldTriggerHelpUi(String featureName, Consumer<Boolean> consumer) {}
-    }
 }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/tooltip/BasicTooltipApi.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/tooltip/BasicTooltipApi.java
new file mode 100644
index 0000000..b4b8edd
--- /dev/null
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/tooltip/BasicTooltipApi.java
@@ -0,0 +1,52 @@
+// 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.feed.tooltip;
+
+import android.text.TextUtils;
+import android.view.View;
+
+import com.google.android.libraries.feed.api.host.stream.TooltipApi;
+import com.google.android.libraries.feed.api.host.stream.TooltipCallbackApi;
+import com.google.android.libraries.feed.api.host.stream.TooltipInfo;
+
+import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.widget.textbubble.TextBubble;
+import org.chromium.components.feature_engagement.Tracker;
+import org.chromium.ui.widget.ViewRectProvider;
+
+/**
+ * A basic implementation of the {@link TooltipApi}.
+ */
+public class BasicTooltipApi implements TooltipApi {
+    private static final int TEXT_BUBBLE_TIMEOUT_MS = 10000;
+
+    @Override
+    public boolean maybeShowHelpUi(
+            TooltipInfo tooltipInfo, View view, TooltipCallbackApi tooltipCallback) {
+        final String featureForIPH =
+                FeedTooltipUtils.getFeatureForIPH(tooltipInfo.getFeatureName());
+        if (TextUtils.isEmpty(featureForIPH)) return false;
+
+        final Tracker tracker = TrackerFactory.getTrackerForProfile(
+                Profile.getLastUsedProfile().getOriginalProfile());
+        if (!tracker.shouldTriggerHelpUI(featureForIPH)) return false;
+
+        ViewRectProvider rectProvider = new ViewRectProvider(view);
+        rectProvider.setInsetPx(0, tooltipInfo.getTopInset(), 0, tooltipInfo.getBottomInset());
+
+        TextBubble textBubble = new TextBubble(view.getContext(), view, tooltipInfo.getLabel(),
+                tooltipInfo.getAccessibilityLabel(), true, rectProvider);
+        textBubble.setAutoDismissTimeout(TEXT_BUBBLE_TIMEOUT_MS);
+        textBubble.addOnDismissListener(() -> {
+            tracker.dismissed(featureForIPH);
+            tooltipCallback.onHide(TooltipCallbackApi.TooltipDismissType.TIMEOUT);
+        });
+
+        textBubble.show();
+        tooltipCallback.onShow();
+        return true;
+    }
+}
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/tooltip/BasicTooltipSupportedApi.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/tooltip/BasicTooltipSupportedApi.java
new file mode 100644
index 0000000..92d2e31
--- /dev/null
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/tooltip/BasicTooltipSupportedApi.java
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.feed.tooltip;
+
+import android.text.TextUtils;
+
+import com.google.android.libraries.feed.api.host.stream.TooltipSupportedApi;
+import com.google.android.libraries.feed.common.functional.Consumer;
+
+import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.components.feature_engagement.Tracker;
+
+/**
+ * A basic implementation of the {@link TooltipSupportedApi}.
+ */
+public class BasicTooltipSupportedApi implements TooltipSupportedApi {
+    @Override
+    public void wouldTriggerHelpUi(String featureName, Consumer<Boolean> consumer) {
+        final String featureForIPH = FeedTooltipUtils.getFeatureForIPH(featureName);
+        if (TextUtils.isEmpty(featureForIPH)) {
+            consumer.accept(false);
+            return;
+        }
+
+        final Tracker tracker = TrackerFactory.getTrackerForProfile(
+                Profile.getLastUsedProfile().getOriginalProfile());
+        consumer.accept(tracker.wouldTriggerHelpUI(featureForIPH));
+    }
+}
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/tooltip/FeedTooltipUtils.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/tooltip/FeedTooltipUtils.java
new file mode 100644
index 0000000..88628ae
--- /dev/null
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/tooltip/FeedTooltipUtils.java
@@ -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.
+
+package org.chromium.chrome.browser.feed.tooltip;
+
+import android.support.annotation.Nullable;
+
+import com.google.android.libraries.feed.api.host.stream.TooltipApi;
+import com.google.android.libraries.feed.api.host.stream.TooltipInfo;
+import com.google.android.libraries.feed.api.host.stream.TooltipSupportedApi;
+
+import org.chromium.components.feature_engagement.FeatureConstants;
+
+/**
+ * Helper methods for implementation of {@link TooltipApi} and {@link TooltipSupportedApi}.
+ */
+class FeedTooltipUtils {
+    /**
+     * @param featureName The feature name from {@link TooltipInfo.FeatureName}.
+     * @return A corresponding IPH feature name from {@link FeatureConstants} for the specified
+     *         {@code featureName}.
+     */
+    @Nullable
+    @FeatureConstants
+    static String getFeatureForIPH(@TooltipInfo.FeatureName String featureName) {
+        switch (featureName) {
+            case TooltipInfo.FeatureName.CARD_MENU_TOOLTIP:
+                return FeatureConstants.FEED_CARD_MENU_FEATURE;
+            case TooltipInfo.FeatureName.UNKNOWN:
+                return null;
+            default:
+                assert false
+                    : String.format("Unknown mapping to IPH feature for TooltipInfo.FeatureName %s",
+                              featureName);
+                return null;
+        }
+    }
+}
diff --git a/chrome/android/feed/feed_java_sources.gni b/chrome/android/feed/feed_java_sources.gni
index d295621..9a010464 100644
--- a/chrome/android/feed/feed_java_sources.gni
+++ b/chrome/android/feed/feed_java_sources.gni
@@ -32,6 +32,9 @@
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/StreamLifecycleManager.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/TestNetworkClient.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/action/FeedActionHandler.java",
+    "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/tooltip/BasicTooltipApi.java",
+    "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/tooltip/BasicTooltipSupportedApi.java",
+    "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/tooltip/FeedTooltipUtils.java",
   ]
 
   feed_srcjar_deps = [ "//components/feed/core:feed_core_java_enums_srcjar" ]
diff --git a/chrome/android/java/monochrome_public_bundle.proguard_flags.expected b/chrome/android/java/monochrome_public_bundle.proguard_flags.expected
index 03e3e54..7f2645a7 100644
--- a/chrome/android/java/monochrome_public_bundle.proguard_flags.expected
+++ b/chrome/android/java/monochrome_public_bundle.proguard_flags.expected
@@ -300,9 +300,14 @@
 # Fragments loaded by name via Fragment.instantiate(Context,String)
 # Not all fragments in this package are PreferenceFragments. E.g. HomepageEditor
 # is a normal Fragment.
+# TODO(crbug.com/967022): Remove the android.app.Fragment rule once all
+# Fragments have been migrated to the support library.
 -keep public class org.chromium.chrome.browser.** extends android.app.Fragment {
   public <init>();
 }
+-keep public class org.chromium.chrome.browser.** extends android.support.v7.preference.PreferenceFragmentCompat {
+  public <init>();
+}
 
 # These classes aren't themselves referenced, but __ProcessService[0,1,2...] are
 # referenced, and we look up these services by appending a number onto the name
diff --git a/chrome/android/java/proguard.flags b/chrome/android/java/proguard.flags
index 54326db..9cb3724d 100644
--- a/chrome/android/java/proguard.flags
+++ b/chrome/android/java/proguard.flags
@@ -5,9 +5,14 @@
 # Fragments loaded by name via Fragment.instantiate(Context,String)
 # Not all fragments in this package are PreferenceFragments. E.g. HomepageEditor
 # is a normal Fragment.
+# TODO(crbug.com/967022): Remove the android.app.Fragment rule once all
+# Fragments have been migrated to the support library.
 -keep public class org.chromium.chrome.browser.** extends android.app.Fragment {
   public <init>();
 }
+-keep public class org.chromium.chrome.browser.** extends android.support.v7.preference.PreferenceFragmentCompat {
+  public <init>();
+}
 
 # These classes aren't themselves referenced, but __ProcessService[0,1,2...] are
 # referenced, and we look up these services by appending a number onto the name
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index c57c6964..8050e38 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -260,6 +260,11 @@
         <item name="android:paddingEnd">@dimen/pref_list_padding_kitkat</item>
     </style>
 
+    <style name="Theme.Chromium.PreferenceItemNoDividers">
+        <item name="allowDividerAbove">false</item>
+        <item name="allowDividerBelow">false</item>
+    </style>
+
     <style name="PreferenceActionBarModern" parent="@style/Widget.AppCompat.Light.ActionBar.Solid">
       <item name="titleTextStyle">@style/TextAppearance.BlackHeadline</item>
     </style>
diff --git a/chrome/android/java/res/xml/developer_preferences.xml b/chrome/android/java/res/xml/developer_preferences.xml
index 398674eb..1063877 100644
--- a/chrome/android/java/res/xml/developer_preferences.xml
+++ b/chrome/android/java/res/xml/developer_preferences.xml
@@ -3,15 +3,17 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:orderingFromXml="true">
-    <Preference
+<android.support.v7.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orderingFromXml="true">
+    <android.support.v7.preference.Preference
         android:fragment="org.chromium.chrome.browser.preferences.developer.TracingPreferences"
         android:key="tracing"
-        android:title="Tracing"/>
-    <org.chromium.chrome.browser.preferences.TextMessagePreference
-        android:key="beta_stable_hint"
-        android:title="Hint: You can also enable Developer options on Beta/Stable channels by tapping the Chrome version in &quot;Settings > About Chrome&quot; multiple times."
-        android:enabled="false"
+        android:title="Tracing" />
+    <org.chromium.chrome.browser.preferences.TextMessagePreferenceCompat
+        style="@style/Theme.Chromium.PreferenceItemNoDividers"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"/>
-</PreferenceScreen>
+        android:layout_height="wrap_content"
+        android:enabled="false"
+        android:key="beta_stable_hint"
+        android:title="Hint: You can also enable Developer options on Beta/Stable channels by tapping the Chrome version in &quot;Settings > About Chrome&quot; multiple times." />
+</android.support.v7.preference.PreferenceScreen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index ac1dc06e..46395f4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -258,6 +258,7 @@
     public static final String MODAL_PERMISSION_DIALOG_VIEW = "ModalPermissionDialogView";
     public static final String NEW_PHOTO_PICKER = "NewPhotoPicker";
     public static final String NETWORK_SERVICE = "NetworkService";
+    public static final String NOTIFICATION_SUSPENDER = "NotificationSuspender";
     public static final String NO_CREDIT_CARD_ABORT = "NoCreditCardAbort";
     public static final String NTP_ARTICLE_SUGGESTIONS = "NTPArticleSuggestions";
     public static final String NTP_BUTTON = "NTPButton";
@@ -321,6 +322,7 @@
     public static final String TAB_REPARENTING = "TabReparenting";
     public static final String TAB_SWITCHER_ON_RETURN = "TabSwitcherOnReturn";
     public static final String TAB_TO_GTS_ANIMATION = "TabToGTSAnimation";
+    public static final String TOUCH_TO_FILL_ANDROID = "TouchToFillAndroid";
     public static final String TRANSLATE_ANDROID_MANUAL_TRIGGER = "TranslateAndroidManualTrigger";
     public static final String TRUSTED_WEB_ACTIVITY = "TrustedWebActivity";
     public static final String TRUSTED_WEB_ACTIVITY_POST_MESSAGE = "TrustedWebActivityPostMessage";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 6bdd439..c0728ae3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -36,7 +36,6 @@
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ApplicationStatus;
-import org.chromium.base.BuildInfo;
 import org.chromium.base.CommandLine;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
@@ -832,8 +831,7 @@
                 FeedProcessScopeFactory.getFeedAppLifecycle();
             }
 
-            if (BuildInfo.isAtLeastQ()
-                    && ChromeFeatureList.isEnabled(ChromeFeatureList.USAGE_STATS)) {
+            if (UsageStatsService.isEnabled()) {
                 UsageStatsService.getInstance().createPageViewObserver(mTabModelSelectorImpl, this);
             }
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
index d624251..13007f8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
@@ -41,6 +41,7 @@
 import org.chromium.chrome.browser.preferences.website.SingleCategoryPreferences;
 import org.chromium.chrome.browser.preferences.website.SingleWebsitePreferences;
 import org.chromium.chrome.browser.preferences.website.SiteSettingsCategory;
+import org.chromium.chrome.browser.usage_stats.NotificationSuspender;
 import org.chromium.chrome.browser.webapps.ChromeWebApkHost;
 import org.chromium.chrome.browser.webapps.WebApkServiceClient;
 import org.chromium.components.url_formatter.UrlFormatter;
@@ -62,7 +63,7 @@
     // We always use the same integer id when showing and closing notifications. The notification
     // tag is always set, which is a safe and sufficient way of identifying a notification, so the
     // integer id is not needed anymore except it must not vary in an uncontrolled way.
-    @VisibleForTesting static final int PLATFORM_ID = -1;
+    public static final int PLATFORM_ID = -1;
 
     // We always use the same request code for pending intents. We use other ways to force
     // uniqueness of pending intents when necessary.
@@ -383,8 +384,7 @@
      * tag, or if the notification tag didn't match the expected format.
      */
     @Nullable
-    @VisibleForTesting
-    static String getOriginFromNotificationTag(@Nullable String tag) {
+    public static String getOriginFromNotificationTag(@Nullable String tag) {
         if (tag == null
                 || !tag.startsWith(NotificationConstants.PERSISTENT_NOTIFICATION_TAG_PREFIX
                            + NotificationConstants.NOTIFICATION_TAG_SEPARATOR))
@@ -538,11 +538,16 @@
 
         ChromeNotification notification =
                 buildNotification(notificationBuilder, notificationId, origin, actions, image);
-        // Display notification as Chrome.
-        mNotificationManager.notify(notification);
-        NotificationUmaTracker.getInstance().onNotificationShown(
-                NotificationUmaTracker.SystemNotificationType.SITES,
-                notification.getNotification());
+
+        // Store notification if its origin is suspended.
+        NotificationSuspender.maybeSuspendNotification(notification).then((suspended) -> {
+            if (suspended) return;
+            // Display notification as Chrome.
+            mNotificationManager.notify(notification);
+            NotificationUmaTracker.getInstance().onNotificationShown(
+                    NotificationUmaTracker.SystemNotificationType.SITES,
+                    notification.getNotification());
+        });
     }
 
     private NotificationBuilderBase prepareNotificationBuilder(String notificationId, String origin,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskScheduler.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskScheduler.java
index e6b1bb7d..45b9917 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskScheduler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskScheduler.java
@@ -20,7 +20,7 @@
 @JNINamespace("offline_pages::prefetch")
 public class PrefetchBackgroundTaskScheduler {
     public static final long DEFAULT_START_DELAY_SECONDS = 15 * 60;
-    public static final long LIMITLESS_START_DELAY_SECONDS = 5;
+    public static final long LIMITLESS_START_DELAY_SECONDS = 20;
 
     /**
      * Schedules the default 'NWake' task for the prefetching service. This task will normally be
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index acf3ee44..9ee40ee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -82,6 +82,7 @@
     // Delay triggering the omnibox results upon key press to allow the location bar to repaint
     // with the new characters.
     private static final long OMNIBOX_SUGGESTION_START_DELAY_MS = 30;
+    private static final int OMNIBOX_HISTOGRAMS_MAX_SUGGESTIONS = 10;
 
     private final Context mContext;
     private final AutocompleteDelegate mDelegate;
@@ -188,12 +189,21 @@
      * revisiting or a secondary metric telling us how many answer suggestions have been shown.
      */
     private void recordAnswersHistogram() {
+        int richEntitiesCount = 0;
         for (SuggestionViewInfo info : mCurrentModels) {
             if (info.suggestion.hasAnswer()) {
                 RecordHistogram.recordEnumeratedHistogram("Omnibox.AnswerInSuggestShown",
                         info.suggestion.getAnswer().getType(), AnswerType.TOTAL_COUNT);
+            } else if (mEntitySuggestionProcessor.doesProcessSuggestion(info.suggestion)) {
+                richEntitiesCount++;
             }
         }
+
+        // Note: valid range for histograms must start with (at least) 1. This does not prevent us
+        // from reporting 0 as a count though - values lower than 'min' fall in the 'underflow'
+        // bucket, while values larger than 'max' will be reported in 'overflow' bucket.
+        RecordHistogram.recordLinearCountHistogram("Omnibox.RichEntityShown", richEntitiesCount, 1,
+                OMNIBOX_HISTOGRAMS_MAX_SUGGESTIONS, OMNIBOX_HISTOGRAMS_MAX_SUGGESTIONS + 1);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBasePreferenceCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBasePreferenceCompat.java
new file mode 100644
index 0000000..124de2b
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBasePreferenceCompat.java
@@ -0,0 +1,81 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.preferences;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import org.chromium.chrome.R;
+
+/**
+ * A preference that supports some Chrome-specific customizations:
+ *
+ * 1. This preference supports being managed. If this preference is managed (as determined by its
+ *    ManagedPreferenceDelegate), it updates its appearance and behavior appropriately: shows an
+ *    enterprise icon, disables clicks, etc.
+ *
+ * 2. This preference can have a multiline title.
+ * 3. This preference can set an icon color in XML through app:iconTint. Note that if a
+ *    ColorStateList is set, only the default color will be used.
+ *
+ * TODO(crbug.com/967022): This class is analogous to {@link ChromeBasePreference}, but extends the
+ * Preference Support Library rather than the deprecated Framework preferences. Once all {@link
+ * ChromeBasePreference}s have been migrated to the Support Library, {@link ChromeBasePreference}
+ * will be removed in favor of {@link ChromeBasePreferenceCompat}.
+ */
+public class ChromeBasePreferenceCompat extends Preference {
+    private ColorStateList mIconTint;
+    private ManagedPreferenceDelegateCompat mManagedPrefDelegate;
+
+    /**
+     * Constructor for use in Java.
+     */
+    public ChromeBasePreferenceCompat(Context context) {
+        this(context, null);
+    }
+
+    /**
+     * Constructor for inflating from XML.
+     */
+    public ChromeBasePreferenceCompat(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ChromeBasePreference);
+        mIconTint = a.getColorStateList(R.styleable.ChromeBasePreference_iconTint);
+        a.recycle();
+    }
+
+    /**
+     * Sets the ManagedPreferenceDelegate which will determine whether this preference is managed.
+     */
+    public void setManagedPreferenceDelegate(ManagedPreferenceDelegateCompat delegate) {
+        mManagedPrefDelegate = delegate;
+        ManagedPreferencesUtils.initPreference(mManagedPrefDelegate, this);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        ((TextView) holder.findViewById(android.R.id.title)).setSingleLine(false);
+        Drawable icon = getIcon();
+        if (icon != null && mIconTint != null) {
+            icon.setColorFilter(mIconTint.getDefaultColor(), PorterDuff.Mode.SRC_IN);
+        }
+        ManagedPreferencesUtils.onBindViewToPreference(mManagedPrefDelegate, this, holder.itemView);
+    }
+
+    @Override
+    protected void onClick() {
+        if (ManagedPreferencesUtils.onClickPreference(mManagedPrefDelegate, this)) return;
+        super.onClick();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferenceDelegateCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferenceDelegateCompat.java
new file mode 100644
index 0000000..193a856a
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferenceDelegateCompat.java
@@ -0,0 +1,63 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.preferences;
+
+import android.support.v7.preference.Preference;
+
+/**
+ * A delegate that determines whether a Preference is managed by enterprise policy. This is used
+ * in various Preference subclasses (e.g. ChromeSwitchPreference) to determine whether to show
+ * an enterprise icon next to the Preference and whether to disable clicks on the Preference.
+ *
+ * An implementation of this delegate should override isPreferenceControlledByPolicy() and,
+ * optionally, isPreferenceClickDisabledByPolicy(). Example:
+ *
+ *   class RocketManagedPreferenceDelegate extends ManagedPreferenceDelegate {
+ *       @Override
+ *       public boolean isPreferenceControlledByPolicy(Preference preference) {
+ *           if ("enable_rockets".equals(preference.getKey())) {
+ *               return RocketUtils.isEnableRocketsManaged();
+ *           }
+ *           return false;
+ *       }
+ *   }
+ *
+ *   ChromeSwitchPreference enableRocketsPref = ...;
+ *   enableRocketsPref.setManagedPreferenceDelegate(new RocketManagedPreferenceDelegate());
+ *
+ * TODO(crbug.com/967022): This class is analogous to {@link ManagedPreferenceDelegate}, but is
+ * implemented for Support Library preferences rather than the deprecated Framework preferences.
+ * Once all managed preferences have been migrated to the Support Library, {@link
+ * ManagedPreferenceDelegate} will be removed in favor of this class.
+ */
+public interface ManagedPreferenceDelegateCompat {
+    /**
+     * Returns whether the given Preference is controlled by an enterprise policy.
+     * @param preference the {@link Preference} under consideration.
+     * @return whether the given Preference is controlled by an enterprise policy.
+     */
+    boolean isPreferenceControlledByPolicy(Preference preference);
+
+    /**
+     * Returns whether the given Preference is controlled by the supervised user's custodian.
+     * @param preference the {@link Preference} under consideration.
+     * @return whether the given Preference is controlled by the supervised user's custodian.
+     */
+    default boolean isPreferenceControlledByCustodian(Preference preference) {
+        return false;
+    }
+
+    /**
+     * Returns whether clicking on the given Preference is disabled due to a policy. The default
+     * implementation just returns whether the preference is not modifiable by the user.
+     * However, some preferences that are controlled by policy may still be clicked to show an
+     * informational subscreen, in which case this method needs a custom implementation.
+     */
+    // TODO(bauerb): Rename to isPreferenceClickDisabled.
+    default boolean isPreferenceClickDisabledByPolicy(Preference preference) {
+        return isPreferenceControlledByPolicy(preference)
+                || isPreferenceControlledByCustodian(preference);
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferencesUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferencesUtils.java
index 14b4a38..14683b1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferencesUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferencesUtils.java
@@ -103,6 +103,10 @@
      *
      * This should be called once, before the preference is displayed.
      *
+     * TODO(crbug.com/967022): Remove this method once all fragments are migrated to the Support
+     * Library in favor of {@link #initPreference(ManagedPreferenceDelegateCompat,
+     * android.support.v7.preference.Preference)}.
+     *
      * @param delegate The delegate that controls whether the preference is managed. May be null,
      *         then this method does nothing.
      * @param preference The Preference that is being initialized
@@ -127,6 +131,77 @@
     }
 
     /**
+     * Initializes the Preference based on the state of any policies that may affect it,
+     * e.g. by showing a managed icon or disabling clicks on the preference. If |preference| is an
+     * instance of ChromeImageViewPreference, the icon is not set since the ImageView widget will
+     * display the managed icons.
+     *
+     * This should be called once, before the preference is displayed.
+     *
+     * @param delegate The delegate that controls whether the preference is managed. May be null,
+     *         then this method does nothing.
+     * @param preference The Preference that is being initialized
+     */
+    public static void initPreference(@Nullable ManagedPreferenceDelegateCompat delegate,
+            android.support.v7.preference.Preference preference) {
+        if (delegate == null) return;
+
+        // TODO(chouinard): A compat version of ChromeImageViewPreference hasn't been created yet.
+        // Once it is, uncomment this section.
+        /*
+        if (!(preference instanceof ChromeImageViewPreference)) {
+            preference.setIcon(getManagedIconDrawable(delegate, preference));
+        }
+        */
+
+        if (delegate.isPreferenceClickDisabledByPolicy(preference)) {
+            // Disable the views and prevent the Preference from mucking with the enabled state.
+            preference.setShouldDisableView(false);
+
+            // Prevent default click behavior.
+            preference.setFragment(null);
+            preference.setIntent(null);
+            preference.setOnPreferenceClickListener(null);
+        }
+    }
+
+    /**
+     * Disables the Preference's views if the preference is not clickable.
+     *
+     * Note: this disables the View instead of disabling the Preference, so that the Preference
+     * still receives click events, which will trigger a "Managed by your administrator" toast.
+     *
+     * This should be called from the Preference's onBindView() method.
+     *
+     * TODO(crbug.com/967022): Remove this method once all fragments are migrated to the Support
+     * Library in favor of {@link #onBindViewToPreference(ManagedPreferenceDelegateCompat,
+     * android.support.v7.preference.Preference, View)}.
+     *
+     * @param delegate The delegate that controls whether the preference is managed. May be null,
+     *         then this method does nothing.
+     * @param preference The Preference that owns the view
+     * @param view The View that was bound to the Preference
+     */
+    public static void onBindViewToPreference(
+            @Nullable ManagedPreferenceDelegate delegate, Preference preference, View view) {
+        if (delegate == null) return;
+
+        if (delegate.isPreferenceClickDisabledByPolicy(preference)) {
+            ViewUtils.setEnabledRecursive(view, false);
+        }
+
+        // Append managed information to summary if necessary.
+        TextView summaryView = view.findViewById(android.R.id.summary);
+        CharSequence summary =
+                ManagedPreferencesUtils.getSummaryWithManagedInfo(delegate, preference,
+                        summaryView.getVisibility() == View.VISIBLE ? summaryView.getText() : null);
+        if (!TextUtils.isEmpty(summary)) {
+            summaryView.setText(summary);
+            summaryView.setVisibility(View.VISIBLE);
+        }
+    }
+
+    /**
      * Disables the Preference's views if the preference is not clickable.
      *
      * Note: this disables the View instead of disabling the Preference, so that the Preference
@@ -139,8 +214,8 @@
      * @param preference The Preference that owns the view
      * @param view The View that was bound to the Preference
      */
-    public static void onBindViewToPreference(
-            @Nullable ManagedPreferenceDelegate delegate, Preference preference, View view) {
+    public static void onBindViewToPreference(@Nullable ManagedPreferenceDelegateCompat delegate,
+            android.support.v7.preference.Preference preference, View view) {
         if (delegate == null) return;
 
         if (delegate.isPreferenceClickDisabledByPolicy(preference)) {
@@ -197,6 +272,10 @@
      *
      * This should be called from the Preference's onClick() method.
      *
+     * TODO(crbug.com/967022): Remove this method once all fragments are migrated to the Support
+     * Library in favor of {@link #onClickPreference(ManagedPreferenceDelegateCompat,
+     * android.support.v7.preference.Preference)}.
+     *
      * @param delegate The delegate that controls whether the preference is managed. May be null,
      *         then this method does nothing and returns false.
      * @param preference The Preference that was clicked.
@@ -222,6 +301,39 @@
     }
 
     /**
+     * Intercepts the click event if the given Preference is managed and shows a toast in that case.
+     *
+     * This should be called from the Preference's onClick() method.
+     *
+     * @param delegate The delegate that controls whether the preference is managed. May be null,
+     *         then this method does nothing and returns false.
+     * @param preference The Preference that was clicked.
+     * @return true if the click event was handled by this helper and shouldn't be further
+     *         propagated; false otherwise.
+     */
+    public static boolean onClickPreference(@Nullable ManagedPreferenceDelegateCompat delegate,
+            android.support.v7.preference.Preference preference) {
+        if (delegate == null || !delegate.isPreferenceClickDisabledByPolicy(preference)) {
+            return false;
+        }
+
+        if (delegate.isPreferenceControlledByPolicy(preference)) {
+            showManagedByAdministratorToast(preference.getContext());
+        } else if (delegate.isPreferenceControlledByCustodian(preference)) {
+            showManagedByParentToast(preference.getContext());
+        } else {
+            // If the preference is disabled, it should be either because it's managed by enterprise
+            // policy or by the custodian.
+            assert false;
+        }
+        return true;
+    }
+
+    /**
+     * TODO(crbug.com/967022): Remove this method once all fragments are migrated to the Support
+     * Library in favor of {@link #getSummaryWithManagedInfo(ManagedPreferenceDelegateCompat,
+     * android.support.v7.preference.Preference, CharSequence)}.
+     *
      * @param delegate The {@link ManagedPreferenceDelegate} that controls whether the preference is
      *        managed.
      * @param preference The {@link Preference} that the summary should be used for.
@@ -246,6 +358,33 @@
         return String.format(Locale.getDefault(), "%s\n%s", summary, extraSummary);
     }
 
+    /**
+     * @param delegate The {@link ManagedPreferenceDelegateCompat} that controls whether the
+     *         preference is
+     *        managed.
+     * @param preference The {@link android.support.v7.preference.Preference} that the summary
+     *         should be used for.
+     * @param summary The original summary without the managed information.
+     * @return The summary appended with information about whether the specified preference is
+     *         managed.
+     */
+    private static CharSequence getSummaryWithManagedInfo(
+            @Nullable ManagedPreferenceDelegateCompat delegate,
+            android.support.v7.preference.Preference preference, @Nullable CharSequence summary) {
+        if (delegate == null) return summary;
+
+        String extraSummary = null;
+        if (delegate.isPreferenceControlledByPolicy(preference)) {
+            extraSummary = preference.getContext().getString(R.string.managed_by_your_organization);
+        } else if (delegate.isPreferenceControlledByCustodian(preference)) {
+            extraSummary = preference.getContext().getString(getManagedByParentStringRes());
+        }
+
+        if (TextUtils.isEmpty(extraSummary)) return summary;
+        if (TextUtils.isEmpty(summary)) return extraSummary;
+        return String.format(Locale.getDefault(), "%s\n%s", summary, extraSummary);
+    }
+
     private static @StringRes int getManagedByParentStringRes() {
         boolean singleParentIsManager =
                 PrefServiceBridge.getInstance().getSupervisedUserSecondCustodianName().isEmpty();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessagePreferenceCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessagePreferenceCompat.java
new file mode 100644
index 0000000..bf32e46
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessagePreferenceCompat.java
@@ -0,0 +1,50 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.preferences;
+
+import android.content.Context;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.text.TextUtils;
+import android.text.method.LinkMovementMethod;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * A preference that displays informational text.
+ *
+ * TODO(crbug.com/967022): This class is analogous to {@link TextMessagePreference}, but extends
+ * {@link ChromeBasePreferenceCompat} rather than {@link ChromeBasePreference}. Once all {@link
+ * TextMessagePreference}-containing fragments have been migrated to the Support Library, remove
+ * {@link TextMessagePreference} in favor of this class.
+ */
+public class TextMessagePreferenceCompat extends ChromeBasePreferenceCompat {
+    /**
+     * Constructor for inflating from XML.
+     */
+    public TextMessagePreferenceCompat(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setSelectable(false);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+
+        TextView titleView = (TextView) holder.findViewById(android.R.id.title);
+        if (!TextUtils.isEmpty(getTitle())) {
+            titleView.setVisibility(View.VISIBLE);
+            titleView.setSingleLine(false);
+            titleView.setMaxLines(Integer.MAX_VALUE);
+            titleView.setMovementMethod(LinkMovementMethod.getInstance());
+        } else {
+            titleView.setVisibility(View.GONE);
+        }
+
+        TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
+        // No need to manually toggle visibility for summary - it is done in super.onBindView.
+        summaryView.setMovementMethod(LinkMovementMethod.getInstance());
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/developer/DeveloperPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/developer/DeveloperPreferences.java
index de4ccc4..189006e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/developer/DeveloperPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/developer/DeveloperPreferences.java
@@ -5,7 +5,7 @@
 package org.chromium.chrome.browser.preferences.developer;
 
 import android.os.Bundle;
-import android.preference.PreferenceFragment;
+import android.support.v7.preference.PreferenceFragmentCompat;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.R;
@@ -17,7 +17,7 @@
 /**
  * Settings fragment containing preferences aimed at Chrome and web developers.
  */
-public class DeveloperPreferences extends PreferenceFragment {
+public class DeveloperPreferences extends PreferenceFragmentCompat {
     private static final String UI_PREF_BETA_STABLE_HINT = "beta_stable_hint";
     private static final String PREF_DEVELOPER_ENABLED = "developer";
 
@@ -39,8 +39,7 @@
     }
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
+    public void onCreatePreferences(Bundle savedInstanceState, String s) {
         getActivity().setTitle(MSG_DEVELOPER_OPTIONS_TITLE);
         PreferenceUtils.addPreferencesFromResource(this, R.xml.developer_preferences);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
index 511a897..4972780 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
@@ -27,6 +27,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.task.PostTask;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.components.signin.AccountIdProvider;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.AccountTrackerService;
@@ -301,7 +302,8 @@
      */
     public boolean isSigninSupported() {
         return !ApiCompatibilityUtils.isDemoUser(mContext)
-                && mDelegate.isGooglePlayServicesPresent(mContext);
+                && mDelegate.isGooglePlayServicesPresent(mContext)
+                && !ChromeFeatureList.isEnabled(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY);
     }
 
     /**
@@ -443,13 +445,6 @@
             return;
         }
 
-        if (!SigninManagerJni.get().shouldLoadPolicyForUser(mSignInState.mAccount.name)) {
-            // Proceed with the sign-in flow without checking for policy if it can be determined
-            // that this account can't have management enabled based on the username.
-            finishSignIn();
-            return;
-        }
-
         Log.d(TAG, "Checking if account has policy management enabled");
         // This will call back to onPolicyFetchedBeforeSignIn.
         SigninManagerJni.get().registerAndFetchPolicyBeforeSignIn(
@@ -458,9 +453,11 @@
 
     @CalledByNative
     @VisibleForTesting
+    /**
+     * If the user is managed, its policy has been fetched and is being enforced; features like sync
+     * may now be disabled by policy, and the rest of the sign-in flow can be resumed.
+     */
     void onPolicyFetchedBeforeSignIn() {
-        // Policy has been fetched for the user and is being enforced; features like sync may now
-        // be disabled by policy, and the rest of the sign-in flow can be resumed.
         finishSignIn();
     }
 
@@ -727,7 +724,7 @@
      *         otherwise. May be called synchronously from this function.
      */
     public void isUserManaged(String email, final Callback<Boolean> callback) {
-        SigninManagerJni.get().isUserManaged(email, callback);
+        SigninManagerJni.get().isUserManaged(this, mNativeSigninManagerAndroid, email, callback);
     }
 
     public static String extractDomainName(String email) {
@@ -773,9 +770,8 @@
 
         boolean isSignedInOnNative(@JCaller SigninManager self, long nativeSigninManagerAndroid);
 
-        boolean shouldLoadPolicyForUser(String username);
-
-        void isUserManaged(String username, Callback<Boolean> callback);
+        void isUserManaged(@JCaller SigninManager self, long nativeSigninManagerAndroid,
+                String username, Callback<Boolean> callback);
 
         String extractDomainName(String email);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabCountProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabCountProvider.java
index 37510921..19b6f8a7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabCountProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabCountProvider.java
@@ -13,6 +13,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
+import org.chromium.chrome.browser.tasks.tabgroup.TabGroupModelFilter;
 
 import java.util.List;
 
@@ -39,6 +40,11 @@
     /** The {@link TabModelObserver} that observes when the tab count may have changed. */
     private TabModelObserver mTabModelFilterObserver;
 
+    /**
+     * The {@link TabGroupModelFilter.Observer} that observes when the tab count may have changed.
+     */
+    private TabGroupModelFilter.Observer mTabGroupModelFilterObserver;
+
     private int mTabCount;
 
     private boolean mIsIncognito;
@@ -136,6 +142,28 @@
 
         mTabModelSelector.getTabModelFilterProvider().addTabModelFilterObserver(
                 mTabModelFilterObserver);
+
+        mTabGroupModelFilterObserver = new TabGroupModelFilter.Observer() {
+            @Override
+            public void didMergeTabToGroup(Tab movedTab, int selectedTabIdInGroup) {
+                updateTabCount();
+            }
+
+            @Override
+            public void didMoveTabGroup(Tab movedTab, int tabModelOldIndex, int tabModelNewIndex) {}
+
+            @Override
+            public void didMoveWithinGroup(
+                    Tab movedTab, int tabModelOldIndex, int tabModelNewIndex) {}
+        };
+
+        if (mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter()
+                        instanceof TabGroupModelFilter) {
+            ((TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider()
+                            .getCurrentTabModelFilter())
+                    .addTabGroupObserver(mTabGroupModelFilterObserver);
+        }
+
         updateTabCount();
     }
 
@@ -148,6 +176,14 @@
                     mTabModelFilterObserver);
         }
 
+        if (mTabGroupModelFilterObserver != null
+                && mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter()
+                                instanceof TabGroupModelFilter) {
+            ((TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider()
+                            .getCurrentTabModelFilter())
+                    .removeTabGroupObserver(mTabGroupModelFilterObserver);
+        }
+
         if (mTabModelSelector != null) {
             mTabModelSelector.removeObserver(mTabModelSelectorObserver);
             mTabModelSelector = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/NotificationSuspender.java b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/NotificationSuspender.java
new file mode 100644
index 0000000..ade20e6e
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/NotificationSuspender.java
@@ -0,0 +1,198 @@
+// 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.usage_stats;
+
+import android.annotation.TargetApi;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Build;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.webkit.URLUtil;
+
+import org.chromium.base.CollectionUtil;
+import org.chromium.base.ContextUtils;
+import org.chromium.base.Promise;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.base.library_loader.LibraryProcessType;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.notifications.ChromeNotification;
+import org.chromium.chrome.browser.notifications.NotificationMetadata;
+import org.chromium.chrome.browser.notifications.NotificationPlatformBridge;
+import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.content_public.browser.BrowserStartupController;
+import org.chromium.content_public.browser.BrowserStartupController.StartupCallback;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class that suspends and revives notifications for suspended websites. All calls must be made on
+ * the UI thread.
+ */
+@JNINamespace("usage_stats")
+public class NotificationSuspender {
+    private final Profile mProfile;
+    private final Context mContext;
+    private final NotificationManager mNotificationManager;
+
+    private static boolean isEnabled() {
+        return UsageStatsService.isEnabled()
+                && ChromeFeatureList.isEnabled(ChromeFeatureList.NOTIFICATION_SUSPENDER);
+    }
+
+    /**
+     * Suspends the given notification if it originates from a suspended domain.
+     * @param notification The notification to suspend.
+     * @return A {@link Promise} that resolves to whether the given notification got suspended.
+     */
+    public static Promise<Boolean> maybeSuspendNotification(ChromeNotification notification) {
+        // No need to initialize UsageStatsService if it is disabled.
+        if (!isEnabled()) return Promise.fulfilled(false);
+        return waitForChromeStartup()
+                .then((Void v) -> UsageStatsService.getInstance().getAllSuspendedWebsitesAsync())
+                .then((List<String> fqdns) -> {
+                    if (!fqdns.contains(getValidFqdnOrEmptyString(notification))) return false;
+                    UsageStatsService.getInstance()
+                            .getNotificationSuspender()
+                            .storeNotificationResources(CollectionUtil.newArrayList(notification));
+                    return true;
+                });
+    }
+
+    public NotificationSuspender(Profile profile) {
+        mProfile = profile;
+        mContext = ContextUtils.getApplicationContext();
+        mNotificationManager =
+                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+    }
+
+    public void setWebsitesSuspended(List<String> fqdns, boolean suspended) {
+        if (fqdns.isEmpty() || !isEnabled()) return;
+        if (suspended) {
+            storeNotificationResources(getActiveNotificationsForFqdns(fqdns));
+        } else {
+            unsuspendWebsites(fqdns);
+        }
+    }
+
+    private void storeNotificationResources(List<ChromeNotification> notifications) {
+        if (notifications.isEmpty()) return;
+
+        String[] ids = new String[notifications.size()];
+        String[] origins = new String[notifications.size()];
+        Bitmap[] resources = new Bitmap[notifications.size() * 3];
+
+        for (int i = 0; i < notifications.size(); ++i) {
+            Notification notification = notifications.get(i).getNotification();
+            String tag = notifications.get(i).getMetadata().tag;
+            ids[i] = tag;
+            origins[i] = NotificationPlatformBridge.getOriginFromNotificationTag(tag);
+            resources[i * 3 + 0] = getNotificationIcon(notification);
+            resources[i * 3 + 1] = getNotificationBadge(notification);
+            resources[i * 3 + 2] = getNotificationImage(notification);
+            mNotificationManager.cancel(tag, NotificationPlatformBridge.PLATFORM_ID);
+        }
+
+        NotificationSuspenderJni.get().storeNotificationResources(
+                mProfile, ids, origins, resources);
+    }
+
+    private void unsuspendWebsites(List<String> fqdns) {
+        if (fqdns.isEmpty()) return;
+        // Handle both http and https schemes as native expects origins.
+        String[] origins = new String[fqdns.size() * 2];
+        for (int i = 0; i < fqdns.size(); ++i) {
+            origins[i * 2 + 0] = "http://" + fqdns.get(i);
+            origins[i * 2 + 1] = "https://" + fqdns.get(i);
+        }
+        NotificationSuspenderJni.get().reDisplayNotifications(mProfile, origins);
+    }
+
+    @TargetApi(Build.VERSION_CODES.M)
+    private List<ChromeNotification> getActiveNotificationsForFqdns(List<String> fqdns) {
+        List<ChromeNotification> notifications = new ArrayList<>();
+
+        for (StatusBarNotification notification : mNotificationManager.getActiveNotifications()) {
+            if (notification.getId() != NotificationPlatformBridge.PLATFORM_ID) continue;
+            String tag = notification.getTag();
+            String origin = NotificationPlatformBridge.getOriginFromNotificationTag(tag);
+            if (!URLUtil.isHttpUrl(origin) && !URLUtil.isHttpsUrl(origin)) continue;
+            if (!fqdns.contains(Uri.parse(origin).getHost())) continue;
+            NotificationMetadata metadata =
+                    new NotificationMetadata(NotificationUmaTracker.SystemNotificationType.SITES,
+                            tag, NotificationPlatformBridge.PLATFORM_ID);
+            notifications.add(new ChromeNotification(notification.getNotification(), metadata));
+        }
+
+        return notifications;
+    }
+
+    @TargetApi(Build.VERSION_CODES.P)
+    private Bitmap getBitmapFromIcon(Icon icon) {
+        if (icon == null || icon.getType() != Icon.TYPE_BITMAP) return null;
+        return ((BitmapDrawable) icon.loadDrawable(mContext)).getBitmap();
+    }
+
+    @TargetApi(Build.VERSION_CODES.M)
+    private Bitmap getNotificationIcon(Notification notification) {
+        return getBitmapFromIcon(notification.getLargeIcon());
+    }
+
+    @TargetApi(Build.VERSION_CODES.M)
+    private Bitmap getNotificationBadge(Notification notification) {
+        return getBitmapFromIcon(notification.getSmallIcon());
+    }
+
+    private Bitmap getNotificationImage(Notification notification) {
+        return (Bitmap) notification.extras.get(Notification.EXTRA_PICTURE);
+    }
+
+    private static String getValidFqdnOrEmptyString(ChromeNotification notification) {
+        String tag = notification.getMetadata().tag;
+        String origin = NotificationPlatformBridge.getOriginFromNotificationTag(tag);
+        if (TextUtils.isEmpty(origin)) return "";
+        String host = Uri.parse(origin).getHost();
+        return host == null ? "" : host;
+    }
+
+    private static Promise<Void> waitForChromeStartup() {
+        BrowserStartupController browserStartup =
+                BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER);
+        if (browserStartup.isStartupSuccessfullyCompleted()) return Promise.fulfilled(null);
+        Promise<Void> promise = new Promise<>();
+        browserStartup.addStartupCompletedObserver(new StartupCallback() {
+            @Override
+            public void onSuccess() {
+                promise.fulfill(null);
+            }
+            @Override
+            public void onFailure() {
+                promise.reject(null);
+            }
+        });
+        return promise;
+    }
+
+    @NativeMethods
+    interface Natives {
+        // Stores the given |resources| to be displayed later again. Note that |resources| is
+        // expected to have 3 entries (icon, badge, image in that order) for each notification id in
+        // |notificationIds|. If a notification does not have a particular resource, pass null
+        // instead. |origins| must be the same size as |notificationIds|.
+        void storeNotificationResources(
+                Profile profile, String[] notificationIds, String[] origins, Bitmap[] resources);
+
+        // Displays all suspended notifications for the given |origins|.
+        void reDisplayNotifications(Profile profile, String[] origins);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspensionTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspensionTracker.java
index ec423d9..7b638dec 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspensionTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspensionTracker.java
@@ -14,12 +14,14 @@
  * Class that tracks which sites are currently suspended.
  */
 public class SuspensionTracker {
-    private UsageStatsBridge mBridge;
-    private Promise<List<String>> mRootPromise;
+    private final UsageStatsBridge mBridge;
+    private final NotificationSuspender mNotificationSuspender;
+    private final Promise<List<String>> mRootPromise;
     private Promise<Void> mWritePromise;
 
-    public SuspensionTracker(UsageStatsBridge bridge) {
+    public SuspensionTracker(UsageStatsBridge bridge, NotificationSuspender notificationSuspender) {
         mBridge = bridge;
+        mNotificationSuspender = notificationSuspender;
         mRootPromise = new Promise<>();
         mBridge.getAllSuspensions((result) -> { mRootPromise.fulfill(result); });
         mWritePromise = Promise.fulfilled(null);
@@ -55,7 +57,7 @@
                                 } else {
                                     result.removeAll(fqdns);
                                 }
-
+                                mNotificationSuspender.setWebsitesSuspended(fqdns, suspended);
                                 newWritePromise.fulfill(null);
                             } else {
                                 newWritePromise.reject();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java
index 741f03ad..b2815f6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java
@@ -6,6 +6,7 @@
 
 import android.app.Activity;
 
+import org.chromium.base.BuildInfo;
 import org.chromium.base.Log;
 import org.chromium.base.Promise;
 import org.chromium.base.ThreadUtils;
@@ -31,6 +32,7 @@
     private static UsageStatsService sInstance;
 
     private EventTracker mEventTracker;
+    private NotificationSuspender mNotificationSuspender;
     private SuspensionTracker mSuspensionTracker;
     private TokenTracker mTokenTracker;
     private UsageStatsBridge mBridge;
@@ -42,8 +44,14 @@
     private DigitalWellbeingClient mClient;
     private boolean mOptInState;
 
+    /** Returns if the UsageStatsService is enabled on this device */
+    public static boolean isEnabled() {
+        return BuildInfo.isAtLeastQ() && ChromeFeatureList.isEnabled(ChromeFeatureList.USAGE_STATS);
+    }
+
     /** Get the global instance of UsageStatsService */
     public static UsageStatsService getInstance() {
+        assert isEnabled();
         if (sInstance == null) {
             sInstance = new UsageStatsService();
         }
@@ -56,7 +64,8 @@
         Profile profile = Profile.getLastUsedProfile().getOriginalProfile();
         mBridge = new UsageStatsBridge(profile, this);
         mEventTracker = new EventTracker(mBridge);
-        mSuspensionTracker = new SuspensionTracker(mBridge);
+        mNotificationSuspender = new NotificationSuspender(profile);
+        mSuspensionTracker = new SuspensionTracker(mBridge, mNotificationSuspender);
         mTokenTracker = new TokenTracker(mBridge);
         mPageViewObservers = new ArrayList<>();
 
@@ -67,6 +76,10 @@
         mClient = AppHooks.get().createDigitalWellbeingClient();
     }
 
+    /* package */ NotificationSuspender getNotificationSuspender() {
+        return mNotificationSuspender;
+    }
+
     /**
      * Create a {@link PageViewObserver} for the given tab model selector and activity.
      * @param tabModelSelector The tab model selector that should be used to get the current tab
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webshare/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/webshare/OWNERS
new file mode 100644
index 0000000..c880f89
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webshare/OWNERS
@@ -0,0 +1,5 @@
+ericwilligers@chromium.org
+raymes@chromium.org
+
+# COMPONENT: Blink>WebShare
+# OS: Android
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImpl.java
index af420c8..8a0728c0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImpl.java
@@ -254,7 +254,8 @@
     }
 
     static boolean isDangerousFilename(String name) {
-        return name.indexOf('/') != -1 || name.indexOf('\\') != -1 || name.indexOf('.') <= 0
+        // Reject filenames without a permitted extension.
+        return name.indexOf('.') <= 0
                 || !PERMITTED_EXTENSIONS.contains(FileUtils.getExtension(name));
     }
 
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 24e57db..33f6a88d 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -3061,6 +3061,9 @@
       <message name="IDS_MENU_CLOSE_ALL_INCOGNITO_TABS" desc="Menu item for closing all open incognito tabs. [CHAR-LIMIT=27]">
         Close incognito tabs
       </message>
+      <message name="IDS_MENU_GROUP_TABS" desc="Menu item for grouping tabs. [CHAR-LIMIT=27]">
+        Group tabs...
+      </message>
 
       <!-- Bookmarks strings -->
       <message name="IDS_BOOKMARKS" desc="Title of the bookmarks page, which shows a list of the user's bookmarks. [CHAR-LIMIT=18]">
@@ -3481,6 +3484,10 @@
       <message name="IDS_UNDO_BAR_MULTIPLE_DOWNLOADS_DELETE_MESSAGE" desc="Message shown when you can undo deleting several downloads.">
         <ph name="NUMBER_OF_DOWNLOADS">%1$s<ex>3</ex></ph> downloads deleted
       </message>
+      <message name="IDS_UNDO_BAR_GROUP_TABS_MESSAGE" desc="Message shown or announced when a group has been created.">
+        <ph name="TAB_COUNT">%1$s<ex>3</ex></ph> tabs grouped
+      </message>
+
 
       <!-- MultiWindow -->
       <message name="IDS_UNSUPPORTED_NUMBER_OF_WINDOWS" desc="Popup message for when the user has tried to start too many concurrent versions of Chrome.">
@@ -4060,6 +4067,23 @@
         Tab management
       </message>
 
+      <!-- Tab Selection Editor strings -->
+      <message name="IDS_TAB_SELECTION_EDITOR_GROUP" desc="This text button is shown in the Tab Selection Editor Toolbar. 'Group' is a verb. When the user taps the button, Chrome creates a new group that contains the selected tabs.">
+        Group
+      </message>
+      <message name="IDS_ACCESSIBILITY_GROUP_SELECTED_TABS" desc="Content description for the button to group selected tab.">
+        Group <ph name="NUM_SELECTED">%1$s<ex>3</ex></ph> tabs.
+      </message>
+      <message name="IDS_TAB_SELECTION_EDITOR_TOOLBAR_SELECT_TABS" desc="Label shown on Tab Selection Editor Toolbar asking user to select tabs from the list.">
+        Select tabs
+      </message>
+      <message name="IDS_ACCESSIBILITY_SELECT_TAB" desc="Content description for the button to group selected tab.">
+        Select <ph name="TAB_TITLE">%1$s<ex>Google</ex></ph> tab.
+      </message>
+      <message name="IDS_ACCESSIBILITY_UNSELECT_TAB" desc="Content description for the button to group selected tab.">
+        Un-select <ph name="TAB_TITLE">%1$s<ex>Google</ex></ph> tab.
+      </message>
+
       <message name="IDS_AUTOFILL_ASSISTANT_MODULE_TITLE"
   desc="Text shown when the Foo module is referenced in install start, success,
         failure UI (e.g. in IDS_MODULE_INSTALL_START_TEXT, which will expand to
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_MENU_GROUP_TABS.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_MENU_GROUP_TABS.png.sha1
new file mode 100644
index 0000000..c0bc050
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_MENU_GROUP_TABS.png.sha1
@@ -0,0 +1 @@
+b5e5e378a463ef3101e64f04a0709e1ea6763848
\ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_TAB_SELECTION_EDITOR_GROUP.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_TAB_SELECTION_EDITOR_GROUP.png.sha1
new file mode 100644
index 0000000..bc57093
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_TAB_SELECTION_EDITOR_GROUP.png.sha1
@@ -0,0 +1 @@
+9c874ea2b179181e6ffb3e291e69d77646c4b702
\ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_TAB_SELECTION_EDITOR_TOOLBAR_SELECT_TABS.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_TAB_SELECTION_EDITOR_TOOLBAR_SELECT_TABS.png.sha1
new file mode 100644
index 0000000..88d4e721
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_TAB_SELECTION_EDITOR_TOOLBAR_SELECT_TABS.png.sha1
@@ -0,0 +1 @@
+7b922d176e915e87bb4ec4ca02a00eca7e6b49a1
\ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_UNDO_BAR_GROUP_TABS_MESSAGE.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_UNDO_BAR_GROUP_TABS_MESSAGE.png.sha1
new file mode 100644
index 0000000..8d43282
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_UNDO_BAR_GROUP_TABS_MESSAGE.png.sha1
@@ -0,0 +1 @@
+e4d16394d291a1e1c85e076c5a4bd0d2a425600a
\ No newline at end of file
diff --git a/chrome/android/java/strings/translations/android_chrome_strings_ca.xtb b/chrome/android/java/strings/translations/android_chrome_strings_ca.xtb
index a884781a..dc3ce21 100644
--- a/chrome/android/java/strings/translations/android_chrome_strings_ca.xtb
+++ b/chrome/android/java/strings/translations/android_chrome_strings_ca.xtb
@@ -167,7 +167,7 @@
 <translation id="2107397443965016585">Pregunta'm abans de permetre que els llocs web reprodueixin contingut protegit (opció recomanada)</translation>
 <translation id="2111511281910874386">Ves a la pàgina</translation>
 <translation id="2122601567107267586">L'aplicació no s'ha pogut obrir</translation>
-<translation id="2126426811489709554">Amb tecnologia de Chrome</translation>
+<translation id="2126426811489709554">Amb la tecnologia de Chrome</translation>
 <translation id="2131665479022868825">Dades estalviades: <ph name="DATA" /></translation>
 <translation id="213279576345780926">Pestanya <ph name="TAB_TITLE" /> tancada</translation>
 <translation id="2139186145475833000">Afegeix a pantalla d'inici</translation>
diff --git a/chrome/android/java/strings/translations/android_chrome_strings_cs.xtb b/chrome/android/java/strings/translations/android_chrome_strings_cs.xtb
index 8da782e..3f888bb 100644
--- a/chrome/android/java/strings/translations/android_chrome_strings_cs.xtb
+++ b/chrome/android/java/strings/translations/android_chrome_strings_cs.xtb
@@ -20,7 +20,7 @@
 <translation id="1145536944570833626">Smazat existující data.</translation>
 <translation id="1146678959555564648">Zapnout VR</translation>
 <translation id="116280672541001035">Využito</translation>
-<translation id="1171770572613082465">Populární weby zobrazíte klepnutím na tlačítko Nejčastější stránky</translation>
+<translation id="1171770572613082465">Populární weby zobrazíte klepnutím na tlačítko Top weby</translation>
 <translation id="1172593791219290334">Počáteční stránka</translation>
 <translation id="1173894706177603556">Přejmenovat</translation>
 <translation id="1175310183703641346">Vaše záložky, historie, hesla a další nastavení budou synchronizovány do účtu Google.</translation>
diff --git a/chrome/android/java/strings/translations/android_chrome_strings_id.xtb b/chrome/android/java/strings/translations/android_chrome_strings_id.xtb
index 0868117..228887b8 100644
--- a/chrome/android/java/strings/translations/android_chrome_strings_id.xtb
+++ b/chrome/android/java/strings/translations/android_chrome_strings_id.xtb
@@ -20,7 +20,7 @@
 <translation id="1145536944570833626">Hapus data yang sudah ada.</translation>
 <translation id="1146678959555564648">Masuki VR</translation>
 <translation id="116280672541001035">Digunakan</translation>
-<translation id="1171770572613082465">Lihat situs populer dengan menge-tap tombol "Situs teratas"</translation>
+<translation id="1171770572613082465">Lihat situs populer dengan menge-tap tombol "Situs populer"</translation>
 <translation id="1172593791219290334">Halaman Awal</translation>
 <translation id="1173894706177603556">Ganti nama</translation>
 <translation id="1175310183703641346">Bookmark, histori, sandi, dan lainnya tidak akan lagi disinkronkan ke Akun Google Anda.</translation>
@@ -257,7 +257,7 @@
 <translation id="2836148919159985482">Ketuk tombol kembali untuk keluar dari mode layar penuh.</translation>
 <translation id="2842985007712546952">Folder induk</translation>
 <translation id="2845873210977809562">Tab yang terbuka ditutup</translation>
-<translation id="2858138569776157458">Situs teratas</translation>
+<translation id="2858138569776157458">Situs populer</translation>
 <translation id="2870560284913253234">Situs</translation>
 <translation id="2874939134665556319">Lagu sebelumnya</translation>
 <translation id="2876369937070532032">Mengirimkan URL beberapa halaman yang Anda kunjungi ke Google, jika keamanan Anda berisiko</translation>
diff --git a/chrome/android/java/strings/translations/android_chrome_strings_iw.xtb b/chrome/android/java/strings/translations/android_chrome_strings_iw.xtb
index 320fd1d..4aac7c54 100644
--- a/chrome/android/java/strings/translations/android_chrome_strings_iw.xtb
+++ b/chrome/android/java/strings/translations/android_chrome_strings_iw.xtb
@@ -80,7 +80,7 @@
 <translation id="1513352483775369820">סימניות והיסטוריית אתרים</translation>
 <translation id="1513858653616922153">מחק סיסמה</translation>
 <translation id="1516229014686355813">‏התכונה 'הקשה כדי לחפש' שולחת אל חיפוש Google את המילה הנבחרת, יחד עם הדף הנוכחי בתור הקשר. ניתן לכבות תכונה זאת ב<ph name="BEGIN_LINK" />הגדרות<ph name="END_LINK" />.</translation>
-<translation id="1521774566618522728">שימוש אחרון היום</translation>
+<translation id="1521774566618522728">שימוש אחרון: היום</translation>
 <translation id="1539064842193522527">‏הקישור נפתח ב-Chrome</translation>
 <translation id="1549000191223877751">העבר לחלון האחר</translation>
 <translation id="1553358976309200471">‏כדאי לעדכן את Chrome</translation>
diff --git a/chrome/android/java/strings/translations/android_chrome_strings_ja.xtb b/chrome/android/java/strings/translations/android_chrome_strings_ja.xtb
index ea3c2be0..bfef17b 100644
--- a/chrome/android/java/strings/translations/android_chrome_strings_ja.xtb
+++ b/chrome/android/java/strings/translations/android_chrome_strings_ja.xtb
@@ -80,7 +80,7 @@
 <translation id="1513352483775369820">ブックマークとウェブ履歴</translation>
 <translation id="1513858653616922153">パスワードを削除</translation>
 <translation id="1516229014686355813">「タップして検索」では選択した単語と現在のページがコンテキストとして Google 検索に送信されます。これは [<ph name="BEGIN_LINK" />設定<ph name="END_LINK" />] で無効にすることができます。</translation>
-<translation id="1521774566618522728">オンライン: 今日</translation>
+<translation id="1521774566618522728">最終同期: 今日</translation>
 <translation id="1539064842193522527">リンクが Chrome で開かれました</translation>
 <translation id="1549000191223877751">他のウィンドウに移動</translation>
 <translation id="1553358976309200471">Chrome を更新</translation>
diff --git a/chrome/android/java/strings/xr_consent_ui_strings_java.grdp b/chrome/android/java/strings/xr_consent_ui_strings_java.grdp
index 2f6a0a64..880fb8f 100644
--- a/chrome/android/java/strings/xr_consent_ui_strings_java.grdp
+++ b/chrome/android/java/strings/xr_consent_ui_strings_java.grdp
@@ -8,11 +8,11 @@
   </message>
   <message name="IDS_XR_CONSENT_DIALOG_DESCRIPTION" desc="Body of the user consent dialog displayed before allowing a website to start a VR presentation">
     Sensor data will only be shared while you're in this VR experience. The site may be able to learn about you using certain info, such as:
-  - Your location
-  - Your physical features, like eye position
-  - Your movements, like how you walk
+    - Your location
+    - Your physical features, like eye position
+    - Your movements, like how you walk
 
-Make sure you trust this site before you allow access.
+    Make sure you trust this site before you allow access.
   </message>
   <message name="IDS_XR_CONSENT_DIALOG_BUTTON_DENY_VR" desc="Text on the button of a user consent dialog which denies a website from starting a VR presentation">
     Don&#39;t allow
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedAppLifecycleTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedAppLifecycleTest.java
index ca7a590..fcec227 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedAppLifecycleTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedAppLifecycleTest.java
@@ -5,8 +5,11 @@
 package org.chromium.chrome.browser.feed;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.anyString;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -22,6 +25,8 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -51,7 +56,6 @@
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
-import java.util.Map;
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -59,10 +63,10 @@
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
-@EnableFeatures({ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS})
 public class FeedAppLifecycleTest {
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+
     @Mock
     private FeedScheduler mFeedScheduler;
     @Mock
@@ -72,7 +76,11 @@
     @Mock
     private AppLifecycleListener mAppLifecycleListener;
     @Mock
-    private Map<String, Boolean> mMockFeatureList;
+    private FeedAppLifecycle.TaskDelegate mTestDelegate;
+
+    @Captor
+    ArgumentCaptor<Runnable> mRunnableCaptor;
+
     private ChromeTabbedActivity mActivity;
     private FeedAppLifecycle mAppLifecycle;
     private FeedLifecycleBridge mLifecycleBridge;
@@ -82,8 +90,6 @@
     @Before
     public void setUp() throws InterruptedException {
         MockitoAnnotations.initMocks(this);
-        when(mMockFeatureList.get(anyString())).thenReturn(true);
-        ChromeFeatureList.setTestFeatures(mMockFeatureList);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             try {
                 ChromeBrowserInitializer.getInstance().handleSynchronousStartup();
@@ -92,8 +98,8 @@
             }
             Profile profile = Profile.getLastUsedProfile().getOriginalProfile();
             mLifecycleBridge = new FeedLifecycleBridge(profile);
-            mAppLifecycle =
-                    new FeedAppLifecycle(mAppLifecycleListener, mLifecycleBridge, mFeedScheduler);
+            mAppLifecycle = new FeedAppLifecycle(
+                    mAppLifecycleListener, mLifecycleBridge, mFeedScheduler, mTestDelegate);
             FeedProcessScopeFactory.createFeedProcessScopeForTesting(mFeedScheduler, mNetworkClient,
                     mOfflineIndicator, mAppLifecycle,
                     new FeedLoggingBridge(profile));
@@ -105,14 +111,14 @@
 
     @Test
     @SmallTest
-    @Feature({"InterestFeedContentSuggestions"})
+    @Feature({"Feed"})
     public void testConstructionChecksActiveTabbedActivities() {
         verify(mAppLifecycleListener, times(1)).onEnterForeground();
     }
 
     @Test
     @SmallTest
-    @Feature({"InterestFeedContentSuggestions"})
+    @Feature({"Feed"})
     public void testActivityStateChangesIncrementStateCounters()
             throws InterruptedException, TimeoutException {
         verifyHistogram(mHistogramAppLifecycleEvents, AppLifecycleEvent.ENTER_BACKGROUND, 0);
@@ -128,7 +134,8 @@
 
     @Test
     @SmallTest
-    @Feature({"InterestFeedContentSuggestions"})
+    @Feature({"Feed"})
+    @EnableFeatures({ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS})
     public void testNtpOpeningTriggersInitializeOnlyOnce() throws InterruptedException {
         // We open to about:blank initially so we shouldn't have called initialize() yet.
         verify(mAppLifecycleListener, times(0)).initialize();
@@ -143,7 +150,7 @@
 
     @Test
     @SmallTest
-    @Feature({"InterestFeedContentSuggestions"})
+    @Feature({"Feed"})
     public void testOnHistoryDeleted() {
         verify(mAppLifecycleListener, times(0)).onClearAll();
         verify(mAppLifecycleListener, times(0)).onClearAllWithRefresh();
@@ -167,7 +174,7 @@
 
     @Test
     @SmallTest
-    @Feature({"InterestFeedContentSuggestions"})
+    @Feature({"Feed"})
     public void testOnCachedDataCleared() {
         verify(mAppLifecycleListener, times(0)).onClearAll();
         verify(mAppLifecycleListener, times(0)).onClearAllWithRefresh();
@@ -189,7 +196,7 @@
 
     @Test
     @SmallTest
-    @Feature({"InterestFeedContentSuggestions"})
+    @Feature({"Feed"})
     public void testOnSignedOut() {
         verify(mAppLifecycleListener, times(0)).onClearAll();
         verify(mAppLifecycleListener, times(0)).onClearAllWithRefresh();
@@ -211,7 +218,7 @@
 
     @Test
     @SmallTest
-    @Feature({"InterestFeedContentSuggestions"})
+    @Feature({"Feed"})
     public void testOnSignedIn() {
         verify(mAppLifecycleListener, times(0)).onClearAll();
         verify(mAppLifecycleListener, times(0)).onClearAllWithRefresh();
@@ -233,7 +240,7 @@
 
     @Test
     @SmallTest
-    @Feature({"InterestFeedContentSuggestions"})
+    @Feature({"Feed"})
     public void testSecondWindowDoesNotTriggerForegroundOrBackground()
             throws InterruptedException, TimeoutException {
         verify(mAppLifecycleListener, times(1)).onEnterForeground();
@@ -258,7 +265,8 @@
 
     @Test
     @SmallTest
-    @Feature({"InterestFeedContentSuggestions"})
+    @Feature({"Feed"})
+    @EnableFeatures({ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS})
     public void testMultiWindowDoesNotCauseMultipleInitialize() throws InterruptedException {
         mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
         verify(mAppLifecycleListener, times(1)).initialize();
@@ -272,7 +280,7 @@
 
     @Test
     @SmallTest
-    @Feature({"InterestFeedContentSuggestions"})
+    @Feature({"Feed"})
     public void testResumeTriggersSchedulerForegrounded()
             throws InterruptedException, TimeoutException {
         verify(mFeedScheduler, times(1)).onForegrounded();
@@ -282,7 +290,7 @@
 
     @Test
     @SmallTest
-    @Feature({"InterestFeedContentSuggestions"})
+    @Feature({"Feed"})
     public void testClearDataAfterDisablingDoesNotCrash() {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             FeedProcessScopeFactory.clearFeedProcessScopeForTesting();
@@ -292,6 +300,72 @@
         });
     }
 
+    @Test
+    @SmallTest
+    @Feature({"Feed"})
+    public void testDelayedInitNoParam() {
+        verify(mAppLifecycleListener, times(1)).onEnterForeground();
+        verify(mAppLifecycleListener, times(0)).initialize();
+        verify(mTestDelegate, never()).postDelayedTask(any(), any(), anyLong());
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Feed"})
+    @CommandLineFlags.
+    Add({"enable-features=InterestFeedContentSuggestions<Trial", "force-fieldtrials=Trial/Group",
+            "force-fieldtrial-params=Trial.Group:init_feed_after_delay_ms/99"})
+    public void
+    testDelayedInitWithParam() {
+        verify(mAppLifecycleListener, times(1)).onEnterForeground();
+        verify(mAppLifecycleListener, times(0)).initialize();
+        verify(mTestDelegate, times(1))
+                .postDelayedTask(
+                        eq(UiThreadTaskTraits.BEST_EFFORT), mRunnableCaptor.capture(), eq(99L));
+        mRunnableCaptor.getValue().run();
+        verify(mAppLifecycleListener, times(1)).initialize();
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Feed"})
+    @CommandLineFlags.
+    Add({"enable-features=InterestFeedContentSuggestions<Trial", "force-fieldtrials=Trial/Group",
+            "force-fieldtrial-params=Trial.Group:init_feed_after_delay_ms/0"})
+    public void
+    testDelayedInitZeroParam() {
+        verify(mAppLifecycleListener, times(1)).onEnterForeground();
+        // While the real implementation will likely synchronously invoke the callback when given a
+        // delay of 0, our mocks have no logic in them.
+        verify(mAppLifecycleListener, times(0)).initialize();
+        verify(mTestDelegate, times(1))
+                .postDelayedTask(
+                        eq(UiThreadTaskTraits.BEST_EFFORT), mRunnableCaptor.capture(), eq(0L));
+        mRunnableCaptor.getValue().run();
+        verify(mAppLifecycleListener, times(1)).initialize();
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Feed"})
+    @CommandLineFlags.
+    Add({"enable-features=InterestFeedContentSuggestions<Trial", "force-fieldtrials=Trial/Group",
+            "force-fieldtrial-params=Trial.Group:init_feed_after_delay_ms/99"})
+    public void
+    testDelayedInitWithDestroy() {
+        verify(mAppLifecycleListener, times(1)).onEnterForeground();
+        verify(mAppLifecycleListener, times(0)).initialize();
+        verify(mTestDelegate, times(1))
+                .postDelayedTask(
+                        eq(UiThreadTaskTraits.BEST_EFFORT), mRunnableCaptor.capture(), eq(99L));
+        // Must be on the UI thread, one of the dependencies checks.
+        TestThreadUtils.runOnUiThreadBlocking(() -> mAppLifecycle.destroy());
+
+        // Initialize shouldn't be called after we're destroyed.
+        mRunnableCaptor.getValue().run();
+        verify(mAppLifecycleListener, never()).initialize();
+    }
+
     private void signalActivityStart(Activity activity)
             throws InterruptedException, TimeoutException {
         signalActivityState(activity, ActivityState.STARTED);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md
index d263575..3b85860 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md
@@ -161,14 +161,21 @@
 `--shared-prefs-file path/to/preference/json/file`
 
 Configures VrCore according to the provided file, e.g. changing the paired
-headset. The two most common files used are:
+headset. The currently supported files are:
 
 * `//chrome/android/shared_preference_files/test/vr_cardboard_skipdon_setupcomplete.json`
-  This will pair the device with a Cardboard headset and disable controller
-  emulation.
+  will cause all cardboard-compatible tests to run. This will pair the device
+  with a Cardboard headset and disable controller emulation.
 * `//chrome/android/shared_preference_files/test/vr_ddview_skipdon_setupcomplete.json`
-  This will pair the device with a Daydream View headset, set the DON flow to be
-  skipped, and enable controller emulation.
+  will cause most Daydream View-compatible tests to run, with the exception of
+  those that require the DON flow to be enabled. This will pair the device with
+  a Daydream View headset, set the DON flow to be skipped, and enable controller
+  emulation.
+* `//chrome/android/shared_preference_files/test/vr_enable_vr_settings_service.json`
+  combined with the extra `--vr-settings-service-enabled` and
+  `--annotation=Restriction=VR_Settings_Service` flags will cause all tests that
+  are using the `RESTRICTION_TYPE_VR_SETTINGS_SERVICE` restriction to run. See
+  the section below for more detail on this.
 
 The test runner will automatically revert any changed settings back to their
 pre-test values after the test suite has completed. If for whatever reason you
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTestFramework.java
index 718f905..91a28a5c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTestFramework.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTestFramework.java
@@ -23,6 +23,14 @@
         }
     }
 
+    @Override
+    public void enterSessionWithUserGesture() {
+        super.enterSessionWithUserGesture();
+
+        PermissionUtils.waitForConsentPrompt(getRule().getActivity());
+        PermissionUtils.acceptConsentPrompt(getRule().getActivity());
+    }
+
     /**
      * VR-specific implementation of enterSessionWithUserGesture that includes a workaround for
      * receiving broadcasts late.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java
index b1d59c7..cdd01eb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java
@@ -38,7 +38,6 @@
 import org.chromium.base.test.params.ParameterSet;
 import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.UrlUtils;
@@ -207,7 +206,6 @@
     @LargeTest
     @Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM)
     @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL})
-    @DisabledTest(message = "https://crbug.com/969024")
     public void testNfcFiresVrdisplayactivate() throws InterruptedException {
         mWebVrTestFramework.loadUrlAndAwaitInitialization(
                 WebVrTestFramework.getFileUrlForHtmlTestFile("test_nfc_fires_vrdisplayactivate"),
@@ -288,7 +286,6 @@
     @MediumTest
     @Restriction({RESTRICTION_TYPE_DEVICE_DAYDREAM, RESTRICTION_TYPE_VR_SETTINGS_SERVICE})
     @VrSettingsFile(VrSettingsServiceUtils.FILE_DDVIEW_DONENABLED)
-    @DisabledTest(message = "https://crbug.com/969024")
     @CommandLineFlags
             .Remove({"enable-webvr"})
             @CommandLineFlags.Add({"enable-features=WebXR"})
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webshare/OWNERS b/chrome/android/javatests/src/org/chromium/chrome/browser/webshare/OWNERS
new file mode 100644
index 0000000..498e4f5
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webshare/OWNERS
@@ -0,0 +1 @@
+file://chrome/android/java/src/org/chromium/chrome/browser/webshare/OWNERS
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/WebShareTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webshare/WebShareTest.java
similarity index 98%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/WebShareTest.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/webshare/WebShareTest.java
index e8775a4..3a461553 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/WebShareTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webshare/WebShareTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser;
+package org.chromium.chrome.browser.webshare;
 
 import android.content.Context;
 import android.content.Intent;
@@ -23,6 +23,8 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.share.ShareHelper;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
@@ -108,7 +110,6 @@
         // Clean up some state that might have been changed by tests.
         ShareHelper.setForceCustomChooserForTesting(false);
         ShareHelper.setFakeIntentReceiverForTesting(null);
-
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/GridTabSwitcherLayoutPerfTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/GridTabSwitcherLayoutPerfTest.java
index e8a6f49..3ef2c532d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/GridTabSwitcherLayoutPerfTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/GridTabSwitcherLayoutPerfTest.java
@@ -53,7 +53,7 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
         "enable-features=" + ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study",
-        "force-fieldtrials=Study/Group", "force-fieldtrial-params=Study.Group:cleanup-delay/0"})
+        "force-fieldtrials=Study/Group"})
 @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
 public class GridTabSwitcherLayoutPerfTest {
     private static final String TAG = "GTSLayoutTest";
@@ -84,7 +84,7 @@
         assertTrue(layout instanceof GridTabSwitcherLayout);
         mGtsLayout = (GridTabSwitcherLayout) layout;
         mUrl = mTestServer.getURL("/chrome/test/data/android/navigate/simple.html");
-        mRepeat = 2;
+        mRepeat = 3;
         mWaitingTime = 0;
         mTabNumCap = 3;
 
@@ -98,6 +98,8 @@
 
     @Test
     @MediumTest
+    @CommandLineFlags.
+    Add({"force-fieldtrial-params=Study.Group:soft-cleanup-delay/0/cleanup-delay/0"})
     public void testTabToGridFromLiveTab() throws InterruptedException {
         prepareTabs(1, NTP_URL);
         reportTabToGridPerf(mUrl, "Tab-to-Grid from live tab");
@@ -105,6 +107,8 @@
 
     @Test
     @MediumTest
+    @CommandLineFlags.
+    Add({"force-fieldtrial-params=Study.Group:soft-cleanup-delay/0/cleanup-delay/0"})
     public void testTabToGridFromLiveTabWith10Tabs() throws InterruptedException {
         prepareTabs(10, NTP_URL);
         reportTabToGridPerf(mUrl, "Tab-to-Grid from live tab with 10 tabs");
@@ -112,7 +116,8 @@
 
     @Test
     @MediumTest
-    @CommandLineFlags.Add({"force-fieldtrial-params=Study.Group:cleanup-delay/10000"})
+    @CommandLineFlags.
+    Add({"force-fieldtrial-params=Study.Group:soft-cleanup-delay/10000/cleanup-delay/10000"})
     public void testTabToGridFromLiveTabWith10TabsWarm() throws InterruptedException {
         prepareTabs(10, NTP_URL);
         reportTabToGridPerf(mUrl, "Tab-to-Grid from live tab with 10 tabs (warm)");
@@ -120,7 +125,17 @@
 
     @Test
     @MediumTest
-    @CommandLineFlags.Add({"force-fieldtrial-params=Study.Group:downsampling-scale/1"})
+    @CommandLineFlags.
+    Add({"force-fieldtrial-params=Study.Group:soft-cleanup-delay/0/cleanup-delay/10000"})
+    public void testTabToGridFromLiveTabWith10TabsSoft() throws InterruptedException {
+        prepareTabs(10, NTP_URL);
+        reportTabToGridPerf(mUrl, "Tab-to-Grid from live tab with 10 tabs (soft)");
+    }
+
+    @Test
+    @MediumTest
+    @CommandLineFlags.
+    Add({"force-fieldtrial-params=Study.Group:downsampling-scale/1/soft-cleanup-delay/0/cleanup-delay/0"})
     public void testTabToGridFromLiveTabWith10TabsNoDownsample() throws InterruptedException {
         prepareTabs(10, NTP_URL);
         reportTabToGridPerf(mUrl, "Tab-to-Grid from live tab with 10 tabs (no downsample)");
@@ -128,6 +143,8 @@
 
     @Test
     @MediumTest
+    @CommandLineFlags.
+    Add({"force-fieldtrial-params=Study.Group:soft-cleanup-delay/0/cleanup-delay/0"})
     public void testTabToGridFromLiveTabWith10TabsWithoutThumbnail() throws InterruptedException {
         // Note that most of the tabs won't have thumbnails.
         prepareTabs(10, null);
@@ -136,6 +153,8 @@
 
     @Test
     @LargeTest
+    @CommandLineFlags.
+    Add({"force-fieldtrial-params=Study.Group:soft-cleanup-delay/0/cleanup-delay/0"})
     public void testTabToGridFromLiveTabWith100Tabs() throws InterruptedException {
         // Skip waiting for loading. Otherwise it would take too long.
         // Note that most of the tabs won't have thumbnails.
@@ -145,6 +164,8 @@
 
     @Test
     @MediumTest
+    @CommandLineFlags.
+    Add({"force-fieldtrial-params=Study.Group:soft-cleanup-delay/0/cleanup-delay/0"})
     public void testTabToGridFromNtp() throws InterruptedException {
         prepareTabs(1, NTP_URL);
         reportTabToGridPerf(NTP_URL, "Tab-to-Grid from NTP");
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java
index 06616212..e6ad5d76 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java
@@ -212,7 +212,6 @@
         doReturn(true).when(mAccountTrackerService).checkAndSeedSystemAccounts();
         // Request that policy is loaded. It will pause sign-in until onPolicyCheckedBeforeSignIn is
         // invoked.
-        doReturn(true).when(mNativeMock).shouldLoadPolicyForUser(any());
         doNothing().when(mNativeMock).registerAndFetchPolicyBeforeSignIn(any(), anyLong(), any());
 
         doReturn(true).when(mSigninManager).isSigninSupported();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webshare/OWNERS b/chrome/android/junit/src/org/chromium/chrome/browser/webshare/OWNERS
new file mode 100644
index 0000000..498e4f5
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webshare/OWNERS
@@ -0,0 +1 @@
+file://chrome/android/java/src/org/chromium/chrome/browser/webshare/OWNERS
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webshare/ShareServiceImplTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webshare/ShareServiceImplTest.java
index 3c9632085..6a5f9008 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webshare/ShareServiceImplTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webshare/ShareServiceImplTest.java
@@ -21,15 +21,18 @@
 public class ShareServiceImplTest {
     @Test
     @SmallTest
-    public void testSlash() {
-        Assert.assertTrue(ShareServiceImpl.isDangerousFilename("foo/bar.txt"));
-        Assert.assertTrue(ShareServiceImpl.isDangerousFilename("foo\\bar.txt"));
-    }
-
-    @Test
-    @SmallTest
-    public void testPeriod() {
+    public void testExtensionFormatting() {
+        Assert.assertFalse(ShareServiceImpl.isDangerousFilename("foo/bar.txt"));
+        Assert.assertFalse(ShareServiceImpl.isDangerousFilename("foo\\bar\u03C0.txt"));
+        Assert.assertTrue(ShareServiceImpl.isDangerousFilename("foo\\bar.tx\u03C0t"));
+        Assert.assertFalse(ShareServiceImpl.isDangerousFilename("https://example.com/a/b.html"));
+        Assert.assertTrue(ShareServiceImpl.isDangerousFilename("foo/bar.txt/"));
+        Assert.assertTrue(ShareServiceImpl.isDangerousFilename("foobar.tx\\t"));
         Assert.assertTrue(ShareServiceImpl.isDangerousFilename("hello"));
+        Assert.assertTrue(ShareServiceImpl.isDangerousFilename("hellotxt"));
+        Assert.assertTrue(ShareServiceImpl.isDangerousFilename(".txt"));
+        Assert.assertFalse(ShareServiceImpl.isDangerousFilename("https://example.com/a/.txt"));
+        Assert.assertFalse(ShareServiceImpl.isDangerousFilename("/.txt"));
         Assert.assertTrue(ShareServiceImpl.isDangerousFilename(".."));
         Assert.assertTrue(ShareServiceImpl.isDangerousFilename(".hello.txt"));
     }
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index c85bacbf..e3694769 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-77.0.3815.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-77.0.3816.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/android/webapk/PRESUBMIT.py b/chrome/android/webapk/PRESUBMIT.py
index 9a1474d4..85c83eb 100644
--- a/chrome/android/webapk/PRESUBMIT.py
+++ b/chrome/android/webapk/PRESUBMIT.py
@@ -36,7 +36,11 @@
     'shell_apk/src/',
 ]
 
-RES_DIR_LOCAL_PATHS = ['shell_apk/res', 'libs/common/res_splash']
+RES_DIR_LOCAL_PATHS = [
+    'shell_apk/res',
+    'shell_apk/res_template',
+    'libs/common/res_splash'
+]
 
 def _DoChangedContentsContain(changed_contents, key):
   for _, line in changed_contents:
diff --git a/chrome/android/webapk/PRESUBMIT_test.py b/chrome/android/webapk/PRESUBMIT_test.py
index 674f780..4dc6f3e77 100755
--- a/chrome/android/webapk/PRESUBMIT_test.py
+++ b/chrome/android/webapk/PRESUBMIT_test.py
@@ -108,7 +108,8 @@
 class OverlappingResourceFileNames(unittest.TestCase):
    RESOURCES_SHOULD_HAVE_DIFFERENT_FILE_NAMES_MESSAGE = (
        'Resources in different top level res/ directories [\'shell_apk/res\', '
-       '\'libs/common/res_splash\'] should have different names:')
+       '\'shell_apk/res_template\', \'libs/common/res_splash\'] should have '
+       'different names:')
 
    def testAddFileSameNameWithinResDirectory(self):
      # Files within a res/ directory can have same file name.
diff --git a/chrome/android/webapk/shell_apk/BUILD.gn b/chrome/android/webapk/shell_apk/BUILD.gn
index 24e8a86..414e3eb0 100644
--- a/chrome/android/webapk/shell_apk/BUILD.gn
+++ b/chrome/android/webapk/shell_apk/BUILD.gn
@@ -4,7 +4,7 @@
 
 import("//build/config/android/rules.gni")
 import("current_version/current_version.gni")
-import("manifest/manifest_mustache_pass.gni")
+import("mustache_pass.gni")
 
 webapk_manifest_to_upload_output =
     "${target_gen_dir}/webapk_manifest_to_upload/AndroidManifest.xml"
@@ -14,12 +14,6 @@
 # GN breaks with a mysterious error if the target has the word "junit" in it.
 h2o_junit_manifest_target_name = "h2o_j_unit_manifest_target"
 
-android_resources("new_splash_resources") {
-  custom_package = "org.chromium.webapk.shell_apk.h2o"
-  resource_dirs = [ "res_h2o" ]
-  deps = []
-}
-
 # Stamped out copy of the runtime-library, used for fail-safe code in when using an
 # old copy of the runtime library.
 android_library("compiled_in_runtime_library_java") {
@@ -52,21 +46,18 @@
       "src/org/chromium/webapk/shell_apk/TransparentLauncherActivity.java",
       "src/org/chromium/webapk/shell_apk/WebApkSharedPreferences.java",
       "src/org/chromium/webapk/shell_apk/WebApkUtils.java",
+      "src/org/chromium/webapk/shell_apk/h2o/H2OLauncher.java",
+      "src/org/chromium/webapk/shell_apk/h2o/H2OMainActivity.java",
+      "src/org/chromium/webapk/shell_apk/h2o/H2OOpaqueMainActivity.java",
+      "src/org/chromium/webapk/shell_apk/h2o/H2OTransparentLauncherActivity.java",
+      "src/org/chromium/webapk/shell_apk/h2o/SplashActivity.java",
+      "src/org/chromium/webapk/shell_apk/h2o/SplashContentProvider.java",
+      "src/org/chromium/webapk/shell_apk/h2o/SplashUtils.java",
     ]
-    deps += [ "//chrome/android/webapk/libs/common:common_java" ]
-
-    if (invoker.use_new_splash) {
-      java_files += [
-        "src/org/chromium/webapk/shell_apk/h2o/H2OLauncher.java",
-        "src/org/chromium/webapk/shell_apk/h2o/H2OMainActivity.java",
-        "src/org/chromium/webapk/shell_apk/h2o/H2OOpaqueMainActivity.java",
-        "src/org/chromium/webapk/shell_apk/h2o/H2OTransparentLauncherActivity.java",
-        "src/org/chromium/webapk/shell_apk/h2o/SplashActivity.java",
-        "src/org/chromium/webapk/shell_apk/h2o/SplashContentProvider.java",
-        "src/org/chromium/webapk/shell_apk/h2o/SplashUtils.java",
-      ]
-      deps += [ "//chrome/android/webapk/libs/common:splash_java" ]
-    }
+    deps += [
+      "//chrome/android/webapk/libs/common:common_java",
+      "//chrome/android/webapk/libs/common:splash_java",
+    ]
   }
 }
 
@@ -96,6 +87,10 @@
   _java_without_services_target_name = "${target_name}_generated_webapk_java"
   _java_with_services_target_name =
       "${target_name}_generated_webapk_with_service_java"
+  _generate_res_background_xml_target_name =
+      "${target_name}_generate_res_background_xml"
+  _generated_res_background_dir =
+      "${target_gen_dir}/${_generate_res_background_xml_target_name}/res"
   _resources_target_name = "${target_name}_resources"
 
   _use_new_splash = false
@@ -115,7 +110,7 @@
 
   # Generate manifest to upload to WebAPK server. Fills in all of the fields
   # that the server cannot customize on a per-site basis.
-  manifest_mustache_pass(_manifest_to_upload_target_name) {
+  mustache_pass(_manifest_to_upload_target_name) {
     input = "AndroidManifest.xml"
     output = _manifest_to_upload_output
     extra_variables = [
@@ -125,7 +120,7 @@
   }
 
   # Generate manifest with test values.
-  manifest_mustache_pass(_manifest_target_name) {
+  mustache_pass(_manifest_target_name) {
     forward_variables_from(invoker,
                            [
                              "apk_package_name",
@@ -141,20 +136,27 @@
     ]
   }
 
+  # Populate background color in values/background_color.xml
+  mustache_pass(_generate_res_background_xml_target_name) {
+    forward_variables_from(invoker, [ "config_file" ])
+    input = "res_template/values/background_color.xml"
+    output = "${_generated_res_background_dir}/values/background_color.xml"
+  }
+
   android_resources(_resources_target_name) {
     custom_package = "org.chromium.webapk.shell_apk"
     resource_dirs = [ "res" ]
+    generated_resource_dirs = [ _generated_res_background_dir ]
+    generated_resource_files =
+        get_target_outputs(":$_generate_res_background_xml_target_name")
     deps = [
+      ":$_generate_res_background_xml_target_name",
       ":webapk_strings_grd",
     ]
-    if (_use_new_splash) {
-      deps += [ ":new_splash_resources" ]
-    }
   }
 
   webapk_java(_java_without_services_target_name) {
     android_manifest_for_lint = _manifest_output
-    use_new_splash = _use_new_splash
     deps = [
       ":$_manifest_target_name",
       ":$_resources_target_name",
diff --git a/chrome/android/webapk/shell_apk/current_version/current_version.gni b/chrome/android/webapk/shell_apk/current_version/current_version.gni
index 51b231c..28b754e5 100644
--- a/chrome/android/webapk/shell_apk/current_version/current_version.gni
+++ b/chrome/android/webapk/shell_apk/current_version/current_version.gni
@@ -12,4 +12,4 @@
 # //chrome/android/webapk/shell_apk:webapk is changed. This includes
 # Java files, Android resource files and AndroidManifest.xml. Does not affect
 # Chrome.apk
-current_shell_apk_version = 92
+current_shell_apk_version = 94
diff --git a/chrome/android/webapk/shell_apk/manifest/bound_manifest_config.json b/chrome/android/webapk/shell_apk/manifest/bound_manifest_config.json
index e371d8ba..c665f630 100644
--- a/chrome/android/webapk/shell_apk/manifest/bound_manifest_config.json
+++ b/chrome/android/webapk/shell_apk/manifest/bound_manifest_config.json
@@ -11,6 +11,7 @@
   "orientation": "portrait",
   "theme_color": "2147483648L",
   "background_color": "2147483648L",
+  "background_color_xml": "#F8F9FA",
   "icon_urls_and_icon_murmur2_hashes": "http://www.pwa.rocks/icon1.png 0 http://www.pwa.rocks/icon2.png 0",
   "web_manifest_url": "https://pwa.rocks/pwa.webmanifest",
   "distributor": "browser",
diff --git a/chrome/android/webapk/shell_apk/manifest/maps_go_manifest_config.json b/chrome/android/webapk/shell_apk/manifest/maps_go_manifest_config.json
index da8b4cd..9a5b4a82 100644
--- a/chrome/android/webapk/shell_apk/manifest/maps_go_manifest_config.json
+++ b/chrome/android/webapk/shell_apk/manifest/maps_go_manifest_config.json
@@ -11,6 +11,7 @@
   "orientation": "portrait",
   "theme_color": "2147483648L",
   "background_color": "2147483648L",
+  "background_color_xml": "#F8F9FA",
   "icon_urls_and_icon_murmur2_hashes": "https://maps.gstatic.com/mapfiles/maps_lite/pwa/icons/maps_pwa_icon_v0920_48x48.png 0 https://maps.gstatic.com/mapfiles/maps_lite/pwa/icons/maps_pwa_icon_v0920_72x72 0",
   "web_manifest_url": "https://maps.gstatic.com/tactile/worker/ml.json",
   "distributor": "other",
diff --git a/chrome/android/webapk/shell_apk/manifest/unbound_manifest_config.json b/chrome/android/webapk/shell_apk/manifest/unbound_manifest_config.json
index e4620e4..887c021 100644
--- a/chrome/android/webapk/shell_apk/manifest/unbound_manifest_config.json
+++ b/chrome/android/webapk/shell_apk/manifest/unbound_manifest_config.json
@@ -12,6 +12,7 @@
   "orientation": "portrait",
   "theme_color": "2147483648L",
   "background_color": "2147483648L",
+  "background_color_xml": "#F8F9FA",
   "icon_urls_and_icon_murmur2_hashes": "http://www.pwa.rocks/icon1.png 0 http://www.pwa.rocks/icon2.png 0",
   "web_manifest_url": "https://pwa.rocks/pwa.webmanifest",
   "distributor": "other",
diff --git a/chrome/android/webapk/shell_apk/manifest/manifest_mustache_pass.gni b/chrome/android/webapk/shell_apk/mustache_pass.gni
similarity index 91%
rename from chrome/android/webapk/shell_apk/manifest/manifest_mustache_pass.gni
rename to chrome/android/webapk/shell_apk/mustache_pass.gni
index d4f7769b..244575cd 100644
--- a/chrome/android/webapk/shell_apk/manifest/manifest_mustache_pass.gni
+++ b/chrome/android/webapk/shell_apk/mustache_pass.gni
@@ -4,7 +4,7 @@
 
 import("//build/config/android/rules.gni")
 
-template("manifest_mustache_pass") {
+template("mustache_pass") {
   set_sources_assignment_filter([])
   forward_variables_from(invoker, [ "testonly" ])
 
@@ -27,8 +27,7 @@
       sources += [ invoker.delta_config_file ]
     }
 
-    script =
-        "//chrome/android/webapk/shell_apk/manifest/manifest_mustache_pass.py"
+    script = "//chrome/android/webapk/shell_apk/mustache_pass.py"
 
     outputs = [
       invoker.output,
diff --git a/chrome/android/webapk/shell_apk/manifest/manifest_mustache_pass.py b/chrome/android/webapk/shell_apk/mustache_pass.py
similarity index 94%
rename from chrome/android/webapk/shell_apk/manifest/manifest_mustache_pass.py
rename to chrome/android/webapk/shell_apk/mustache_pass.py
index e1be178..abce291 100755
--- a/chrome/android/webapk/shell_apk/manifest/manifest_mustache_pass.py
+++ b/chrome/android/webapk/shell_apk/mustache_pass.py
@@ -4,7 +4,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Expands the ShellApk's AndroidManifest.xml using Mustache template engine."""
+"""Expands template using Mustache template engine."""
 
 import argparse
 import codecs
@@ -14,7 +14,7 @@
 
 #Import pystache from //third_party/pystache
 src_dir = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
-                       os.pardir, os.pardir, os.pardir)
+                       os.pardir, os.pardir)
 sys.path.insert(0, os.path.join(src_dir, 'third_party'))
 import pystache
 sys.path.insert(0, os.path.join(src_dir, 'build/android/gyp'))
diff --git a/chrome/android/webapk/shell_apk/res_h2o/values-v17/styles.xml b/chrome/android/webapk/shell_apk/res/values-v17/styles.xml
similarity index 100%
rename from chrome/android/webapk/shell_apk/res_h2o/values-v17/styles.xml
rename to chrome/android/webapk/shell_apk/res/values-v17/styles.xml
diff --git a/chrome/android/webapk/shell_apk/res/values/colors.xml b/chrome/android/webapk/shell_apk/res/values/colors.xml
index e4539560..54fde41 100644
--- a/chrome/android/webapk/shell_apk/res/values/colors.xml
+++ b/chrome/android/webapk/shell_apk/res/values/colors.xml
@@ -8,4 +8,4 @@
     <color name="black_alpha_38">#61000000</color>
     <color name="black_alpha_54">#8A000000</color>
     <color name="black_alpha_87">#DE000000</color>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/chrome/android/webapk/shell_apk/res_h2o/values/colors.xml b/chrome/android/webapk/shell_apk/res_template/values/background_color.xml
similarity index 77%
rename from chrome/android/webapk/shell_apk/res_h2o/values/colors.xml
rename to chrome/android/webapk/shell_apk/res_template/values/background_color.xml
index 8c66cac..c087ef02 100644
--- a/chrome/android/webapk/shell_apk/res_h2o/values/colors.xml
+++ b/chrome/android/webapk/shell_apk/res_template/values/background_color.xml
@@ -4,5 +4,5 @@
      found in the LICENSE file. -->
 
 <resources>
-  <color name="background_color">#F8F9FA</color>
+  <color name="background_color">{{{background_color_xml}}}</color>
 </resources>
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index 590c4edc..3d918789 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -621,6 +621,7 @@
   if (is_chromeos) {
     deps += [
       "//ash/public/cpp:manifest",
+      "//chrome/services/ble_scan_parser/public/cpp:manifest",
       "//chrome/services/cups_ipp_parser/public/cpp:manifest",
       "//chrome/services/cups_proxy/public/cpp:manifest",
       "//chromeos/services/cellular_setup/public/cpp:manifest",
diff --git a/chrome/app/DEPS b/chrome/app/DEPS
index 4d5d9b0..026cfae 100644
--- a/chrome/app/DEPS
+++ b/chrome/app/DEPS
@@ -84,6 +84,7 @@
     "+third_party/blink/public/mojom",
   ],
   "builtin_service_manifests\.cc": [
+    "+chrome/services/ble_scan_parser/public",
     "+chrome/services/cups_ipp_parser/public",
     "+chrome/services/cups_proxy/public",
     "+chrome/services/file_util/public",
diff --git a/chrome/app/builtin_service_manifests.cc b/chrome/app/builtin_service_manifests.cc
index 6c9c779..865f68f49 100644
--- a/chrome/app/builtin_service_manifests.cc
+++ b/chrome/app/builtin_service_manifests.cc
@@ -8,6 +8,7 @@
 #include "build/build_config.h"
 #include "chrome/common/buildflags.h"
 #include "chrome/common/constants.mojom.h"
+#include "chrome/services/ble_scan_parser/public/cpp/manifest.h"
 #include "chrome/services/file_util/public/cpp/manifest.h"
 #include "chrome/services/noop/public/cpp/manifest.h"
 #include "components/services/patch/public/cpp/manifest.h"
@@ -174,12 +175,13 @@
 #endif
 #if defined(OS_CHROMEOS)
       ash::GetManifest(),
-      GetCupsIppParserManifest(),
+      ble_scan_parser::GetManifest(),
       chromeos::cellular_setup::GetManifest(),
-      chromeos::printing::GetCupsProxyManifest(),
       chromeos::ime::GetManifest(),
       chromeos::network_config::GetManifest(),
+      chromeos::printing::GetCupsProxyManifest(),
       chromeos::secure_channel::GetManifest(),
+      GetCupsIppParserManifest(),
 #endif
   }};
   return *manifests;
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index bb1e72f..87e04fc 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -3360,7 +3360,11 @@
       <message name="IDS_UTILITY_PROCESS_NOOP_SERVICE_NAME" desc="The name of the utility process which does nothing, used for experimentation.">
         No-op Service
       </message>
-
+      <if expr="chromeos">
+        <message name="IDS_UTILITY_PROCESS_BLE_SCAN_PARSER_NAME" desc="The name of the utility process used for parsing BLE scans.">
+          BLE Scan Parser Service
+        </message>
+      </if>
       <!-- Theme preview infobar -->
       <message name="IDS_THEME_INSTALL_INFOBAR_LABEL" desc="Text displayed on an infobar when a theme has been installed.">
         Installed theme "<ph name="THEME_NAME">$1<ex>Snowflake Theme</ex></ph>"
@@ -3851,6 +3855,9 @@
         <message name="IDS_EXTENSION_PROMPT_WARNING_NEW_TAB_PAGE_OVERRIDE" desc="Permission string for extensions that override the new tab page.">
           Replace the page you see when opening a new tab
         </message>
+        <message name="IDS_EXTENSION_PROMPT_WARNING_TRANSIENT_BACKGROUND" desc="Permission string for transient background permission.">
+          Run in the background when requested by a cooperating native application
+        </message>
       </if>
 
       <!-- Extension/App error messages -->
diff --git a/chrome/app/resources/generated_resources_am.xtb b/chrome/app/resources/generated_resources_am.xtb
index d53afb5..edc7017 100644
--- a/chrome/app/resources/generated_resources_am.xtb
+++ b/chrome/app/resources/generated_resources_am.xtb
@@ -2203,7 +2203,6 @@
 <translation id="4350019051035968019">ይህ መሣሪያ በተለየ ጎራ የሚተዳደር መሆኑ ምልክት ስለተደረገበት የእርስዎ መለያ ባለበት ጎራ መመዝገብ አይችልም።</translation>
 <translation id="4354344420232759511">እርስዎ የጎበኙዋቸው ጣቢያዎች እዚህ ላይ ብቅ ይላሉ</translation>
 <translation id="435527878592612277">የእርስዎን ፎቶ ይምረጡ</translation>
-<translation id="4356334633973342967">ወይም የራስዎን ነጂ ይጠቀሱ፦</translation>
 <translation id="4359408040881008151">በጥገኛ ቅጥያ(ዎች) ምክንያት ተጭኗል።</translation>
 <translation id="4359717112757026264">ሲቲስኬፕ</translation>
 <translation id="4361142739114356624">የዚህ ደንበኛ የእውቅና ማረጋገጫ የሆነው የግል ቁልፍ ይጎድላል ወይም አይሠራም</translation>
diff --git a/chrome/app/resources/generated_resources_ar.xtb b/chrome/app/resources/generated_resources_ar.xtb
index 1a41a53..661a2a8 100644
--- a/chrome/app/resources/generated_resources_ar.xtb
+++ b/chrome/app/resources/generated_resources_ar.xtb
@@ -2185,7 +2185,6 @@
 <translation id="4350019051035968019">لا يمكن تسجيل هذا الجهاز في النطاق الذي ينتمي له حسابك نظرًا لأنه تم تصنيف الجهاز للعمل تحت إدارة نطاق آخر.</translation>
 <translation id="4354344420232759511">ستظهر المواقع التي تزورها هنا.</translation>
 <translation id="435527878592612277">اختيار صورتك</translation>
-<translation id="4356334633973342967">أو تحديد برنامج تشغيلك الخاص:</translation>
 <translation id="4359408040881008151">تم التثبيت بسبب الإضافة (الإضافات) التابعة.</translation>
 <translation id="4359717112757026264">مناظر المدن</translation>
 <translation id="4361142739114356624">المفتاح الخاص لشهادة العميل هذه مفقود أو غير صالح</translation>
diff --git a/chrome/app/resources/generated_resources_bg.xtb b/chrome/app/resources/generated_resources_bg.xtb
index f0b7b40..e8dd12d 100644
--- a/chrome/app/resources/generated_resources_bg.xtb
+++ b/chrome/app/resources/generated_resources_bg.xtb
@@ -2205,7 +2205,6 @@
 <translation id="4350019051035968019">Това устройство не може да бъде регистрирано в домейна, към който принадлежи профилът ви, защото е означено за управление от друг домейн.</translation>
 <translation id="4354344420232759511">Посетените от вас сайтове ще се показват тук</translation>
 <translation id="435527878592612277">Изберете своя снимка</translation>
-<translation id="4356334633973342967">Или ръчно посочете драйвер:</translation>
 <translation id="4359408040881008151">Инсталирано заради зависимо разширение или съответно разширения.</translation>
 <translation id="4359717112757026264">Градски пейзажи</translation>
 <translation id="4361142739114356624">Частният ключ за този клиентски сертификат липсва или е невалиден</translation>
diff --git a/chrome/app/resources/generated_resources_bn.xtb b/chrome/app/resources/generated_resources_bn.xtb
index 01ca603..256d0ac 100644
--- a/chrome/app/resources/generated_resources_bn.xtb
+++ b/chrome/app/resources/generated_resources_bn.xtb
@@ -2202,7 +2202,6 @@
 <translation id="4350019051035968019">এই ডিভাইসটি একটি ভিন্ন ডোমেনের দ্বারা পরিচালনার জন্য চিহ্নিত হওয়ায় আপনার অ্যাকাউন্টটি যে ডোমেনের আওতায় রয়েছে তাতে নথিভুক্ত করা যাবে না৷</translation>
 <translation id="4354344420232759511">আপনার দেখা সাইটগুলি এখানে দেখানো হবে</translation>
 <translation id="435527878592612277">আপনার ফটো বেছে নিন</translation>
-<translation id="4356334633973342967">অথবা আপনার নিজের ড্রাইভার উল্লেখ করুন:</translation>
 <translation id="4359408040881008151">নির্ভরশীল এক্সটেনশন(গুলি) বলে ইনস্টল রয়েছে৷</translation>
 <translation id="4359717112757026264">শহরের দৃশ্য</translation>
 <translation id="4361142739114356624">এই ক্লায়েন্ট সার্টিফিকেটের জন্য ব্যক্তিগত কী পাওয়া যাচ্ছে না বা এটি ভুল</translation>
diff --git a/chrome/app/resources/generated_resources_ca.xtb b/chrome/app/resources/generated_resources_ca.xtb
index 5894e09..43ae2d3 100644
--- a/chrome/app/resources/generated_resources_ca.xtb
+++ b/chrome/app/resources/generated_resources_ca.xtb
@@ -2201,7 +2201,6 @@
 <translation id="4350019051035968019">No es pot inscriure aquest dispositiu al domini del vostre compte perquè s'ha indicat que el dispositiu es gestiona en un altre domini.</translation>
 <translation id="4354344420232759511">Els llocs web que visitis es mostraran aquí</translation>
 <translation id="435527878592612277">Selecciona la teva foto</translation>
-<translation id="4356334633973342967">O especifica el teu controlador:</translation>
 <translation id="4359408040881008151">S'ha instal·lat perquè té extensions que en depenen.</translation>
 <translation id="4359717112757026264">Paisatge urbà</translation>
 <translation id="4361142739114356624">Falta la clau privada d'aquest certificat del client o no és vàlida</translation>
@@ -2290,7 +2289,7 @@
 <translation id="4508051413094283164">Obre-ho tot en una finestra d'incògnit</translation>
 <translation id="4508265954913339219">S'ha produït un error en l'activació.</translation>
 <translation id="4508765956121923607">Visualitza l'&amp;origen</translation>
-<translation id="451407183922382411">Amb tecnologia de: <ph name="COMPANY_NAME" /></translation>
+<translation id="451407183922382411">Amb la tecnologia de: <ph name="COMPANY_NAME" /></translation>
 <translation id="4514542542275172126">Configura un usuari supervisat nou</translation>
 <translation id="4514610446763173167">Reprodueix o posa en pausa el vídeo</translation>
 <translation id="451515744433878153">Suprimeix</translation>
@@ -4076,7 +4075,7 @@
 <translation id="7270858098575133036">Pregunta'm quan un lloc vulgui utilitzar els missatges exclusius del sistema per accedir a dispositius MIDI</translation>
 <translation id="7272674038937250585">No s'ha proporcionat cap descripció</translation>
 <translation id="7273110280511444812">darrera connexió el <ph name="DATE" /></translation>
-<translation id="727441411541283857"><ph name="PERCENTAGE" />% - Temps restant fins que es completi la càrrega: <ph name="TIME" /></translation>
+<translation id="727441411541283857"><ph name="PERCENTAGE" />% - <ph name="TIME" /> per completar la càrrega</translation>
 <translation id="727952162645687754">Error de baixada</translation>
 <translation id="7279701417129455881">Gestiona el bloqueig de galetes...</translation>
 <translation id="7280041992884344566">S'ha produït un error mentre Chrome cercava programari nociu</translation>
diff --git a/chrome/app/resources/generated_resources_cs.xtb b/chrome/app/resources/generated_resources_cs.xtb
index c50baf7..6f3c475 100644
--- a/chrome/app/resources/generated_resources_cs.xtb
+++ b/chrome/app/resources/generated_resources_cs.xtb
@@ -2202,7 +2202,6 @@
 <translation id="4350019051035968019">Toto zařízení nelze zaregistrovat do domény, do které patří váš účet, protože je označeno pro správu jinou doménou.</translation>
 <translation id="4354344420232759511">Zde se zobrazí weby, které navštívíte</translation>
 <translation id="435527878592612277">Vyberte svou fotografii</translation>
-<translation id="4356334633973342967">Případně zadejte vlastní ovladač:</translation>
 <translation id="4359408040881008151">Rozšíření bylo nainstalováno kvůli závislým rozšířením.</translation>
 <translation id="4359717112757026264">Města</translation>
 <translation id="4361142739114356624">Soukromý klíč tohoto klientského certifikátu chybí nebo je neplatný</translation>
@@ -2621,7 +2620,7 @@
 <translation id="5029568752722684782">Vymazat kopii</translation>
 <translation id="5030338702439866405">Vydal:</translation>
 <translation id="5033266061063942743">Geometrické obrazce</translation>
-<translation id="5033619151015094114">Vaše společnost nebo organizace vyžaduje aktualizaci tohoto zařízení</translation>
+<translation id="5033619151015094114">Vaše firma nebo organizace vyžaduje aktualizaci tohoto zařízení</translation>
 <translation id="503498442187459473">Web <ph name="HOST" /> chce použít váš mikrofon a kameru</translation>
 <translation id="5036662165765606524">Nepovolovat žádnému webu automatické stahování několika souborů</translation>
 <translation id="5037676449506322593">Vybrat vše</translation>
@@ -5136,7 +5135,7 @@
 <translation id="8871974300055371298">Nastavení obsahu</translation>
 <translation id="8872155268274985541">Byl nalezen neplatný soubor manifestu externí aktualizace terminálové aplikace. Aktualizace terminálové aplikace se nezdařila. Vyjměte jednotku USB.</translation>
 <translation id="8874184842967597500">Nepřipojeno</translation>
-<translation id="887550310442005096">Vaše společnost nebo organizace vás žádá o aktualizaci tohoto zařízení</translation>
+<translation id="887550310442005096">Vaše firma nebo organizace vás žádá o aktualizaci tohoto zařízení</translation>
 <translation id="8876307312329369159">Toto nastavení v ukázkové relaci nelze změnit.</translation>
 <translation id="8877448029301136595">[nadřazený adresář]</translation>
 <translation id="8879284080359814990">Zobrazit jako &amp;kartu</translation>
diff --git a/chrome/app/resources/generated_resources_da.xtb b/chrome/app/resources/generated_resources_da.xtb
index be40a477..db08908 100644
--- a/chrome/app/resources/generated_resources_da.xtb
+++ b/chrome/app/resources/generated_resources_da.xtb
@@ -2204,7 +2204,6 @@
 <translation id="4350019051035968019">Denne enhed kan ikke tilmeldes det domæne, som din konto hører under, da enheden er markeret til administration på et andet domæne.</translation>
 <translation id="4354344420232759511">Her vises de websites, du besøger</translation>
 <translation id="435527878592612277">Vælg dit billede</translation>
-<translation id="4356334633973342967">Du kan også angive din egen driver:</translation>
 <translation id="4359408040881008151">Installeret på grund af andre udvidelser, der er afhængige af den.</translation>
 <translation id="4359717112757026264">Cityscape</translation>
 <translation id="4361142739114356624">Den private nøgle til dette klientcertifikat mangler eller er ugyldig</translation>
diff --git a/chrome/app/resources/generated_resources_de.xtb b/chrome/app/resources/generated_resources_de.xtb
index 62c58692..dcaa546c 100644
--- a/chrome/app/resources/generated_resources_de.xtb
+++ b/chrome/app/resources/generated_resources_de.xtb
@@ -2201,7 +2201,6 @@
 <translation id="4350019051035968019">Dieses Gerät kann nicht in der Domain registriert werden, zu der Ihr Konto gehört, weil das Gerät für die Verwaltung in einer anderen Domain gekennzeichnet wurde.</translation>
 <translation id="4354344420232759511">Hier finden Sie die von Ihnen besuchten Websites</translation>
 <translation id="435527878592612277">Foto auswählen</translation>
-<translation id="4356334633973342967">Oder eigenen Treiber angeben:</translation>
 <translation id="4359408040881008151">Wurde aufgrund der abhängigen Erweiterungen installiert</translation>
 <translation id="4359717112757026264">Stadtlandschaft</translation>
 <translation id="4361142739114356624">Der private Schlüssel für dieses Clientzertifikat fehlt oder ist ungültig</translation>
diff --git a/chrome/app/resources/generated_resources_el.xtb b/chrome/app/resources/generated_resources_el.xtb
index 35b9230..f2180d4 100644
--- a/chrome/app/resources/generated_resources_el.xtb
+++ b/chrome/app/resources/generated_resources_el.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Δεν είναι δυνατή η εγγραφή αυτής της συσκευής στον τομέα στον οποίο ανήκει ο λογαριασμός σας, επειδή η συσκευή έχει επισημανθεί για διαχείριση από κάποιον άλλο τομέα.</translation>
 <translation id="4354344420232759511">Οι ιστότοποι που επισκέπτεστε θα εμφανίζονται εδώ</translation>
 <translation id="435527878592612277">Επιλέξτε τη φωτογραφία σας</translation>
-<translation id="4356334633973342967">Εναλλακτικά, ορίστε το δικό σας πρόγραμμα οδήγησης:</translation>
 <translation id="4359408040881008151">Εγκαταστάθηκε λόγω των εξαρτώμενων επεκτάσεων</translation>
 <translation id="4359717112757026264">Αστικό τοπίο</translation>
 <translation id="4361142739114356624">Το Ιδιωτικό κλειδί για αυτό το Πιστοποιητικό πελάτη λείπει ή δεν είναι έγκυρο</translation>
diff --git a/chrome/app/resources/generated_resources_en-GB.xtb b/chrome/app/resources/generated_resources_en-GB.xtb
index d8075c1c..2ff1c50 100644
--- a/chrome/app/resources/generated_resources_en-GB.xtb
+++ b/chrome/app/resources/generated_resources_en-GB.xtb
@@ -2205,7 +2205,6 @@
 <translation id="4350019051035968019">This device cannot be enrolled to the domain that your account belongs to because the device is marked for management by a different domain.</translation>
 <translation id="4354344420232759511">Sites that you visit will appear here</translation>
 <translation id="435527878592612277">Select your photo</translation>
-<translation id="4356334633973342967">Or specify your own driver:</translation>
 <translation id="4359408040881008151">Installed because of dependent extension(s).</translation>
 <translation id="4359717112757026264">Cityscape</translation>
 <translation id="4361142739114356624">The Private Key for this Client Certificate is missing or invalid</translation>
diff --git a/chrome/app/resources/generated_resources_es-419.xtb b/chrome/app/resources/generated_resources_es-419.xtb
index 1baba989..37e4169f 100644
--- a/chrome/app/resources/generated_resources_es-419.xtb
+++ b/chrome/app/resources/generated_resources_es-419.xtb
@@ -2203,7 +2203,6 @@
 <translation id="4350019051035968019">Este dispositivo no se puede registrar en el dominio al que pertenece tu cuenta porque ya lo administra otro dominio.</translation>
 <translation id="4354344420232759511">Los sitios que visitas aparecerán aquí</translation>
 <translation id="435527878592612277">Seleccionar tu foto</translation>
-<translation id="4356334633973342967">O especifica tu propio controlador:</translation>
 <translation id="4359408040881008151">Instalada a causa de las extensiones dependientes</translation>
 <translation id="4359717112757026264">Paisaje urbano</translation>
 <translation id="4361142739114356624">Falta la clave privada para este Certificado de cliente o no es válida</translation>
diff --git a/chrome/app/resources/generated_resources_es.xtb b/chrome/app/resources/generated_resources_es.xtb
index ebcfff5..8fdc9fe 100644
--- a/chrome/app/resources/generated_resources_es.xtb
+++ b/chrome/app/resources/generated_resources_es.xtb
@@ -2203,7 +2203,6 @@
 <translation id="4350019051035968019">Este dispositivo no se puede registrar en el dominio al que pertenece tu cuenta porque ya lo administra otro dominio.</translation>
 <translation id="4354344420232759511">Los sitios web que visites aparecerán aquí</translation>
 <translation id="435527878592612277">Selecciona una foto</translation>
-<translation id="4356334633973342967">O especificar un controlador:</translation>
 <translation id="4359408040881008151">Instalada debido a las extensiones dependientes</translation>
 <translation id="4359717112757026264">Paisaje urbano</translation>
 <translation id="4361142739114356624">Falta la clave privada de este certificado de cliente o no es válida</translation>
diff --git a/chrome/app/resources/generated_resources_et.xtb b/chrome/app/resources/generated_resources_et.xtb
index a1050b7a..6848b7df 100644
--- a/chrome/app/resources/generated_resources_et.xtb
+++ b/chrome/app/resources/generated_resources_et.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Seda seadet ei saa registreerida domeenil, kuhu teie konto kuulub, sest seade on märgitud teisel domeenil haldamiseks.</translation>
 <translation id="4354344420232759511">Külastatud saidid kuvatakse siin</translation>
 <translation id="435527878592612277">Valige foto</translation>
-<translation id="4356334633973342967">Või määrake oma draiver:</translation>
 <translation id="4359408040881008151">Installitud sõltuva(te) laiendus(t)e tõttu.</translation>
 <translation id="4359717112757026264">Linna panoraam</translation>
 <translation id="4361142739114356624">Selle kliendi sertifikaadi privaatvõti puudub või on sobimatu</translation>
diff --git a/chrome/app/resources/generated_resources_fa.xtb b/chrome/app/resources/generated_resources_fa.xtb
index 42d15d5..4951ac6 100644
--- a/chrome/app/resources/generated_resources_fa.xtb
+++ b/chrome/app/resources/generated_resources_fa.xtb
@@ -2202,7 +2202,6 @@
 <translation id="4350019051035968019">این دستگاه نمی‌تواند در دامنه‌ای که حسابتان متعلق به آن است، ثبت‌نام کند زیرا دستگاه برای مدیریت توسط دامنه دیگری علامت‌گذاری شده است.</translation>
 <translation id="4354344420232759511">سایت‌هایی که بازدید می‌کنید اینجا نشان داده می‌شود</translation>
 <translation id="435527878592612277">انتخاب عکس</translation>
-<translation id="4356334633973342967">یا درایور خود را مشخص کنید</translation>
 <translation id="4359408040881008151">به دلیل برنامه(های) افزودنی وابسته نصب شد.</translation>
 <translation id="4359717112757026264">نمای شهر</translation>
 <translation id="4361142739114356624">کلید خصوصی برای این گواهی کارخواه موجود نیست یا نامعتبر است</translation>
diff --git a/chrome/app/resources/generated_resources_fi.xtb b/chrome/app/resources/generated_resources_fi.xtb
index 334bbbed..8d506a5d 100644
--- a/chrome/app/resources/generated_resources_fi.xtb
+++ b/chrome/app/resources/generated_resources_fi.xtb
@@ -2204,7 +2204,6 @@
 <translation id="4350019051035968019">Tätä laitetta ei voi rekisteröidä verkkotunnukselle, johon tilisi kuuluu, koska laite on merkitty eri verkkotunnuksen hallinnoitavaksi.</translation>
 <translation id="4354344420232759511">Käyttämäsi sivustot tulevat näkyviin tähän</translation>
 <translation id="435527878592612277">Valitse valokuva</translation>
-<translation id="4356334633973342967">Tai määritä oma ajuri:</translation>
 <translation id="4359408040881008151">Asennettiin, koska muut laajennukset ovat riippuvaisia tästä laajennuksesta.</translation>
 <translation id="4359717112757026264">Kaupunkimaisema</translation>
 <translation id="4361142739114356624">Tämän käyttöoikeusvarmenteen yksityinen avain puuttuu tai on virheellinen.</translation>
diff --git a/chrome/app/resources/generated_resources_fil.xtb b/chrome/app/resources/generated_resources_fil.xtb
index 710e6039..8e17c9b 100644
--- a/chrome/app/resources/generated_resources_fil.xtb
+++ b/chrome/app/resources/generated_resources_fil.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Hindi maaaring i-enroll ang device na ito sa domain kung saan nabibilang ang iyong account dahil minarkahan ang iyong device para mapamahalaan ng ibang domain.</translation>
 <translation id="4354344420232759511">Lalabas dito ang mga site na binibisita mo</translation>
 <translation id="435527878592612277">Piliin ang iyong larawan</translation>
-<translation id="4356334633973342967">O tukuyin ang sarili mong driver:</translation>
 <translation id="4359408040881008151">Na-install dahil sa (mga) nakadependeng extension.</translation>
 <translation id="4359717112757026264">Cityscape</translation>
 <translation id="4361142739114356624">Nawawala o di-wasto ang Pribadong Key para sa Client Certificate na ito</translation>
diff --git a/chrome/app/resources/generated_resources_fr.xtb b/chrome/app/resources/generated_resources_fr.xtb
index d87612f2..277c2d78 100644
--- a/chrome/app/resources/generated_resources_fr.xtb
+++ b/chrome/app/resources/generated_resources_fr.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Impossible d'enregistrer cet appareil dans le domaine auquel appartient votre compte, car il est marqué pour être géré par un autre domaine.</translation>
 <translation id="4354344420232759511">Les sites que vous consultez s'affichent ici</translation>
 <translation id="435527878592612277">Sélectionner votre photo</translation>
-<translation id="4356334633973342967">Vous pouvez également indiquer votre propre pilote :</translation>
 <translation id="4359408040881008151">Installée, car une ou plusieurs extensions dépendent de celle-ci.</translation>
 <translation id="4359717112757026264">Paysage urbain</translation>
 <translation id="4361142739114356624">La clé privée de ce certificat client est manquante ou incorrecte</translation>
diff --git a/chrome/app/resources/generated_resources_gu.xtb b/chrome/app/resources/generated_resources_gu.xtb
index 11c410e..b4ac0c3 100644
--- a/chrome/app/resources/generated_resources_gu.xtb
+++ b/chrome/app/resources/generated_resources_gu.xtb
@@ -2202,7 +2202,6 @@
 <translation id="4350019051035968019">તમારું એકાઉન્ટ જે ડોમેનથી સંબંધિત છે, તેમાં આ ડિવાઇસની નોંધણી કરી શકાતી નથી, કારણ કે ડિવાઇસને મેનેજ કરવા માટે કોઈ અલગ ડોમેન દ્વારા ચિહ્નિત કરેલું છે.</translation>
 <translation id="4354344420232759511">તમે મુલાકાત લીધેલી સાઇટ અહીં દેખાશે</translation>
 <translation id="435527878592612277">તમારો ફોટો પસંદ કરો</translation>
-<translation id="4356334633973342967">અથવા તમારા પોતાના ડ્રાઇવરનો ઉલ્લેખ કરો:</translation>
 <translation id="4359408040881008151">નિર્ભર એક્સ્ટેન્શન(એક્સ્ટેન્શન્સ)ને લીધે ઇન્સ્ટોલ કર્યું.</translation>
 <translation id="4359717112757026264">સિટિસ્કેપ</translation>
 <translation id="4361142739114356624">આ ક્લાઇન્ટ પ્રમાણપત્ર માટેની ખાનગી કી ખૂટે છે અથવા તો અમાન્ય છે</translation>
diff --git a/chrome/app/resources/generated_resources_hi.xtb b/chrome/app/resources/generated_resources_hi.xtb
index c5230b1..f31d5871 100644
--- a/chrome/app/resources/generated_resources_hi.xtb
+++ b/chrome/app/resources/generated_resources_hi.xtb
@@ -2205,7 +2205,6 @@
 <translation id="4350019051035968019">इस डिवाइस को उस डोमेन पर नामांकित नहीं किया जा सकता जिससे आपका खाता संबंधित है क्योंकि डिवाइस को भिन्न डोमेन द्वारा प्रबंधित करने के लिए चिह्नित किया गया है.</translation>
 <translation id="4354344420232759511">आपकी देखी गई साइटें यहां दिखाई देंगी</translation>
 <translation id="435527878592612277">अपनी फ़ोटो चुनें</translation>
-<translation id="4356334633973342967">या अपना स्वयं का ड्राइवर निर्दिष्ट करें:</translation>
 <translation id="4359408040881008151">निर्भर एक्सटेंशन के कारण इंस्टॉल किया गया.</translation>
 <translation id="4359717112757026264">सिटीस्केप</translation>
 <translation id="4361142739114356624">इस क्‍लाइंट प्रमाणपत्र की निजी कुंजी मौजूद नहीं है या अमान्‍य है</translation>
diff --git a/chrome/app/resources/generated_resources_hr.xtb b/chrome/app/resources/generated_resources_hr.xtb
index 49d2080..56f9878 100644
--- a/chrome/app/resources/generated_resources_hr.xtb
+++ b/chrome/app/resources/generated_resources_hr.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Uređaj se ne može prijaviti na domenu kojoj pripada vaš račun jer je postavljen za upravljanje na nekoj drugoj domeni.</translation>
 <translation id="4354344420232759511">Web-lokacije koje posjetite pojavit će se ovdje</translation>
 <translation id="435527878592612277">Odaberite fotografiju</translation>
-<translation id="4356334633973342967">Možete i navesti vlastiti upravljački program:</translation>
 <translation id="4359408040881008151">Instalirano zbog ovisnih proširenja.</translation>
 <translation id="4359717112757026264">Gradski krajolik</translation>
 <translation id="4361142739114356624">Privatni ključ za ovaj certifikat klijenta nedostaje ili nije važeći</translation>
diff --git a/chrome/app/resources/generated_resources_hu.xtb b/chrome/app/resources/generated_resources_hu.xtb
index 12dc7a3..fd52ce8 100644
--- a/chrome/app/resources/generated_resources_hu.xtb
+++ b/chrome/app/resources/generated_resources_hu.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Ez az eszköz nem regisztrálható annál a domainnél, amelyhez fiókja tartozik, mert az eszköz felügyeletére egy másik domain van kijelölve.</translation>
 <translation id="4354344420232759511">Az Ön által felkeresett webhelyek itt jelennek meg</translation>
 <translation id="435527878592612277">Válassza ki a fotóját</translation>
-<translation id="4356334633973342967">Vagy adja meg saját illesztőprogramját:</translation>
 <translation id="4359408040881008151">Telepítve az ezt nélkülözni nem tudó bővítmény(ek) miatt.</translation>
 <translation id="4359717112757026264">Városkép</translation>
 <translation id="4361142739114356624">Az ügyféltanúsítvány privát kulcsa hiányzik vagy érvénytelen</translation>
diff --git a/chrome/app/resources/generated_resources_id.xtb b/chrome/app/resources/generated_resources_id.xtb
index 234268d..4accea8 100644
--- a/chrome/app/resources/generated_resources_id.xtb
+++ b/chrome/app/resources/generated_resources_id.xtb
@@ -1232,7 +1232,7 @@
 <translation id="284975061945174219">Pembersihan gagal</translation>
 <translation id="2849936225196189499">Kritis</translation>
 <translation id="2850541429955027218">Tambahkan tema</translation>
-<translation id="2858138569776157458">Situs teratas</translation>
+<translation id="2858138569776157458">Situs populer</translation>
 <translation id="2859806420264540918">Situs ini menampilkan iklan yang mengganggu atau menyesatkan.</translation>
 <translation id="2861301611394761800">Pembaruan sistem selesai. Mulai ulang sistem.</translation>
 <translation id="2861941300086904918">Pengelola keamanan Program Asli</translation>
@@ -2204,7 +2204,6 @@
 <translation id="4350019051035968019">Perangkat ini tidak dapat didaftarkan ke domain tempat akun Anda berada karena perangkat ditandai untuk dikelola oleh domain lain.</translation>
 <translation id="4354344420232759511">Situs yang Anda kunjungi akan ditampilkan di sini</translation>
 <translation id="435527878592612277">Pilih foto Anda</translation>
-<translation id="4356334633973342967">Atau tentukan driver Anda sendiri:</translation>
 <translation id="4359408040881008151">Dipasang karena ekstensi dependen.</translation>
 <translation id="4359717112757026264">Pemandangan Kota</translation>
 <translation id="4361142739114356624">Kunci Pribadi untuk Sertifikat Klien ini tidak ditemukan atau tidak valid</translation>
diff --git a/chrome/app/resources/generated_resources_it.xtb b/chrome/app/resources/generated_resources_it.xtb
index 23fc3e52..b4fdc2d 100644
--- a/chrome/app/resources/generated_resources_it.xtb
+++ b/chrome/app/resources/generated_resources_it.xtb
@@ -2202,7 +2202,6 @@
 <translation id="4350019051035968019">Questo dispositivo non può essere registrato sul dominio a cui appartiene il tuo account perché è contrassegnato per essere gestito da un altro dominio.</translation>
 <translation id="4354344420232759511">Qui verranno visualizzati i siti visitati</translation>
 <translation id="435527878592612277">Seleziona la foto</translation>
-<translation id="4356334633973342967">In alternativa specifica il tuo driver:</translation>
 <translation id="4359408040881008151">Installata a causa di estensioni dipendenti.</translation>
 <translation id="4359717112757026264">Cityscape</translation>
 <translation id="4361142739114356624">La chiave privata per il certificato client manca o non è valida</translation>
diff --git a/chrome/app/resources/generated_resources_iw.xtb b/chrome/app/resources/generated_resources_iw.xtb
index 6fc3d8c..e202e2e 100644
--- a/chrome/app/resources/generated_resources_iw.xtb
+++ b/chrome/app/resources/generated_resources_iw.xtb
@@ -345,7 +345,7 @@
 <translation id="151501797353681931">‏יובא מ-Safari</translation>
 <translation id="1515163294334130951">הפעל</translation>
 <translation id="1521442365706402292">נהל אישורים</translation>
-<translation id="1521774566618522728">שימוש אחרון היום</translation>
+<translation id="1521774566618522728">שימוש אחרון: היום</translation>
 <translation id="152234381334907219">פריטים שאף פעם לא נשמרו</translation>
 <translation id="1524430321211440688">מקלדת</translation>
 <translation id="1524563461097350801">לא, תודה</translation>
@@ -1612,7 +1612,7 @@
 <translation id="346431825526753">זהו חשבון לילדים, המנוהל על-ידי <ph name="CUSTODIAN_EMAIL" />.</translation>
 <translation id="3468275649641751422">העברת קובץ וידאו או אודיו בסטרימינג</translation>
 <translation id="3468999815377931311">‏טלפון Android</translation>
-<translation id="3470248707805984963">אירעה שגיאה. נסה שוב.</translation>
+<translation id="3470248707805984963">אירעה שגיאה. אפשר לנסות שוב.</translation>
 <translation id="3470442499439619530">הסר משתמש זה</translation>
 <translation id="3473479545200714844">מגדיל התצוגה</translation>
 <translation id="3475843873335999118">המערכת עדיין לא מזהה את טביעת האצבע. הזן את הסיסמה שלך.</translation>
@@ -2203,7 +2203,6 @@
 <translation id="4350019051035968019">לא ניתן לרשום את המכשיר הזה לדומיין שאליו שייך החשבון שלך מפני שהמכשיר מסומן לניהול על ידי דומיין אחר.</translation>
 <translation id="4354344420232759511">אתרים שבהם ביקרת יופיעו כאן</translation>
 <translation id="435527878592612277">בחירת התמונה שלך</translation>
-<translation id="4356334633973342967">או ציין מנהל התקן משלך:</translation>
 <translation id="4359408040881008151">הותקן מפני שיש תוספים התלויים בו.</translation>
 <translation id="4359717112757026264">נוף עירוני</translation>
 <translation id="4361142739114356624">המפתח הפרטי לאישור לקוח זה חסר או אינו חוקי</translation>
@@ -2267,7 +2266,7 @@
 <translation id="4450974146388585462">אבחן</translation>
 <translation id="4451757071857432900">חסומות באתרים שמוצגות בהם מודעות מפריעות או מטעות (מומלץ)</translation>
 <translation id="4453946976636652378">אפשר לחפש ב-<ph name="SEARCH_ENGINE_NAME" /> או להקליד כתובת אתר</translation>
-<translation id="4459169140545916303">שימוש אחרון לפני <ph name="DEVICE_LAST_ACTIVATED_TIME" /> ימים</translation>
+<translation id="4459169140545916303">שימוש אחרון: לפני <ph name="DEVICE_LAST_ACTIVATED_TIME" /> ימים</translation>
 <translation id="4462159676511157176">שרתי שמות מותאמים אישית</translation>
 <translation id="4469477701382819144">חסומות באתרים שמוצגות בהם מודעות מפריעות או מטעות</translation>
 <translation id="4470957202018033307">העדפות אחסון חיצוני</translation>
@@ -5210,7 +5209,7 @@
 <translation id="8978154919215542464">פועל - סנכרון הכול</translation>
 <translation id="897939795688207351">ב-<ph name="ORIGIN" /></translation>
 <translation id="8980951173413349704"><ph name="WINDOW_TITLE" /> - קרס</translation>
-<translation id="8985264973231822211">שימוש אחרון לפני יום אחד (<ph name="DEVICE_LAST_ACTIVATED_TIME" />)</translation>
+<translation id="8985264973231822211">שימוש אחרון: לפני יום אחד (<ph name="DEVICE_LAST_ACTIVATED_TIME" />)</translation>
 <translation id="8986362086234534611">שכח</translation>
 <translation id="8986494364107987395">‏שליחה אוטומטית של דוחות קריסה וסטטיסטיקות שימוש ל-Google</translation>
 <translation id="8987927404178983737">חודש</translation>
diff --git a/chrome/app/resources/generated_resources_ja.xtb b/chrome/app/resources/generated_resources_ja.xtb
index dd069e7..356a232 100644
--- a/chrome/app/resources/generated_resources_ja.xtb
+++ b/chrome/app/resources/generated_resources_ja.xtb
@@ -348,7 +348,7 @@
 <translation id="151501797353681931">Safari からのインポート</translation>
 <translation id="1515163294334130951">起動</translation>
 <translation id="1521442365706402292">証明書の管理</translation>
-<translation id="1521774566618522728">オンライン: 今日</translation>
+<translation id="1521774566618522728">最終同期: 今日</translation>
 <translation id="152234381334907219">常に保存しない</translation>
 <translation id="1524430321211440688">キーボード</translation>
 <translation id="1524563461097350801">自動チェックアウトしない</translation>
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">このデバイスは、別のドメインの管理対象として指定されているため、現在のアカウントの所属先ドメインに登録することはできません。</translation>
 <translation id="4354344420232759511">アクセスしたサイトがここに表示されます</translation>
 <translation id="435527878592612277">写真を選択</translation>
-<translation id="4356334633973342967">または独自のドライバを指定:</translation>
 <translation id="4359408040881008151">依存関係にある拡張機能によってインストールされました。</translation>
 <translation id="4359717112757026264">Cityscape</translation>
 <translation id="4361142739114356624">このクライアント証明書には秘密鍵がないか、秘密鍵が無効です</translation>
@@ -2270,7 +2269,7 @@
 <translation id="4450974146388585462">診断</translation>
 <translation id="4451757071857432900">煩わしい広告や誤解を招く広告が表示されるサイトでブロック(推奨)</translation>
 <translation id="4453946976636652378"><ph name="SEARCH_ENGINE_NAME" /> で検索するか、URL を入力してください</translation>
-<translation id="4459169140545916303">オンライン: <ph name="DEVICE_LAST_ACTIVATED_TIME" /> 日前</translation>
+<translation id="4459169140545916303">最終同期: <ph name="DEVICE_LAST_ACTIVATED_TIME" /> 日前</translation>
 <translation id="4462159676511157176">カスタム ネーム サーバー</translation>
 <translation id="4469477701382819144">煩わしい広告や誤解を招く広告が表示されるサイトでブロックされています</translation>
 <translation id="4470957202018033307">外部ストレージの設定</translation>
@@ -5215,7 +5214,7 @@
 <translation id="8978154919215542464">オン - すべて同期</translation>
 <translation id="897939795688207351"><ph name="ORIGIN" /></translation>
 <translation id="8980951173413349704"><ph name="WINDOW_TITLE" /> - クラッシュしました</translation>
-<translation id="8985264973231822211">オンライン: <ph name="DEVICE_LAST_ACTIVATED_TIME" /> 日前</translation>
+<translation id="8985264973231822211">最終同期: <ph name="DEVICE_LAST_ACTIVATED_TIME" /> 日前</translation>
 <translation id="8986362086234534611">削除</translation>
 <translation id="8986494364107987395">使用統計データと障害レポートを Google に自動送信します</translation>
 <translation id="8987927404178983737">月</translation>
diff --git a/chrome/app/resources/generated_resources_kn.xtb b/chrome/app/resources/generated_resources_kn.xtb
index 70ac6e7..f477efa 100644
--- a/chrome/app/resources/generated_resources_kn.xtb
+++ b/chrome/app/resources/generated_resources_kn.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">ಈ ಸಾಧನವನ್ನು ನಿಮ್ಮ ಖಾತೆಗೆ ಸಂಬಂಧಿಸಿದ ಡೊಮೇನ್‌ಗೆ ಸೇರಿಸಿಕೊಳ್ಳಲು ಸಾಧ್ಯವಿಲ್ಲ ಏಕೆಂದರೆ ಈ ಸಾಧನವನ್ನು ನಿರ್ವಹಿಸಲು ಬೇರೊಂದು ಡೊಮೇ‌ನ್ ಮೂಲಕ ಗುರುತಿಸಲಾಗಿದೆ.</translation>
 <translation id="4354344420232759511">ನೀವು ಭೇಟಿ ಮಾಡುವ ಸೈಟ್‌ಗಳು ಇಲ್ಲಿ ಗೋಚರಿಸುತ್ತವೆ</translation>
 <translation id="435527878592612277">ನಿಮ್ಮ ಫೋಟೋ ಆಯ್ಕೆಮಾಡಿ</translation>
-<translation id="4356334633973342967">ಅಥವಾ ನಿಮ್ಮ ಸ್ವಂತ ಡ್ರೈವರ್ ನಿರ್ದಿಷ್ಟಪಡಿಸಿ:</translation>
 <translation id="4359408040881008151">ಅವಲಂಬಿತ ವಿಸ್ತರಣೆ(ಗಳು) ಯಿಂದಾಗಿ ಸ್ಥಾಪಿಸಲಾಗಿದೆ.</translation>
 <translation id="4359717112757026264">Cityscape</translation>
 <translation id="4361142739114356624">ಈ ಕ್ಲೈಂಟ್ ಪ್ರಮಾಣಪತ್ರಕ್ಕೆ ವೈಯಕ್ತಿಕ ಕೀ ಕಾಣೆಯಾಗಿದೆ ಅಥವಾ ಅಮಾನ್ಯವಾಗಿದೆ</translation>
diff --git a/chrome/app/resources/generated_resources_ko.xtb b/chrome/app/resources/generated_resources_ko.xtb
index a2a97d0..9a800ac 100644
--- a/chrome/app/resources/generated_resources_ko.xtb
+++ b/chrome/app/resources/generated_resources_ko.xtb
@@ -2204,7 +2204,6 @@
 <translation id="4350019051035968019">이 기기는 다른 도메인에서 관리하는 것으로 표시되어 있기 때문에 사용자의 계정이 속한 도메인에 등록할 수 없습니다.</translation>
 <translation id="4354344420232759511">내가 방문한 사이트가 여기에 표시됩니다.</translation>
 <translation id="435527878592612277">사진 선택</translation>
-<translation id="4356334633973342967">또는 드라이버 지정:</translation>
 <translation id="4359408040881008151">함께 설치된 확장 프로그램입니다.</translation>
 <translation id="4359717112757026264">도시 풍경</translation>
 <translation id="4361142739114356624">이 클라이언트 인증서의 비공개 키가 없거나 잘못되었습니다.</translation>
diff --git a/chrome/app/resources/generated_resources_lt.xtb b/chrome/app/resources/generated_resources_lt.xtb
index f6d2a14..c542736 100644
--- a/chrome/app/resources/generated_resources_lt.xtb
+++ b/chrome/app/resources/generated_resources_lt.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Šio įrenginio negalima užregistruoti domene, kuriam priklauso paskyra, nes pažymėta, kad įrenginys tvarkomas kito domeno.</translation>
 <translation id="4354344420232759511">Lankytos svetainės bus rodomos čia</translation>
 <translation id="435527878592612277">Pasirinkite nuotrauką</translation>
-<translation id="4356334633973342967">Arba nurodykite savo tvarkyklę:</translation>
 <translation id="4359408040881008151">Įdiegta, nes priklauso nuo plėtinio (-ių).</translation>
 <translation id="4359717112757026264">Miesto architektūra</translation>
 <translation id="4361142739114356624">Trūksta šio kliento sertifikato privataus rakto arba jis netinkamas</translation>
diff --git a/chrome/app/resources/generated_resources_lv.xtb b/chrome/app/resources/generated_resources_lv.xtb
index 3b39cd46..af2504ac 100644
--- a/chrome/app/resources/generated_resources_lv.xtb
+++ b/chrome/app/resources/generated_resources_lv.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Šo ierīci nevar reģistrēt domēnā, kuram pieder jūsu konts, jo tā ir iestatīta pārvaldībai citā domēnā.</translation>
 <translation id="4354344420232759511">Šeit tiks rādītas jūsu apmeklētās vietnes.</translation>
 <translation id="435527878592612277">Atlasiet savu fotoattēlu</translation>
-<translation id="4356334633973342967">Varat arī norādīt savu dzini:</translation>
 <translation id="4359408040881008151">Paplašinājums tika instalēts atkarīga(-u) paplašinājuma(-u) dēļ.</translation>
 <translation id="4359717112757026264">Pilsētu fotoattēli</translation>
 <translation id="4361142739114356624">Nav norādīta šī klienta sertifikāta privātā atslēga, vai tā nav derīga.</translation>
diff --git a/chrome/app/resources/generated_resources_ml.xtb b/chrome/app/resources/generated_resources_ml.xtb
index de03026..3107b8f 100644
--- a/chrome/app/resources/generated_resources_ml.xtb
+++ b/chrome/app/resources/generated_resources_ml.xtb
@@ -2202,7 +2202,6 @@
 <translation id="4350019051035968019">മറ്റൊരു ഡൊമെയ്‌ൻ, ഉപകരണത്തെ നിയന്ത്രിക്കുന്നതിനായി അടയാളപ്പെടുത്തിയിരിക്കുന്നതിനാൽ, ഈ ഉപകരണത്തിന് നിങ്ങളുടെ അക്കൗണ്ട് ഉൾപ്പെട്ടിരിക്കുന്ന ഡൊമെയ്‌നിലേക്ക് എൻറോൾ ചെയ്യാനാവില്ല.</translation>
 <translation id="4354344420232759511">നിങ്ങൾ സന്ദർശിക്കുന്ന സൈറ്റുകൾ ഇവിടെ ദൃശ്യമാകും</translation>
 <translation id="435527878592612277">നിങ്ങളുടെ ഫോട്ടോ തിരഞ്ഞെടുക്കുക</translation>
-<translation id="4356334633973342967">അല്ലെങ്കിൽ നിങ്ങളുടേത് മാത്രമായ ഡ്രൈവറെ വ്യക്തമാക്കുക:</translation>
 <translation id="4359408040881008151">ആശ്രയിച്ചിരിക്കുന്ന വിപുലീകരണം (വിപുലീകരണങ്ങൾ) കാരണം ഇൻസ്‌റ്റാൾ ചെയ്തു.</translation>
 <translation id="4359717112757026264">സിറ്റി‌സ്‌കേപ്പ്</translation>
 <translation id="4361142739114356624">ഈ ക്ലയന്റ് സർട്ടിഫിക്കറ്റിന്റെ സ്വകാര്യ കീ നഷ്‌ടമായി അല്ലെങ്കിൽ അസാധുവാണ്</translation>
diff --git a/chrome/app/resources/generated_resources_mr.xtb b/chrome/app/resources/generated_resources_mr.xtb
index 44e9149e..ce8c0a1e 100644
--- a/chrome/app/resources/generated_resources_mr.xtb
+++ b/chrome/app/resources/generated_resources_mr.xtb
@@ -2205,7 +2205,6 @@
 <translation id="4350019051035968019">एका भिन्न डोमेनद्वारे व्यवस्थापनासाठी डिव्हाइस चिन्हांकित केले असल्यामुळे तुमचे खाते ज्या मालकीचे आहे त्या डोमेवर या डिव्हाइसची नोंदणी केली जाऊ शकत नाही.</translation>
 <translation id="4354344420232759511">तुम्ही भेट दिलेल्या साइट येथे दिसतील</translation>
 <translation id="435527878592612277">तुमचा फोटो निवडा</translation>
-<translation id="4356334633973342967">किंवा तुमच्या स्वत:चा ड्रायव्हर नमूद करा:</translation>
 <translation id="4359408040881008151">अवलंबून असलेल्या एक्स्टेंशन(ना) मुळे इंस्टॉल केले.</translation>
 <translation id="4359717112757026264">शहराचे दृश्य</translation>
 <translation id="4361142739114356624">या क्लायंट प्रमाणपत्रासाठी खाजगी, की अनुपलब्ध किंवा अवैध आहे</translation>
diff --git a/chrome/app/resources/generated_resources_ms.xtb b/chrome/app/resources/generated_resources_ms.xtb
index 9453061..457f21c 100644
--- a/chrome/app/resources/generated_resources_ms.xtb
+++ b/chrome/app/resources/generated_resources_ms.xtb
@@ -2207,7 +2207,6 @@
 <translation id="4350019051035968019">Peranti ini tidak boleh didaftarkan pada domain bagi akaun anda kerana peranti telah ditandai untuk pengurusan oleh domain lain.</translation>
 <translation id="4354344420232759511">Tapak yang anda lawati akan dipaparkan di sini</translation>
 <translation id="435527878592612277">Pilih foto anda</translation>
-<translation id="4356334633973342967">Atau, tentukan pemacu anda sendiri:</translation>
 <translation id="4359408040881008151">Dipasang kerana sambungan yang bergantung.</translation>
 <translation id="4359717112757026264">Pandangan bandar</translation>
 <translation id="4361142739114356624">Kunci Peribadi untuk Sijil Pelanggan ini tiada atau tidak sah</translation>
diff --git a/chrome/app/resources/generated_resources_nl.xtb b/chrome/app/resources/generated_resources_nl.xtb
index e3e2fda1..e4194755 100644
--- a/chrome/app/resources/generated_resources_nl.xtb
+++ b/chrome/app/resources/generated_resources_nl.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Dit apparaat kan niet worden ingeschreven bij het domein waarbij je account hoort, omdat het apparaat is aangewezen voor beheer door een ander domein.</translation>
 <translation id="4354344420232759511">Sites die je bezoekt, worden hier weergegeven</translation>
 <translation id="435527878592612277">Selecteer je foto</translation>
-<translation id="4356334633973342967">Of geef je eigen stuurprogramma op:</translation>
 <translation id="4359408040881008151">Geïnstalleerd vanwege afhankelijke extensie(s).</translation>
 <translation id="4359717112757026264">Stadsgezicht</translation>
 <translation id="4361142739114356624">De privésleutel voor dit clientcertificaat ontbreekt of is ongeldig</translation>
diff --git a/chrome/app/resources/generated_resources_no.xtb b/chrome/app/resources/generated_resources_no.xtb
index 0eb8a00..68b2985 100644
--- a/chrome/app/resources/generated_resources_no.xtb
+++ b/chrome/app/resources/generated_resources_no.xtb
@@ -2199,7 +2199,6 @@
 <translation id="4350019051035968019">Denne enheten kan ikke registreres for domenet kontoen din tilhører. Enheten er merket for administrering av et annet domene.</translation>
 <translation id="4354344420232759511">Nettsteder du besøker, vises her</translation>
 <translation id="435527878592612277">Velg bilde</translation>
-<translation id="4356334633973342967">Du kan også angi driveren manuelt:</translation>
 <translation id="4359408040881008151">Installert på grunn av avhengige utvidelser.</translation>
 <translation id="4359717112757026264">Bybilde</translation>
 <translation id="4361142739114356624">Privatnøkkelen for dette klientsertifikatet mangler eller er ugyldig</translation>
diff --git a/chrome/app/resources/generated_resources_pl.xtb b/chrome/app/resources/generated_resources_pl.xtb
index 406d4fc..451ab0d 100644
--- a/chrome/app/resources/generated_resources_pl.xtb
+++ b/chrome/app/resources/generated_resources_pl.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">To urządzenie jest oznaczone jako zarządzane przez inną domenę i nie można go przypisać do domeny, do której należy Twoje konto.</translation>
 <translation id="4354344420232759511">Tutaj pojawią się odwiedzone przez Ciebie strony</translation>
 <translation id="435527878592612277">Wybierz swoje zdjęcie</translation>
-<translation id="4356334633973342967">Możesz też wybrać własny sterownik:</translation>
 <translation id="4359408040881008151">Zainstalowane ponieważ jest wymagane przez inne rozszerzenia.</translation>
 <translation id="4359717112757026264">Pejzaż miejski</translation>
 <translation id="4361142739114356624">Klucz prywatny tego certyfikatu klienta jest nieprawidłowy lub nie istnieje</translation>
diff --git a/chrome/app/resources/generated_resources_pt-BR.xtb b/chrome/app/resources/generated_resources_pt-BR.xtb
index 6b04794..e8df0d02 100644
--- a/chrome/app/resources/generated_resources_pt-BR.xtb
+++ b/chrome/app/resources/generated_resources_pt-BR.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Este dispositivo não pode ser inscrito no domínio ao qual sua conta pertence, porque ele está marcado para ser gerenciado por outro domínio.</translation>
 <translation id="4354344420232759511">Os sites que você visitar serão exibidos aqui</translation>
 <translation id="435527878592612277">Selecione sua foto</translation>
-<translation id="4356334633973342967">Ou especifique seu próprio driver:</translation>
 <translation id="4359408040881008151">Instalada devido a extensões dependentes.</translation>
 <translation id="4359717112757026264">Paisagem urbana</translation>
 <translation id="4361142739114356624">A chave particular desse certificado do cliente está ausente ou é inválida</translation>
diff --git a/chrome/app/resources/generated_resources_pt-PT.xtb b/chrome/app/resources/generated_resources_pt-PT.xtb
index c3bdb2c..2b0da45 100644
--- a/chrome/app/resources/generated_resources_pt-PT.xtb
+++ b/chrome/app/resources/generated_resources_pt-PT.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Não é possível inscrever este dispositivo no domínio a que pertence a sua conta, porque está marcado para gestão por um domínio diferente.</translation>
 <translation id="4354344420232759511">Os sites que visitar aparecem aqui</translation>
 <translation id="435527878592612277">Selecionar a sua foto</translation>
-<translation id="4356334633973342967">Em alternativa, especifique o seu próprio controlador:</translation>
 <translation id="4359408040881008151">Instalada devido a extensão(ões) dependente(s).</translation>
 <translation id="4359717112757026264">Paisagens urbanas</translation>
 <translation id="4361142739114356624">A chave privada para este certificado de cliente está em falta ou é inválida</translation>
diff --git a/chrome/app/resources/generated_resources_ro.xtb b/chrome/app/resources/generated_resources_ro.xtb
index e757a07f..88a9ee0 100644
--- a/chrome/app/resources/generated_resources_ro.xtb
+++ b/chrome/app/resources/generated_resources_ro.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Acest dispozitiv nu poate fi înregistrat pe domeniul căruia îi aparține contul dvs., deoarece dispozitivul este asociat pentru gestionare de alt domeniu.</translation>
 <translation id="4354344420232759511">Site-urile pe care le accesezi apar aici</translation>
 <translation id="435527878592612277">Selectează fotografia</translation>
-<translation id="4356334633973342967">Sau specifică ce driver ai:</translation>
 <translation id="4359408040881008151">Instalată din cauza extensiilor dependente.</translation>
 <translation id="4359717112757026264">Cityscape</translation>
 <translation id="4361142739114356624">Cheia privată pentru acest Certificat de client lipsește sau nu este validă</translation>
diff --git a/chrome/app/resources/generated_resources_ru.xtb b/chrome/app/resources/generated_resources_ru.xtb
index dc20067..cfa0fae 100644
--- a/chrome/app/resources/generated_resources_ru.xtb
+++ b/chrome/app/resources/generated_resources_ru.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Это устройство уже зарегистрировано в другом домене.</translation>
 <translation id="4354344420232759511">Здесь появятся сайты, на которые вы заходили.</translation>
 <translation id="435527878592612277">Выберите фото</translation>
-<translation id="4356334633973342967">Или укажите другой драйвер:</translation>
 <translation id="4359408040881008151">Установлено, так как есть зависимые расширения.</translation>
 <translation id="4359717112757026264">Городской пейзаж</translation>
 <translation id="4361142739114356624">Закрытый ключ для клиентского сертификата отсутствует или недействителен</translation>
diff --git a/chrome/app/resources/generated_resources_sk.xtb b/chrome/app/resources/generated_resources_sk.xtb
index 6fdf363..c79e48a 100644
--- a/chrome/app/resources/generated_resources_sk.xtb
+++ b/chrome/app/resources/generated_resources_sk.xtb
@@ -2204,7 +2204,6 @@
 <translation id="4350019051035968019">Toto zariadenie nemôže byť zaregistrované do domény vášho účtu, pretože je označené ako spravované inou doménou.</translation>
 <translation id="4354344420232759511">Navštívené weby sa zobrazia tu</translation>
 <translation id="435527878592612277">Vyberte fotku</translation>
-<translation id="4356334633973342967">Alebo vyberte vlastný ovládač:</translation>
 <translation id="4359408040881008151">Nainštalované pre závislé rozšírenia.</translation>
 <translation id="4359717112757026264">Mestá</translation>
 <translation id="4361142739114356624">Súkromný kľúč pre tento klientsky certifikát chýba alebo je neplatný</translation>
diff --git a/chrome/app/resources/generated_resources_sl.xtb b/chrome/app/resources/generated_resources_sl.xtb
index cc337a09..c897402 100644
--- a/chrome/app/resources/generated_resources_sl.xtb
+++ b/chrome/app/resources/generated_resources_sl.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Te naprave ni mogoče včlaniti v domeno vašega računa, ker je naprava označena za upravljanje v drugi domeni.</translation>
 <translation id="4354344420232759511">Spletna mesta, ki jih obiščete, bodo prikazana tu</translation>
 <translation id="435527878592612277">Izberite fotografijo</translation>
-<translation id="4356334633973342967">Ali navedite svoj gonilnik:</translation>
 <translation id="4359408040881008151">Nameščeno zaradi odvisnih razširitev.</translation>
 <translation id="4359717112757026264">Urbana pokrajina</translation>
 <translation id="4361142739114356624">Zasebni ključ za to potrdilo odjemalca manjka ali je neveljaven</translation>
diff --git a/chrome/app/resources/generated_resources_sr.xtb b/chrome/app/resources/generated_resources_sr.xtb
index 886bcc3..9a58706c 100644
--- a/chrome/app/resources/generated_resources_sr.xtb
+++ b/chrome/app/resources/generated_resources_sr.xtb
@@ -2202,7 +2202,6 @@
 <translation id="4350019051035968019">Овај уређај не може да се региструје на домену коме припада ваш налог јер је означен као уређај којим управља други домен.</translation>
 <translation id="4354344420232759511">Овде се приказују сајтови које сте посетили</translation>
 <translation id="435527878592612277">Изаберите слику</translation>
-<translation id="4356334633973342967">Или наведите свој управљачки програм:</translation>
 <translation id="4359408040881008151">Инсталиран је због зависних додатака.</translation>
 <translation id="4359717112757026264">Градски пејзажи</translation>
 <translation id="4361142739114356624">Приватни кључ за овај сертификат клијента недостаје или је неважећи</translation>
diff --git a/chrome/app/resources/generated_resources_sv.xtb b/chrome/app/resources/generated_resources_sv.xtb
index 86476d721..fda74de 100644
--- a/chrome/app/resources/generated_resources_sv.xtb
+++ b/chrome/app/resources/generated_resources_sv.xtb
@@ -2205,7 +2205,6 @@
 <translation id="4350019051035968019">Det går inte att registrera enheten på samma domän som dina konton eftersom enheten har registrerats som hanterad av en annan domän.</translation>
 <translation id="4354344420232759511">Webbplatser du besöker visas här</translation>
 <translation id="435527878592612277">Välj foto</translation>
-<translation id="4356334633973342967">Eller ange en egen drivrutin:</translation>
 <translation id="4359408040881008151">Installerades på grund av ett eller flera tillägg som är beroende av detta tillägg.</translation>
 <translation id="4359717112757026264">Cityscape</translation>
 <translation id="4361142739114356624">Det här klientcertifikatets privata nyckel saknas eller är tom</translation>
diff --git a/chrome/app/resources/generated_resources_sw.xtb b/chrome/app/resources/generated_resources_sw.xtb
index d0d57168..199ee65 100644
--- a/chrome/app/resources/generated_resources_sw.xtb
+++ b/chrome/app/resources/generated_resources_sw.xtb
@@ -2197,7 +2197,6 @@
 <translation id="4350019051035968019">Kifaa hiki hakiwezi kusajiliwa kwenye kikoa cha akaunti yako kwa sababu kifaa kimewekewa alama kwa usimamizi wa kikoa tofauti.</translation>
 <translation id="4354344420232759511">Tovuti unazotembelea zitaonekana hapa</translation>
 <translation id="435527878592612277">Chagua picha yako</translation>
-<translation id="4356334633973342967">Au bainisha kiendeshaji chako mwenyewe:</translation>
 <translation id="4359408040881008151">Kilisakinishwa kwa sababu ya kiendelezi au viendelezi vinavyotegemea.</translation>
 <translation id="4359717112757026264">Mandhari ya jiji</translation>
 <translation id="4361142739114356624">Ufunguo wa Faragha wa Cheti hiki cha Seva Teja haupo au si sahihi</translation>
diff --git a/chrome/app/resources/generated_resources_ta.xtb b/chrome/app/resources/generated_resources_ta.xtb
index dfd9e48..267a72a 100644
--- a/chrome/app/resources/generated_resources_ta.xtb
+++ b/chrome/app/resources/generated_resources_ta.xtb
@@ -2190,7 +2190,6 @@
 <translation id="4350019051035968019">இந்தச் சாதனத்தை உங்கள் கணக்கிற்குச் சொந்தமான களத்தில் பதிவுசெய்ய முடியாது, ஏனெனில் சாதனமானது வேறு களத்தால் நிர்வகிக்கப்படுவதற்காகக் குறிக்கப்பட்டுள்ளது.</translation>
 <translation id="4354344420232759511">நீங்கள் பார்வையிட்ட தளங்கள் இங்கே தோன்றும்</translation>
 <translation id="435527878592612277">உங்கள் படத்தைத் தேர்ந்தெடுக்கவும்</translation>
-<translation id="4356334633973342967">அல்லது உங்கள் சொந்த இயக்கியைக் குறிப்பிடவும்:</translation>
 <translation id="4359408040881008151">நீட்டிப்பு(கள்) சார்ந்திருப்பதன் காரணமாக நிறுவப்பட்டது.</translation>
 <translation id="4359717112757026264">நகரக் காட்சி</translation>
 <translation id="4361142739114356624">இந்தக் கிளையண்ட் சான்றிதழுக்கான தனிப்பட்ட குறியீடு இல்லை அல்லது தவறானது</translation>
diff --git a/chrome/app/resources/generated_resources_te.xtb b/chrome/app/resources/generated_resources_te.xtb
index 26ed84bc..17c940f 100644
--- a/chrome/app/resources/generated_resources_te.xtb
+++ b/chrome/app/resources/generated_resources_te.xtb
@@ -2205,7 +2205,6 @@
 <translation id="4350019051035968019">ఈ పరికరాన్ని మీ ఖాతా చెందిన డొమైన్‌కు నమోదు చేయడం సాధ్యపడదు. ఎందుకంటే పరికరం వేరే డొమైన్ ద్వారా నిర్వహించబడేలా గుర్తించబడింది.</translation>
 <translation id="4354344420232759511">మీరు సందర్శించే సైట్‌లు ఇక్కడ చూపబడతాయి</translation>
 <translation id="435527878592612277">మీ ఫోటోని ఎంచుకోండి</translation>
-<translation id="4356334633973342967">లేదంటే, మీ స్వంత డ్రైవర్‌ను పేర్కొనండి:</translation>
 <translation id="4359408040881008151">ఆధారిత పొడిగింపు(లు) కారణంగా ఇన్‌స్టాల్ చేయబడింది.</translation>
 <translation id="4359717112757026264">నగర చిత్రాలు</translation>
 <translation id="4361142739114356624">ఈ క్లయింట్ స‌ర్టిఫికెట్‌ ప్రైవేట్ కీ లేదు లేదా చెల్లదు</translation>
diff --git a/chrome/app/resources/generated_resources_th.xtb b/chrome/app/resources/generated_resources_th.xtb
index 3e9eb6f9..a089d56 100644
--- a/chrome/app/resources/generated_resources_th.xtb
+++ b/chrome/app/resources/generated_resources_th.xtb
@@ -1737,7 +1737,7 @@
 <translation id="3645372836428131288">เลื่อนนิ้วเล็กน้อยเพื่อจับภาพส่วนต่างๆ ของลายนิ้วมือ</translation>
 <translation id="3648348069317717750">ตรวจพบ <ph name="USB_DEVICE_NAME" /></translation>
 <translation id="3649138363871392317">จับภาพแล้ว</translation>
-<translation id="3649505501900178324">เกินกำหนดอัปเดตมานาน</translation>
+<translation id="3649505501900178324">พ้นกำหนดการอัปเดต</translation>
 <translation id="3650845953328929506">กำลังรออัปโหลดบันทึก</translation>
 <translation id="3650952250015018111">อนุญาตให้ "<ph name="APP_NAME" />" เข้าถึง:</translation>
 <translation id="3651488188562686558">ยกเลิกการเชื่อมต่อ Wi-Fi</translation>
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">ไม่สามารถลงทะเบียนอุปกรณ์นี้กับโดเมนที่บัญชีของคุณใช้อยู่ได้ เนื่องจากอุปกรณ์มีการทำเครื่องหมายไว้ว่าได้รับการจัดการจากโดเมนอื่น</translation>
 <translation id="4354344420232759511">เว็บไซต์ที่คุณเข้าชมจะปรากฏที่นี่</translation>
 <translation id="435527878592612277">เลือกรูปภาพของคุณ</translation>
-<translation id="4356334633973342967">หรือระบุไดรเวอร์ของคุณเอง:</translation>
 <translation id="4359408040881008151">ติดตั้งแล้วเนื่องจากมีส่วนขยายที่ต้องพึ่งพา</translation>
 <translation id="4359717112757026264">ทิวทัศน์ของเมือง</translation>
 <translation id="4361142739114356624">คีย์ส่วนตัวของใบรับรองไคลเอ็นต์นี้หายไปหรือไม่ถูกต้อง</translation>
diff --git a/chrome/app/resources/generated_resources_tr.xtb b/chrome/app/resources/generated_resources_tr.xtb
index 3dd1e75..83ff6582 100644
--- a/chrome/app/resources/generated_resources_tr.xtb
+++ b/chrome/app/resources/generated_resources_tr.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Bu cihaz, farklı bir alan adı tarafından yönetilen cihaz olarak işaretlendiğinden hesabınızın ait olduğu alan adına kaydettirilemiyor.</translation>
 <translation id="4354344420232759511">Ziyaret ettiğiniz siteler burada görünür</translation>
 <translation id="435527878592612277">Fotoğrafınızı seçin</translation>
-<translation id="4356334633973342967">Kendi sürücünüzü de belirtebilirsiniz:</translation>
 <translation id="4359408040881008151">Bağımlı uzantılar nedeniyle yüklendi.</translation>
 <translation id="4359717112757026264">Şehir manzaraları</translation>
 <translation id="4361142739114356624">Bu İstemci Sertifikası'nın Özel Anahtarı eksik veya geçersiz</translation>
diff --git a/chrome/app/resources/generated_resources_uk.xtb b/chrome/app/resources/generated_resources_uk.xtb
index b3490298..311d89b7 100644
--- a/chrome/app/resources/generated_resources_uk.xtb
+++ b/chrome/app/resources/generated_resources_uk.xtb
@@ -34,7 +34,7 @@
 <translation id="1039337018183941703">Недійсний або пошкоджений файл</translation>
 <translation id="1041175011127912238">Ця сторінка не відповідає</translation>
 <translation id="1042174272890264476">Ваш комп’ютер також має вбудовану бібліотеку RLZ у <ph name="SHORT_PRODUCT_NAME" />. Параметр RLZ призначає неунікальний тег, який не містить особисті дані, проте дозволяє вимірювати пошуки й користування <ph name="SHORT_PRODUCT_NAME" /> у рамках певної рекламної кампанії. Ці мітки інколи з’являються в пошукових запитах Google у <ph name="PRODUCT_NAME" />.</translation>
-<translation id="1045692658517323508">{0,plural, =1{Оновіть через 1 хвилину}one{Оновіть через # хвилину}few{Оновіть через # хвилини}many{Оновіть через # хвилин}other{Оновіть через # хвилини}}</translation>
+<translation id="1045692658517323508">{0,plural, =1{Оновіть протягом 1 хвилини}one{Оновіть протягом # хвилини}few{Оновіть протягом # хвилин}many{Оновіть протягом # хвилин}other{Оновіть протягом # хвилини}}</translation>
 <translation id="1046059554679513793">На жаль, це ім’я вже використовується.</translation>
 <translation id="1046635659603195359">Схоже, ви вже налаштували Voice Match для Google Асистента на іншому пристрої. За допомогою попередніх записів можна створити зразок голосу на цьому пристрої. Це займе менше хвилини.</translation>
 <translation id="1047431265488717055">Копіювати те&amp;кст посилання</translation>
@@ -1115,7 +1115,7 @@
 <translation id="2677748264148917807">Вийти</translation>
 <translation id="2678063897982469759">Знову ввімкнути</translation>
 <translation id="268053382412112343">Іс&amp;торія</translation>
-<translation id="2682374361574804119">{0,plural, =1{Оновіть через годину}one{Оновіть через # годину}few{Оновіть через # години}many{Оновіть через # годин}other{Оновіть через # години}}</translation>
+<translation id="2682374361574804119">{0,plural, =1{Оновіть протягом години}one{Оновіть протягом # години}few{Оновіть протягом # годин}many{Оновіть протягом # годин}other{Оновіть протягом # години}}</translation>
 <translation id="2682498795777673382">Батьки змінили налаштування</translation>
 <translation id="2683638487103917598">Папку відсортовано</translation>
 <translation id="2684004000387153598">Натисніть OK і виберіть "Додати користувача", щоб створити новий профіль для своєї електронної адреси.</translation>
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Цей пристрій не можна зареєструвати в домені, до якого належить ваш обліковий запис, оскільки пристрій зареєстрований в іншому домені.</translation>
 <translation id="4354344420232759511">Відвідані вами сайти з’являтимуться тут</translation>
 <translation id="435527878592612277">Виберіть фотографію</translation>
-<translation id="4356334633973342967">Або виберіть свій драйвер:</translation>
 <translation id="4359408040881008151">Установлено, оскільки є залежні розширення.</translation>
 <translation id="4359717112757026264">Міський пейзаж</translation>
 <translation id="4361142739114356624">Секретний ключ цього сертифіката клієнта відсутній або недійсний</translation>
@@ -2593,7 +2592,7 @@
 <translation id="4973307593867026061">Додати принтери</translation>
 <translation id="4973325300212422370">{NUM_TABS,plural, =1{Вимкнути звук на сайті}one{Вимкнути звук на сайтах}few{Вимкнути звук на сайтах}many{Вимкнути звук на сайтах}other{Вимкнути звук на сайтах}}</translation>
 <translation id="4974733135013075877">Вийти та заблокувати</translation>
-<translation id="4976795213394241669">{0,plural, =0{Оновіть зараз}=1{Оновіть через 1 секунду}one{Оновіть через # секунду}few{Оновіть через # секунди}many{Оновіть через # секунд}other{Оновіть через # секунди}}</translation>
+<translation id="4976795213394241669">{0,plural, =0{Оновіть зараз}=1{Оновіть протягом 1 секунди}one{Оновіть протягом # секунди}few{Оновіть протягом # секунд}many{Оновіть протягом # секунд}other{Оновіть протягом # секунди}}</translation>
 <translation id="4977942889532008999">Підтвердити доступ</translation>
 <translation id="4980805016576257426">Це розширення містить зловмисне програмне забезпечення.</translation>
 <translation id="4981449534399733132">Щоб очистити дані веб-перегляду на всіх синхронізованих пристроях і в обліковому записі Google, <ph name="BEGIN_LINK" />увійдіть в обліковий запис<ph name="END_LINK" />.</translation>
@@ -5194,7 +5193,7 @@
 <translation id="8948939328578167195">Сайт <ph name="WEBSITE" /> хоче бачити марку й модель вашого ключа безпеки</translation>
 <translation id="8951256747718668828">Не вдалося відновити через помилку</translation>
 <translation id="895347679606913382">Запуск...</translation>
-<translation id="8956941634583033512">{0,plural, =1{Оновіть через день}one{Оновіть через # день}few{Оновіть через # дні}many{Оновіть через # днів}other{Оновіть через # дня}}</translation>
+<translation id="8956941634583033512">{0,plural, =1{Оновіть протягом дня}one{Оновіть протягом # дня}few{Оновіть протягом # днів}many{Оновіть протягом # днів}other{Оновіть протягом # дня}}</translation>
 <translation id="895944840846194039">Пам’ять JavaScript</translation>
 <translation id="8959810181433034287">Контрольований користувач повинен буде використовувати цей пароль для входу, тому придумайте безпечний пароль і не забудьте повідомити його контрольованому користувачу.</translation>
 <translation id="8962083179518285172">Сховати деталі</translation>
diff --git a/chrome/app/resources/generated_resources_vi.xtb b/chrome/app/resources/generated_resources_vi.xtb
index 73795b4..220b767 100644
--- a/chrome/app/resources/generated_resources_vi.xtb
+++ b/chrome/app/resources/generated_resources_vi.xtb
@@ -2206,7 +2206,6 @@
 <translation id="4350019051035968019">Không thể đăng ký thiết bị này vào miền có tài khoản của bạn vì thiết bị được miền khác đánh dấu để quản lý.</translation>
 <translation id="4354344420232759511">Các trang web bạn truy cập sẽ xuất hiện ở đây</translation>
 <translation id="435527878592612277">Chọn ảnh của bạn</translation>
-<translation id="4356334633973342967">Hoặc chỉ định trình điều khiển của riêng bạn:</translation>
 <translation id="4359408040881008151">Đã được cài đặt vì (các) tiện ích phụ thuộc.</translation>
 <translation id="4359717112757026264">Cảnh quan thành phố</translation>
 <translation id="4361142739114356624">Khóa cá nhân cho chứng chỉ ứng dụng này còn thiếu hoặc không hợp lệ</translation>
diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/resources/generated_resources_zh-CN.xtb
index 2207d87..7dec90d 100644
--- a/chrome/app/resources/generated_resources_zh-CN.xtb
+++ b/chrome/app/resources/generated_resources_zh-CN.xtb
@@ -2198,7 +2198,6 @@
 <translation id="4350019051035968019">该设备无法注册到您的帐号所属的网域,因为它已标记为受其他网域管理。</translation>
 <translation id="4354344420232759511">您访问的网站将显示在此处</translation>
 <translation id="435527878592612277">选择您的照片</translation>
-<translation id="4356334633973342967">或指定您自己的驱动程序:</translation>
 <translation id="4359408040881008151">安装此扩展程序是因为某个/些扩展程序需要安装它才能正常使用。</translation>
 <translation id="4359717112757026264">城市景观</translation>
 <translation id="4361142739114356624">缺少此客户端证书的私钥,或私钥无效</translation>
diff --git a/chrome/app/resources/generated_resources_zh-TW.xtb b/chrome/app/resources/generated_resources_zh-TW.xtb
index 991b4f2..d0bf7a4 100644
--- a/chrome/app/resources/generated_resources_zh-TW.xtb
+++ b/chrome/app/resources/generated_resources_zh-TW.xtb
@@ -4,7 +4,7 @@
 <translation id="1003088604756913841">在新的「<ph name="APP" />」視窗中開啟連結</translation>
 <translation id="1004218526896219317">網站存取權</translation>
 <translation id="1005274289863221750">使用你的麥克風和相機</translation>
-<translation id="1005608198058526414">查看你的 Family Link 設定</translation>
+<translation id="1005608198058526414">查看您的 Family Link 設定</translation>
 <translation id="1006873397406093306">這項擴充功能可以讀取及變更你造訪的網站所使用的資料。你可以自行設定要給予這項擴充功能哪些網站的存取權限。</translation>
 <translation id="1007408791287232274">無法載入裝置。</translation>
 <translation id="1008186147501209563">匯出書籤</translation>
@@ -2204,7 +2204,6 @@
 <translation id="4350019051035968019">這台裝置已標示為由其他網域管理,因此無法註冊到你帳戶所屬的網域。</translation>
 <translation id="4354344420232759511">這裡會顯示你曾造訪的網站</translation>
 <translation id="435527878592612277">請選取你的相片</translation>
-<translation id="4356334633973342967">或指定你自己的硬碟:</translation>
 <translation id="4359408040881008151">你已安裝相依的擴充功能,因此必須一併安裝這個擴充功能。</translation>
 <translation id="4359717112757026264">城市景觀</translation>
 <translation id="4361142739114356624">這個用戶端憑證沒有私密金鑰或私密金鑰無效</translation>
@@ -4467,8 +4466,8 @@
 <translation id="7847212883280406910">按下 Ctrl + Alt + S 鍵切換至 <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation>
 <translation id="7849264908733290972">在新分頁中開啟圖片(&amp;I)</translation>
 <translation id="784934925303690534">時間範圍</translation>
-<translation id="7850222546481862746"><ph name="BEGIN_PARAGRAPH1" />你必須登出「<ph name="USER_NAME" />」的帳戶,才能在這部裝置上完成監督功能的設定程序。<ph name="END_PARAGRAPH1" />
-      <ph name="BEGIN_PARAGRAPH2" />如要管理子女裝置上的家長監護功能,請在你自己的裝置上安裝 Family Link 家長版。<ph name="END_PARAGRAPH2" /></translation>
+<translation id="7850222546481862746"><ph name="BEGIN_PARAGRAPH1" />您必須登出<ph name="USER_NAME" />的帳戶,才能在這部裝置上完成監督功能的設定程序。<ph name="END_PARAGRAPH1" />
+      <ph name="BEGIN_PARAGRAPH2" />如要管理子女裝置上的家長監護功能,請在您自己的裝置上安裝 Family Link 家長版。<ph name="END_PARAGRAPH2" /></translation>
 <translation id="7851457902707056880">只能使用擁有者帳戶才能登入。請重新開機並使用擁有者帳戶登入。電腦會在 30 秒後自動開機。</translation>
 <translation id="7851716364080026749">一律封鎖存取攝影機和麥克風</translation>
 <translation id="7853747251428735">更多工具(&amp;L)</translation>
diff --git a/chrome/app/resources/google_chrome_strings_id.xtb b/chrome/app/resources/google_chrome_strings_id.xtb
index c500156..5fde49b 100644
--- a/chrome/app/resources/google_chrome_strings_id.xtb
+++ b/chrome/app/resources/google_chrome_strings_id.xtb
@@ -121,7 +121,7 @@
 <translation id="4143243756087420366">Nama dan gambar Chrome</translation>
 <translation id="4147555960264124640">Anda masuk dengan akun yang dikelola dan memberikan administratornya kontrol atas profil Google Chrome Anda. Data Chrome Anda, seperti aplikasi, bookmark, histori, sandi, dan setelan lain selamanya akan dikaitkan ke <ph name="USER_NAME" />. Anda dapat menghapus data ini melalui Dasbor Akun Google, namun Anda tidak akan dapat mengaitkan data ini dengan akun yang lain. <ph name="LEARN_MORE" /></translation>
 <translation id="4149882025268051530">Instalatur gagal mendekompresi arsip. Download kembali Google Chrome.</translation>
-<translation id="4191857738314598978">{0,plural, =1{Luncurkan ulang Chrome dalam 1 hari}other{Luncurkan ulang Chrome dalam # hari}}</translation>
+<translation id="4191857738314598978">{0,plural, =1{Luncurkan ulang Chrome dalam satu hari}other{Luncurkan ulang Chrome dalam # hari}}</translation>
 <translation id="424864128008805179">Logout dari Chrome?</translation>
 <translation id="4251615635259297716">Tautkan data Chrome Anda ke akun ini?</translation>
 <translation id="4251625577313994583">dapatkan Chrome di iPhone</translation>
diff --git a/chrome/app/xr_consent_ui_strings.grdp b/chrome/app/xr_consent_ui_strings.grdp
index 2f6a0a64..880fb8f 100644
--- a/chrome/app/xr_consent_ui_strings.grdp
+++ b/chrome/app/xr_consent_ui_strings.grdp
@@ -8,11 +8,11 @@
   </message>
   <message name="IDS_XR_CONSENT_DIALOG_DESCRIPTION" desc="Body of the user consent dialog displayed before allowing a website to start a VR presentation">
     Sensor data will only be shared while you're in this VR experience. The site may be able to learn about you using certain info, such as:
-  - Your location
-  - Your physical features, like eye position
-  - Your movements, like how you walk
+    - Your location
+    - Your physical features, like eye position
+    - Your movements, like how you walk
 
-Make sure you trust this site before you allow access.
+    Make sure you trust this site before you allow access.
   </message>
   <message name="IDS_XR_CONSENT_DIALOG_BUTTON_DENY_VR" desc="Text on the button of a user consent dialog which denies a website from starting a VR presentation">
     Don&#39;t allow
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 3a1b8a4..022da24b 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1178,10 +1178,6 @@
     "policy/chrome_browser_policy_connector.h",
     "policy/cloud/cloud_policy_invalidator.cc",
     "policy/cloud/cloud_policy_invalidator.h",
-    "policy/cloud/policy_header_navigation_throttle.cc",
-    "policy/cloud/policy_header_navigation_throttle.h",
-    "policy/cloud/policy_header_service_factory.cc",
-    "policy/cloud/policy_header_service_factory.h",
     "policy/cloud/remote_commands_invalidator.cc",
     "policy/cloud/remote_commands_invalidator.h",
     "policy/cloud/remote_commands_invalidator_impl.cc",
@@ -2585,6 +2581,7 @@
       "android/trusted_cdn.cc",
       "android/trusted_cdn.h",
       "android/url_utilities.cc",
+      "android/usage_stats/notification_suspender.cc",
       "android/usage_stats/usage_stats_bridge.cc",
       "android/usage_stats/usage_stats_bridge.h",
       "android/usage_stats/usage_stats_database.cc",
@@ -2728,6 +2725,8 @@
       "password_manager/password_manager_infobar_delegate_android.h",
       "password_manager/save_password_infobar_delegate_android.cc",
       "password_manager/save_password_infobar_delegate_android.h",
+      "password_manager/touch_to_fill_controller.cc",
+      "password_manager/touch_to_fill_controller.h",
       "password_manager/update_password_infobar_delegate_android.cc",
       "password_manager/update_password_infobar_delegate_android.h",
       "payments/android/can_make_payment_query_android.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 513f648..3848be2 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2236,6 +2236,9 @@
     {"arc-usb-host", flag_descriptions::kArcUsbHostName,
      flag_descriptions::kArcUsbHostDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(arc::kUsbHostFeature)},
+    {"arc-usb-storage-ui", flag_descriptions::kArcUsbStorageUIName,
+     flag_descriptions::kArcUsbStorageUIDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(arc::kUsbStorageUIFeature)},
     {"arc-vpn", flag_descriptions::kArcVpnName,
      flag_descriptions::kArcVpnDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(arc::kVpnFeature)},
@@ -3903,6 +3906,13 @@
      FEATURE_VALUE_TYPE(chromeos::features::kSplitSettings)},
 #endif  // defined(OS_CHROMEOS)
 
+#if defined(OS_CHROMEOS)
+    {"gesture-properties-dbus-service",
+     flag_descriptions::kEnableGesturePropertiesDBusServiceName,
+     flag_descriptions::kEnableGesturePropertiesDBusServiceDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::features::kGesturePropertiesDBusService)},
+#endif  // defined(OS_CHROMEOS)
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/bookmarks/bookmark_bridge.cc b/chrome/browser/android/bookmarks/bookmark_bridge.cc
index d083070e..6907232 100644
--- a/chrome/browser/android/bookmarks/bookmark_bridge.cc
+++ b/chrome/browser/android/bookmarks/bookmark_bridge.cc
@@ -640,7 +640,8 @@
   const BookmarkNode* parent = GetNodeByID(bookmark_id, type);
 
   const BookmarkNode* new_node = bookmark_model_->AddFolder(
-      parent, index, base::android::ConvertJavaStringToUTF16(env, j_title));
+      parent, size_t{index},
+      base::android::ConvertJavaStringToUTF16(env, j_title));
   DCHECK(new_node);
   ScopedJavaLocalRef<jobject> new_java_obj =
       JavaBookmarkIdCreateBookmarkId(
@@ -690,7 +691,7 @@
   bookmark_id = JavaBookmarkIdGetId(env, j_parent_id_obj);
   type = JavaBookmarkIdGetType(env, j_parent_id_obj);
   const BookmarkNode* new_parent_node = GetNodeByID(bookmark_id, type);
-  bookmark_model_->Move(node, new_parent_node, index);
+  bookmark_model_->Move(node, new_parent_node, size_t{index});
 }
 
 ScopedJavaLocalRef<jobject> BookmarkBridge::AddBookmark(
@@ -706,8 +707,7 @@
   const BookmarkNode* parent = GetNodeByID(bookmark_id, type);
 
   const BookmarkNode* new_node = bookmark_model_->AddURL(
-      parent,
-      index,
+      parent, size_t{index},
       base::android::ConvertJavaStringToUTF16(env, j_title),
       GURL(base::android::ConvertJavaStringToUTF16(env, j_url)));
   DCHECK(new_node);
@@ -914,10 +914,10 @@
 }
 
 void BookmarkBridge::BookmarkNodeMoved(BookmarkModel* model,
-                                        const BookmarkNode* old_parent,
-                                        int old_index,
-                                        const BookmarkNode* new_parent,
-                                        int new_index) {
+                                       const BookmarkNode* old_parent,
+                                       size_t old_index,
+                                       const BookmarkNode* new_parent,
+                                       size_t new_index) {
   if (!IsLoaded())
     return;
 
@@ -926,13 +926,13 @@
   if (obj.is_null())
     return;
   Java_BookmarkBridge_bookmarkNodeMoved(
-      env, obj, CreateJavaBookmark(old_parent), old_index,
-      CreateJavaBookmark(new_parent), new_index);
+      env, obj, CreateJavaBookmark(old_parent), int{old_index},
+      CreateJavaBookmark(new_parent), int{new_index});
 }
 
 void BookmarkBridge::BookmarkNodeAdded(BookmarkModel* model,
-                                        const BookmarkNode* parent,
-                                        int index) {
+                                       const BookmarkNode* parent,
+                                       size_t index) {
   if (!IsLoaded())
     return;
 
@@ -941,14 +941,14 @@
   if (obj.is_null())
     return;
   Java_BookmarkBridge_bookmarkNodeAdded(env, obj, CreateJavaBookmark(parent),
-                                        index);
+                                        int{index});
 }
 
 void BookmarkBridge::BookmarkNodeRemoved(BookmarkModel* model,
-                                          const BookmarkNode* parent,
-                                          int old_index,
-                                          const BookmarkNode* node,
-                                          const std::set<GURL>& removed_urls) {
+                                         const BookmarkNode* parent,
+                                         size_t old_index,
+                                         const BookmarkNode* node,
+                                         const std::set<GURL>& removed_urls) {
   if (!IsLoaded())
     return;
 
@@ -957,7 +957,8 @@
   if (obj.is_null())
     return;
   Java_BookmarkBridge_bookmarkNodeRemoved(env, obj, CreateJavaBookmark(parent),
-                                          old_index, CreateJavaBookmark(node));
+                                          int{old_index},
+                                          CreateJavaBookmark(node));
 }
 
 void BookmarkBridge::BookmarkAllUserNodesRemoved(
diff --git a/chrome/browser/android/bookmarks/bookmark_bridge.h b/chrome/browser/android/bookmarks/bookmark_bridge.h
index 4222738..56ebe488 100644
--- a/chrome/browser/android/bookmarks/bookmark_bridge.h
+++ b/chrome/browser/android/bookmarks/bookmark_bridge.h
@@ -233,15 +233,15 @@
   void BookmarkModelBeingDeleted(bookmarks::BookmarkModel* model) override;
   void BookmarkNodeMoved(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* old_parent,
-                         int old_index,
+                         size_t old_index,
                          const bookmarks::BookmarkNode* new_parent,
-                         int new_index) override;
+                         size_t new_index) override;
   void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* parent,
-                         int index) override;
+                         size_t index) override;
   void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
                            const bookmarks::BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const bookmarks::BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override;
   void BookmarkAllUserNodesRemoved(bookmarks::BookmarkModel* model,
diff --git a/chrome/browser/android/bookmarks/partner_bookmarks_shim_unittest.cc b/chrome/browser/android/bookmarks/partner_bookmarks_shim_unittest.cc
index 4abe19b0..bd404cd 100644
--- a/chrome/browser/android/bookmarks/partner_bookmarks_shim_unittest.cc
+++ b/chrome/browser/android/bookmarks/partner_bookmarks_shim_unittest.cc
@@ -49,14 +49,14 @@
                                   const base::string16& title) {
     if (!parent)
       parent = model_->bookmark_bar_node();
-    return model_->AddURL(parent, parent->child_count(), title, url);
+    return model_->AddURL(parent, parent->children().size(), title, url);
   }
 
   const BookmarkNode* AddFolder(const BookmarkNode* parent,
                                 const base::string16& title) {
     if (!parent)
       parent = model_->bookmark_bar_node();
-    return model_->AddFolder(parent, parent->child_count(), title);
+    return model_->AddFolder(parent, parent->children().size(), title);
   }
 
  protected:
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index c3914564..87b90cb 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -145,6 +145,7 @@
     &kIntentBlockExternalFormRedirectsNoGesture,
     &kJellyBeanSupported,
     &kNewPhotoPicker,
+    &kNotificationSuspender,
     &kNoCreditCardAbort,
     &kNTPButton,
     &kNTPLaunchAfterInactivity,
@@ -452,6 +453,11 @@
 const base::Feature kNewPhotoPicker{"NewPhotoPicker",
                                     base::FEATURE_ENABLED_BY_DEFAULT};
 
+// TODO(knollr): This is a temporary kill switch, it can be removed once we feel
+// okay about leaving it on.
+const base::Feature kNotificationSuspender{"NotificationSuspender",
+                                           base::FEATURE_ENABLED_BY_DEFAULT};
+
 const base::Feature kNoCreditCardAbort{"NoCreditCardAbort",
                                        base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 1c04e49..87d4116 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -83,6 +83,7 @@
 extern const base::Feature kJellyBeanSupported;
 extern const base::Feature kLanguagesPreference;
 extern const base::Feature kNewPhotoPicker;
+extern const base::Feature kNotificationSuspender;
 extern const base::Feature kNoCreditCardAbort;
 extern const base::Feature kNTPButton;
 extern const base::Feature kNTPLaunchAfterInactivity;
diff --git a/chrome/browser/android/history_report/data_observer.cc b/chrome/browser/android/history_report/data_observer.cc
index 704fd8c7..0108344 100644
--- a/chrome/browser/android/history_report/data_observer.cc
+++ b/chrome/browser/android/history_report/data_observer.cc
@@ -51,20 +51,20 @@
 
 void DataObserver::BookmarkNodeMoved(BookmarkModel* model,
                                      const BookmarkNode* old_parent,
-                                     int old_index,
+                                     size_t old_index,
                                      const BookmarkNode* new_parent,
-                                     int new_index) {}
+                                     size_t new_index) {}
 
 void DataObserver::BookmarkNodeAdded(BookmarkModel* model,
                                      const BookmarkNode* parent,
-                                     int index) {
-  delta_file_service_->PageAdded(parent->GetChild(index)->url());
+                                     size_t index) {
+  delta_file_service_->PageAdded(parent->children()[index]->url());
   data_changed_callback_.Run();
 }
 
 void DataObserver::BookmarkNodeRemoved(BookmarkModel* model,
                                        const BookmarkNode* parent,
-                                       int old_index,
+                                       size_t old_index,
                                        const BookmarkNode* node,
                                        const std::set<GURL>& removed_urls) {
   DeleteBookmarks(removed_urls);
diff --git a/chrome/browser/android/history_report/data_observer.h b/chrome/browser/android/history_report/data_observer.h
index c8e8eb9..d83a1e95 100644
--- a/chrome/browser/android/history_report/data_observer.h
+++ b/chrome/browser/android/history_report/data_observer.h
@@ -44,15 +44,15 @@
   void BookmarkModelBeingDeleted(bookmarks::BookmarkModel* model) override;
   void BookmarkNodeMoved(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* old_parent,
-                         int old_index,
+                         size_t old_index,
                          const bookmarks::BookmarkNode* new_parent,
-                         int new_index) override;
+                         size_t new_index) override;
   void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* parent,
-                         int index) override;
+                         size_t index) override;
   void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
                            const bookmarks::BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const bookmarks::BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override;
   void BookmarkAllUserNodesRemoved(
diff --git a/chrome/browser/android/omnibox/autocomplete_controller_android.cc b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
index 01c2a00..f9c7094 100644
--- a/chrome/browser/android/omnibox/autocomplete_controller_android.cc
+++ b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
@@ -266,6 +266,11 @@
   const auto& match =
       autocomplete_controller_->result().match_at(selected_index);
   SuggestionAnswer::LogAnswerUsed(match.answer);
+
+  UMA_HISTOGRAM_BOOLEAN(
+      "Omnibox.SuggestionUsed.RichEntity",
+      match.type == AutocompleteMatchType::SEARCH_SUGGEST_ENTITY);
+
   if (match.type == AutocompleteMatchType::CLIPBOARD_URL) {
     UMA_HISTOGRAM_LONG_TIMES_100(
         "MobileOmnibox.PressedClipboardSuggestionAge",
diff --git a/chrome/browser/android/provider/chrome_browser_provider.cc b/chrome/browser/android/provider/chrome_browser_provider.cc
index 7f89ab47..3490ad3 100644
--- a/chrome/browser/android/provider/chrome_browser_provider.cc
+++ b/chrome/browser/android/provider/chrome_browser_provider.cc
@@ -188,10 +188,9 @@
       if (!parent_node)
         parent_node = model->bookmark_bar_node();
 
-      if (is_folder)
-        node = model->AddFolder(parent_node, parent_node->child_count(), title);
-      else
-        node = model->AddURL(parent_node, 0, title, gurl);
+      node = is_folder ? model->AddFolder(parent_node,
+                                          parent_node->children().size(), title)
+                       : model->AddURL(parent_node, 0, title, gurl);
     }
 
     *result = node ? node ->id() : kInvalidBookmarkId;
diff --git a/chrome/browser/android/signin/signin_manager_android.cc b/chrome/browser/android/signin/signin_manager_android.cc
index 200c73a1..836d548 100644
--- a/chrome/browser/android/signin/signin_manager_android.cc
+++ b/chrome/browser/android/signin/signin_manager_android.cc
@@ -209,6 +209,31 @@
                       weak_factory_.GetWeakPtr()));
 }
 
+void SigninManagerAndroid::RegisterPolicyWithAccount(
+    const CoreAccountInfo& account,
+    RegisterPolicyWithAccountCallback callback) {
+  if (!ShouldLoadPolicyForUser(account.email)) {
+    std::move(callback).Run(base::nullopt);
+    return;
+  }
+
+  policy::UserPolicySigninService* service =
+      policy::UserPolicySigninServiceFactory::GetForProfile(profile_);
+
+  service->RegisterForPolicyWithAccountId(
+      account.email, account.account_id,
+      base::AdaptCallbackForRepeating(base::BindOnce(
+          [](RegisterPolicyWithAccountCallback callback,
+             const std::string& dm_token, const std::string& client_id) {
+            base::Optional<ManagementCredentials> credentials;
+            if (!dm_token.empty()) {
+              credentials.emplace(dm_token, client_id);
+            }
+            std::move(callback).Run(credentials);
+          },
+          std::move(callback))));
+}
+
 void SigninManagerAndroid::RegisterAndFetchPolicyBeforeSignIn(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
@@ -220,43 +245,39 @@
   // ExtractDomainName Dchecks that username is a valid email, in practice
   // this checks that @ is present and is not the last character.
   gaia::ExtractDomainName(username);
-
-  policy::UserPolicySigninService* service =
-      policy::UserPolicySigninServiceFactory::GetForProfile(profile_);
   CoreAccountInfo account =
       IdentityManagerFactory::GetForProfile(profile_)
           ->FindAccountInfoForAccountWithRefreshTokenByEmailAddress(username)
           .value();
-  service->RegisterForPolicyWithAccountId(
-      username, account.account_id,
-      base::Bind(&SigninManagerAndroid::OnPolicyRegisterDone,
-                 weak_factory_.GetWeakPtr(), account));
+
+  RegisterPolicyWithAccount(
+      account, base::BindOnce(&SigninManagerAndroid::OnPolicyRegisterDone,
+                              weak_factory_.GetWeakPtr(), account));
 }
 
-void SigninManagerAndroid::OnPolicyRegisterDone(const CoreAccountInfo& account,
-                                                const std::string& dm_token,
-                                                const std::string& client_id) {
-  if (dm_token.empty()) {
+void SigninManagerAndroid::OnPolicyRegisterDone(
+    const CoreAccountInfo& account,
+    const base::Optional<ManagementCredentials>& credentials) {
+  if (credentials) {
+    FetchPolicyBeforeSignIn(account, credentials.value());
+  } else {
     // User's account does not have a policy to fetch.
     JNIEnv* env = base::android::AttachCurrentThread();
     Java_SigninManager_onPolicyFetchedBeforeSignIn(env, java_signin_manager_);
-  } else {
-    FetchPolicyBeforeSignIn(account, dm_token, client_id);
   }
 }
 
 void SigninManagerAndroid::FetchPolicyBeforeSignIn(
     const CoreAccountInfo& account,
-    const std::string& dm_token,
-    const std::string& client_id) {
+    const ManagementCredentials& credentials) {
   policy::UserPolicySigninService* service =
       policy::UserPolicySigninServiceFactory::GetForProfile(profile_);
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
       content::BrowserContext::GetDefaultStoragePartition(profile_)
           ->GetURLLoaderFactoryForBrowserProcess();
   service->FetchPolicyForSignedInUser(
-      AccountIdFromAccountInfo(account), dm_token, client_id,
-      url_loader_factory,
+      AccountIdFromAccountInfo(account), credentials.dm_token,
+      credentials.client_id, url_loader_factory,
       base::Bind(&SigninManagerAndroid::OnPolicyFetchDone,
                  weak_factory_.GetWeakPtr()));
 }
@@ -335,43 +356,26 @@
   return reinterpret_cast<intptr_t>(signin_manager_android);
 }
 
-static jboolean JNI_SigninManager_ShouldLoadPolicyForUser(
+void SigninManagerAndroid::IsUserManaged(
     JNIEnv* env,
-    const JavaParamRef<jstring>& j_username) {
-  std::string username =
-      base::android::ConvertJavaStringToUTF8(env, j_username);
-  return ShouldLoadPolicyForUser(username);
-}
-
-static void JNI_SigninManager_IsUserManaged(
-    JNIEnv* env,
+    const JavaParamRef<jobject>& obj,
     const JavaParamRef<jstring>& j_username,
     const JavaParamRef<jobject>& j_callback) {
   base::android::ScopedJavaGlobalRef<jobject> callback(env, j_callback);
   std::string username =
       base::android::ConvertJavaStringToUTF8(env, j_username);
-  if (!ShouldLoadPolicyForUser(username)) {
-    base::android::RunBooleanCallbackAndroid(callback, false);
-    return;
-  }
 
-  Profile* profile = ProfileManager::GetActiveUserProfile();
-  policy::UserPolicySigninService* service =
-      policy::UserPolicySigninServiceFactory::GetForProfile(profile);
-  service->RegisterForPolicyWithAccountId(
-      username,
-      IdentityManagerFactory::GetForProfile(profile)
-          ->FindAccountInfoForAccountWithRefreshTokenByEmailAddress(username)
-          .value_or(AccountInfo{})
-          .account_id,
-      // TODO: if UserPolicySigninService::PolicyRegistrationCallback is changed
-      // to base::OnceCallback, change this to base::BindOnce; otherwise remove
-      // this comment. See https://crbug.com/948098 for more details.
-      base::BindRepeating(
+  base::Optional<CoreAccountInfo> account =
+      IdentityManagerFactory::GetForProfile(profile_)
+          ->FindAccountInfoForAccountWithRefreshTokenByEmailAddress(username);
+
+  RegisterPolicyWithAccount(
+      account.value_or(CoreAccountInfo{}),
+      base::BindOnce(
           [](base::android::ScopedJavaGlobalRef<jobject> callback,
-             const std::string& dm_token, const std::string& client_id) {
+             const base::Optional<ManagementCredentials>& credentials) {
             base::android::RunBooleanCallbackAndroid(callback,
-                                                     !dm_token.empty());
+                                                     credentials.has_value());
           },
           callback));
 }
diff --git a/chrome/browser/android/signin/signin_manager_android.h b/chrome/browser/android/signin/signin_manager_android.h
index 2727092..6bce23d 100644
--- a/chrome/browser/android/signin/signin_manager_android.h
+++ b/chrome/browser/android/signin/signin_manager_android.h
@@ -79,24 +79,44 @@
   jboolean IsSignedInOnNative(JNIEnv* env,
                               const base::android::JavaParamRef<jobject>& obj);
 
+  void IsUserManaged(JNIEnv* env,
+                     const base::android::JavaParamRef<jobject>& obj,
+                     const base::android::JavaParamRef<jstring>& j_username,
+                     const base::android::JavaParamRef<jobject>& j_callback);
+
   // identity::IdentityManager::Observer implementation.
   void OnPrimaryAccountCleared(
       const CoreAccountInfo& previous_primary_account_info) override;
 
  private:
+  struct ManagementCredentials {
+    ManagementCredentials(const std::string& dm_token,
+                          const std::string& client_id)
+        : dm_token(dm_token), client_id(client_id) {}
+    const std::string dm_token;
+    const std::string client_id;
+  };
+
+  using RegisterPolicyWithAccountCallback = base::OnceCallback<void(
+      const base::Optional<ManagementCredentials>& credentials)>;
+
   friend class SigninManagerAndroidTest;
   FRIEND_TEST_ALL_PREFIXES(SigninManagerAndroidTest,
                            DeleteGoogleServiceWorkerCaches);
 
   ~SigninManagerAndroid() override;
 
-  void OnPolicyRegisterDone(const CoreAccountInfo& account_id,
-                            const std::string& dm_token,
-                            const std::string& client_id);
+  // If required registers for policy with given account. callback will be
+  // called with credentials if the account is managed.
+  void RegisterPolicyWithAccount(const CoreAccountInfo& account,
+                                 RegisterPolicyWithAccountCallback callback);
+
+  void OnPolicyRegisterDone(
+      const CoreAccountInfo& account_id,
+      const base::Optional<ManagementCredentials>& credentials);
 
   void FetchPolicyBeforeSignIn(const CoreAccountInfo& account_id,
-                               const std::string& dm_token,
-                               const std::string& client_id);
+                               const ManagementCredentials& credentials);
 
   void OnPolicyFetchDone(bool success) const;
 
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.cc b/chrome/browser/android/tab_web_contents_delegate_android.cc
index ad79cb6..bc22ecd 100644
--- a/chrome/browser/android/tab_web_contents_delegate_android.cc
+++ b/chrome/browser/android/tab_web_contents_delegate_android.cc
@@ -316,7 +316,7 @@
 bool TabWebContentsDelegateAndroid::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   return MediaCaptureDevicesDispatcher::GetInstance()
       ->CheckMediaAccessPermission(render_frame_host, security_origin, type);
 }
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.h b/chrome/browser/android/tab_web_contents_delegate_android.h
index b4f69bd..332dfed 100644
--- a/chrome/browser/android/tab_web_contents_delegate_android.h
+++ b/chrome/browser/android/tab_web_contents_delegate_android.h
@@ -67,7 +67,7 @@
       content::MediaResponseCallback callback) override;
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type) override;
+                                  blink::mojom::MediaStreamType type) override;
   void SetOverlayMode(bool use_overlay_mode) override;
   void RequestPpapiBrokerPermission(
       content::WebContents* web_contents,
diff --git a/chrome/browser/android/usage_stats/notification_suspender.cc b/chrome/browser/android/usage_stats/notification_suspender.cc
new file mode 100644
index 0000000..74c65098
--- /dev/null
+++ b/chrome/browser/android/usage_stats/notification_suspender.cc
@@ -0,0 +1,142 @@
+// 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 <vector>
+
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/logging.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_android.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/platform_notification_context.h"
+#include "content/public/browser/storage_partition.h"
+#include "jni/NotificationSuspender_jni.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/android/java_bitmap.h"
+#include "ui/gfx/image/image.h"
+
+using base::android::AttachCurrentThread;
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+using content::BrowserContext;
+using content::NotificationResourceData;
+using content::PlatformNotificationContext;
+
+namespace {
+
+SkBitmap ExtractImage(JNIEnv* env,
+                      const JavaParamRef<jobjectArray>& j_resources,
+                      int index) {
+  ScopedJavaLocalRef<jobject> j_image(
+      env, env->GetObjectArrayElement(j_resources, index));
+  return j_image.is_null()
+             ? SkBitmap()
+             : CreateSkBitmapFromJavaBitmap(gfx::JavaBitmap(j_image));
+}
+
+std::vector<blink::NotificationResources> ParseResources(
+    JNIEnv* env,
+    const JavaParamRef<jobjectArray>& j_resources) {
+  // Resources is an array of bitmaps with the following order:
+  // [icon, badge, image, icon, badge, image, ...]
+  int resource_count = env->GetArrayLength(j_resources);
+  DCHECK(resource_count % 3 == 0);
+
+  std::vector<blink::NotificationResources> resources;
+  for (int i = 0; i < resource_count; i += 3) {
+    blink::NotificationResources res;
+    res.notification_icon = ExtractImage(env, j_resources, i + 0);
+    res.badge = ExtractImage(env, j_resources, i + 1);
+    res.image = ExtractImage(env, j_resources, i + 2);
+    resources.emplace_back(std::move(res));
+  }
+  return resources;
+}
+
+PlatformNotificationContext* GetContext(Profile* profile, const GURL& origin) {
+  auto* partition = BrowserContext::GetStoragePartitionForSite(profile, origin);
+  auto* context = partition->GetPlatformNotificationContext();
+  DCHECK(context);
+  return context;
+}
+
+}  // namespace
+
+namespace usage_stats {
+
+// Stores the given |j_resources| to be displayed later again. Note that
+// |j_resources| is expected to have 3 entries (icon, badge, image in that
+// order) for each notification id in |j_notification_ids|. If a notification
+// does not have a particular resource, pass null instead. |j_origins| must be
+// the same size as |j_notification_ids|.
+static void JNI_NotificationSuspender_StoreNotificationResources(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& j_profile,
+    const JavaParamRef<jobjectArray>& j_notification_ids,
+    const JavaParamRef<jobjectArray>& j_origins,
+    const JavaParamRef<jobjectArray>& j_resources) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile);
+  DCHECK(profile);
+
+  std::vector<std::string> id_strings;
+  AppendJavaStringArrayToStringVector(env, j_notification_ids, &id_strings);
+  std::vector<std::string> origin_strings;
+  AppendJavaStringArrayToStringVector(env, j_origins, &origin_strings);
+  std::vector<blink::NotificationResources> resources =
+      ParseResources(env, j_resources);
+
+  DCHECK(id_strings.size() == origin_strings.size());
+  DCHECK(id_strings.size() == resources.size());
+
+  // Group resources by context.
+  std::map<PlatformNotificationContext*, std::vector<NotificationResourceData>>
+      resources_by_context;
+  for (size_t i = 0; i < id_strings.size(); ++i) {
+    GURL origin(std::move(origin_strings[i]));
+    if (!origin.is_valid() || !origin.SchemeIsHTTPOrHTTPS())
+      continue;
+    resources_by_context[GetContext(profile, origin)].emplace_back(
+        NotificationResourceData{std::move(id_strings[i]), std::move(origin),
+                                 std::move(resources[i])});
+  }
+
+  // Store resources in each context.
+  for (auto& entry : resources_by_context) {
+    entry.first->WriteNotificationResources(std::move(entry.second),
+                                            base::DoNothing());
+  }
+}
+
+// ReDisplays all notifications with stored resources for all |j_origins|.
+static void JNI_NotificationSuspender_ReDisplayNotifications(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& j_profile,
+    const JavaParamRef<jobjectArray>& j_origins) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile);
+  DCHECK(profile);
+
+  std::vector<std::string> origin_strings;
+  AppendJavaStringArrayToStringVector(env, j_origins, &origin_strings);
+
+  // Group origins by context.
+  std::map<PlatformNotificationContext*, std::vector<GURL>> origins_by_context;
+  for (std::string& origin_string : origin_strings) {
+    GURL origin(std::move(origin_string));
+    if (!origin.is_valid() || !origin.SchemeIsHTTPOrHTTPS())
+      continue;
+    origins_by_context[GetContext(profile, origin)].emplace_back(
+        std::move(origin));
+  }
+
+  // ReDisplay notifications from each context.
+  for (auto& entry : origins_by_context) {
+    entry.first->ReDisplayNotifications(std::move(entry.second),
+                                        base::DoNothing());
+  }
+}
+
+}  // namespace usage_stats
diff --git a/chrome/browser/apps/app_shim/extension_app_shim_handler_mac_unittest.cc b/chrome/browser/apps/app_shim/extension_app_shim_handler_mac_unittest.cc
index 6f1ec04..94c39470 100644
--- a/chrome/browser/apps/app_shim/extension_app_shim_handler_mac_unittest.cc
+++ b/chrome/browser/apps/app_shim/extension_app_shim_handler_mac_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
 #include "chrome/browser/apps/app_shim/app_shim_host_bootstrap_mac.h"
 #include "chrome/browser/apps/app_shim/app_shim_host_mac.h"
@@ -26,18 +27,12 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-// Templates for calling base::OnceCallback from gmock actions.
-ACTION_TEMPLATE(RunOnceCallback,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_0_VALUE_PARAMS()) {
-  return std::move(std::get<k>(args)).Run();
-}
-
 namespace apps {
 
 using extensions::Extension;
 typedef extensions::AppWindowRegistry::AppWindowList AppWindowList;
 
+using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::Invoke;
 using ::testing::Return;
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 04af14a6..fb1a56c8 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -402,7 +402,7 @@
 
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type) override {
+                                  blink::mojom::MediaStreamType type) override {
     checked_ = true;
     if (check_message_loop_runner_.get())
       check_message_loop_runner_->Quit();
diff --git a/chrome/browser/autofill/manual_filling_controller.h b/chrome/browser/autofill/manual_filling_controller.h
index 120fbf6..f279ae3 100644
--- a/chrome/browser/autofill/manual_filling_controller.h
+++ b/chrome/browser/autofill/manual_filling_controller.h
@@ -74,7 +74,8 @@
   virtual void ShowWhenKeyboardIsVisible(FillingSource source) = 0;
 
   // Requests to show the touch to fill sheet.
-  virtual void ShowTouchToFillSheet() = 0;
+  virtual void ShowTouchToFillSheet(
+      const autofill::AccessorySheetData& data) = 0;
 
   // Requests to hide the accessory. This hides both the accessory sheet
   // (if open) and the accessory bar.
diff --git a/chrome/browser/autofill/manual_filling_controller_impl.cc b/chrome/browser/autofill/manual_filling_controller_impl.cc
index ce8690a..78021f9 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl.cc
+++ b/chrome/browser/autofill/manual_filling_controller_impl.cc
@@ -12,14 +12,15 @@
 #include "chrome/browser/password_manager/password_accessory_controller.h"
 #include "chrome/browser/password_manager/password_accessory_metrics_util.h"
 #include "chrome/browser/password_manager/password_generation_controller.h"
+#include "chrome/browser/password_manager/touch_to_fill_controller.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_util.h"
-#include "components/password_manager/core/common/password_manager_features.h"
 #include "content/public/browser/web_contents.h"
 
 using autofill::AccessoryAction;
 using autofill::AccessorySheetData;
+using autofill::AccessoryTabType;
 using autofill::AddressAccessoryController;
 using autofill::mojom::FocusedFieldType;
 
@@ -81,8 +82,7 @@
   view_->OnItemsAvailable(accessory_sheet_data);
 
   // TODO(crbug.com/965478): Refresh visibility for non-PWDs in V2.
-  if (accessory_sheet_data.get_sheet_type() !=
-      autofill::AccessoryTabType::PASSWORDS)
+  if (accessory_sheet_data.get_sheet_type() != AccessoryTabType::PASSWORDS)
     return;
 
   // TODO(crbug.com/905669): The decision for showing the sheet or not will need
@@ -105,12 +105,9 @@
   view_->ShowWhenKeyboardIsVisible();
 }
 
-void ManualFillingControllerImpl::ShowTouchToFillSheet() {
-  if (!base::FeatureList::IsEnabled(
-          password_manager::features::kTouchToFillAndroid)) {
-    return;
-  }
-
+void ManualFillingControllerImpl::ShowTouchToFillSheet(
+    const AccessorySheetData& data) {
+  view_->OnItemsAvailable(data);
   view_->ShowTouchToFillSheet();
 }
 
@@ -127,7 +124,7 @@
 }
 
 void ManualFillingControllerImpl::OnFillingTriggered(
-    autofill::AccessoryTabType type,
+    AccessoryTabType type,
     const autofill::UserInfo::Field& selection) {
   GetControllerForTab(type)->OnFillingTriggered(selection);
 }
@@ -174,9 +171,7 @@
 
 ManualFillingControllerImpl::ManualFillingControllerImpl(
     content::WebContents* web_contents)
-    : web_contents_(web_contents),
-      view_(ManualFillingViewInterface::Create(this)),
-      weak_factory_(this) {
+    : web_contents_(web_contents) {
   if (PasswordAccessoryController::AllowedForWebContents(web_contents)) {
     pwd_controller_ =
         PasswordAccessoryController::GetOrCreate(web_contents)->AsWeakPtr();
@@ -187,6 +182,11 @@
         AddressAccessoryController::GetOrCreate(web_contents)->AsWeakPtr();
     DCHECK(address_controller_);
   }
+  if (TouchToFillController::AllowedForWebContents(web_contents)) {
+    touch_to_fill_controller_ =
+        TouchToFillController::GetOrCreate(web_contents)->AsWeakPtr();
+    DCHECK(touch_to_fill_controller_);
+  }
 }
 
 ManualFillingControllerImpl::ManualFillingControllerImpl(
@@ -200,20 +200,21 @@
       address_controller_(std::move(address_controller)),
       pwd_generation_controller_for_testing_(
           pwd_generation_controller_for_testing),
-      view_(std::move(view)),
-      weak_factory_(this) {}
+      view_(std::move(view)) {}
 
 AccessoryController* ManualFillingControllerImpl::GetControllerForTab(
-    autofill::AccessoryTabType type) {
+    AccessoryTabType type) {
   switch (type) {
-    case autofill::AccessoryTabType::ADDRESSES:
+    case AccessoryTabType::ADDRESSES:
       return address_controller_.get();
-    case autofill::AccessoryTabType::PASSWORDS:
+    case AccessoryTabType::PASSWORDS:
       return pwd_controller_.get();
-    case autofill::AccessoryTabType::CREDIT_CARDS:
+    case AccessoryTabType::CREDIT_CARDS:
       // TODO(crbug.com/902425): return credit card controller.
-    case autofill::AccessoryTabType::ALL:
-    case autofill::AccessoryTabType::COUNT:
+    case AccessoryTabType::TOUCH_TO_FILL:
+      return touch_to_fill_controller_.get();
+    case AccessoryTabType::ALL:
+    case AccessoryTabType::COUNT:
       break;  // Intentional failure.
   }
   NOTREACHED() << "Controller not defined for tab: " << static_cast<int>(type);
diff --git a/chrome/browser/autofill/manual_filling_controller_impl.h b/chrome/browser/autofill/manual_filling_controller_impl.h
index 0df241d..27182a8e 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl.h
+++ b/chrome/browser/autofill/manual_filling_controller_impl.h
@@ -20,6 +20,7 @@
 class AccessoryController;
 class PasswordAccessoryController;
 class PasswordGenerationController;
+class TouchToFillController;
 
 // Use ManualFillingController::GetOrCreate to obtain instances of this class.
 class ManualFillingControllerImpl
@@ -34,7 +35,7 @@
       const autofill::AccessorySheetData& accessory_sheet_data) override;
   void OnFilledIntoFocusedField(autofill::FillingStatus status) override;
   void ShowWhenKeyboardIsVisible(FillingSource source) override;
-  void ShowTouchToFillSheet() override;
+  void ShowTouchToFillSheet(const autofill::AccessorySheetData& data) override;
   void Hide(FillingSource source) override;
   void OnAutomaticGenerationStatusChanged(bool available) override;
   void OnFillingTriggered(autofill::AccessoryTabType type,
@@ -111,6 +112,9 @@
   // The address accessory controller object to forward view requests to.
   base::WeakPtr<autofill::AddressAccessoryController> address_controller_;
 
+  // The touch to fill controller object to forward view requests to.
+  base::WeakPtr<TouchToFillController> touch_to_fill_controller_;
+
   // A password generation controller used in tests which receives requests
   // from the view.
   PasswordGenerationController* pwd_generation_controller_for_testing_ =
@@ -119,9 +123,10 @@
   // Hold the native instance of the view. Must be last declared and initialized
   // member so the view can be created in the constructor with a fully set up
   // controller instance.
-  std::unique_ptr<ManualFillingViewInterface> view_;
+  std::unique_ptr<ManualFillingViewInterface> view_ =
+      ManualFillingViewInterface::Create(this);
 
-  base::WeakPtrFactory<ManualFillingControllerImpl> weak_factory_;
+  base::WeakPtrFactory<ManualFillingControllerImpl> weak_factory_{this};
 
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 
diff --git a/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc b/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
index d2be9c6..81ba628 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
+++ b/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
@@ -189,18 +189,10 @@
   controller()->Hide(FillingSource::PASSWORD_FALLBACKS);
 }
 
-TEST_F(ManualFillingControllerTest, RelaysTouchToFillSheetDependingOnFlag) {
-  for (bool is_touch_to_fill_enabled : {false, true}) {
-    SCOPED_TRACE(is_touch_to_fill_enabled);
-    base::test::ScopedFeatureList scoped_feature_list;
-    scoped_feature_list.InitWithFeatureState(
-        password_manager::features::kTouchToFillAndroid,
-        is_touch_to_fill_enabled);
-
-    EXPECT_CALL(*view(), ShowTouchToFillSheet())
-        .Times(is_touch_to_fill_enabled ? 1 : 0);
-    controller()->ShowTouchToFillSheet();
-  }
+TEST_F(ManualFillingControllerTest, RelaysShowTouchToFillSheet) {
+  EXPECT_CALL(*view(), OnItemsAvailable(dummy_accessory_sheet_data()));
+  EXPECT_CALL(*view(), ShowTouchToFillSheet);
+  controller()->ShowTouchToFillSheet(dummy_accessory_sheet_data());
 }
 
 TEST_F(ManualFillingControllerTest, HidesAccessoryWhenAllSourcesRequestedIt) {
diff --git a/chrome/browser/autofill/mock_manual_filling_controller.h b/chrome/browser/autofill/mock_manual_filling_controller.h
index 1bb82dc..9cd774f 100644
--- a/chrome/browser/autofill/mock_manual_filling_controller.h
+++ b/chrome/browser/autofill/mock_manual_filling_controller.h
@@ -22,7 +22,7 @@
                     const autofill::AccessorySheetData&));
   MOCK_METHOD1(ShowWhenKeyboardIsVisible,
                void(ManualFillingController::FillingSource));
-  MOCK_METHOD0(ShowTouchToFillSheet, void());
+  MOCK_METHOD1(ShowTouchToFillSheet, void(const autofill::AccessorySheetData&));
   MOCK_METHOD1(Hide, void(ManualFillingController::FillingSource));
   MOCK_METHOD2(GetFavicon,
                void(int, base::OnceCallback<void(const gfx::Image&)>));
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
index 4b6a77a..6db7804f 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
@@ -209,12 +209,12 @@
 
   void TreeNodesAdded(ui::TreeModel* model,
                       ui::TreeModelNode* parent,
-                      int start,
-                      int count) override {}
+                      size_t start,
+                      size_t count) override {}
   void TreeNodesRemoved(ui::TreeModel* model,
                         ui::TreeModelNode* parent,
-                        int start,
-                        int count) override {}
+                        size_t start,
+                        size_t count) override {}
   void TreeNodeChanged(ui::TreeModel* model, ui::TreeModelNode* node) override {
   }
 
diff --git a/chrome/browser/browsing_data/cookies_tree_model.cc b/chrome/browser/browsing_data/cookies_tree_model.cc
index b09b2f2..35636ff 100644
--- a/chrome/browser/browsing_data/cookies_tree_model.cc
+++ b/chrome/browser/browsing_data/cookies_tree_model.cc
@@ -330,7 +330,8 @@
   DCHECK(new_child);
   auto iter = std::lower_bound(children().begin(), children().end(), new_child,
                                NodeTitleComparator());
-  GetModel()->Add(this, std::move(new_child), iter - children().begin());
+  GetModel()->Add(this, std::move(new_child),
+                  size_t{iter - children().begin()});
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -847,8 +848,9 @@
   }
   // Node doesn't exist, insert the new one into the (ordered) children.
   DCHECK(model_);
-  return static_cast<CookieTreeHostNode*>(model_->Add(
-      this, std::move(host_node), (host_node_iterator - children().begin())));
+  return static_cast<CookieTreeHostNode*>(
+      model_->Add(this, std::move(host_node),
+                  size_t{host_node_iterator - children().begin()}));
 }
 
 CookiesTreeModel* CookieTreeRootNode::GetModel() const {
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 8287f19..9ae816ff 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -90,7 +90,6 @@
 #include "chrome/browser/platform_util.h"
 #include "chrome/browser/plugins/pdf_iframe_navigation_throttle.h"
 #include "chrome/browser/plugins/plugin_utils.h"
-#include "chrome/browser/policy/cloud/policy_header_navigation_throttle.h"
 #include "chrome/browser/predictors/loading_predictor.h"
 #include "chrome/browser/predictors/loading_predictor_factory.h"
 #include "chrome/browser/prerender/prerender_final_status.h"
@@ -227,7 +226,6 @@
 #include "components/payments/content/payment_request_display_manager.h"
 #include "components/policy/content/policy_blacklist_navigation_throttle.h"
 #include "components/policy/content/policy_blacklist_service.h"
-#include "components/policy/core/common/cloud/policy_header_service.h"
 #include "components/policy/core/common/policy_service.h"
 #include "components/policy/policy_constants.h"
 #include "components/pref_registry/pref_registry_syncable.h"
@@ -4365,9 +4363,6 @@
     throttles.push_back(std::move(browser_switcher_throttle));
 #endif
 
-  throttles.push_back(
-      std::make_unique<policy::PolicyHeaderNavigationThrottle>(handle));
-
   return throttles;
 }
 
diff --git a/chrome/browser/chrome_content_browser_client_browsertest.cc b/chrome/browser/chrome_content_browser_client_browsertest.cc
index 0603924..ad00d3a8 100644
--- a/chrome/browser/chrome_content_browser_client_browsertest.cc
+++ b/chrome/browser/chrome_content_browser_client_browsertest.cc
@@ -16,7 +16,6 @@
 #include "build/build_config.h"
 #include "chrome/browser/custom_handlers/protocol_handler_registry.h"
 #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
-#include "chrome/browser/policy/cloud/policy_header_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/instant_service.h"
 #include "chrome/browser/search/instant_service_factory.h"
@@ -32,7 +31,6 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/network_session_configurator/common/network_switches.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
-#include "components/policy/core/common/cloud/policy_header_service.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
@@ -58,9 +56,6 @@
 
 namespace {
 
-const char kTestPolicyHeader[] = "test_header";
-const char kServerRedirectUrl[] = "/server-redirect";
-
 enum class NetworkServiceState {
   kDisabled,
   kEnabled,
@@ -479,106 +474,6 @@
   }
 }
 
-class PolicyHeaderServiceBrowserTest : public InProcessBrowserTest {
- public:
-  PolicyHeaderServiceBrowserTest() = default;
-
-  void SetUpOnMainThread() override {
-    embedded_test_server()->RegisterRequestHandler(
-        base::BindRepeating(&PolicyHeaderServiceBrowserTest::HandleTestRequest,
-                            base::Unretained(this)));
-    ASSERT_TRUE(embedded_test_server()->Start());
-
-    // Forge a dummy DMServer URL.
-    dm_url_ = embedded_test_server()->GetURL("/DeviceManagement");
-
-    // At this point, the Profile is already initialized and it's too
-    // late to set the DMServer URL via command line flags, so directly
-    // inject it to the PolicyHeaderService.
-    policy::PolicyHeaderService* policy_header_service =
-        policy::PolicyHeaderServiceFactory::GetForBrowserContext(
-            browser()->profile());
-    policy_header_service->SetServerURLForTest(dm_url_.spec());
-    policy_header_service->SetHeaderForTest(kTestPolicyHeader);
-  }
-
-  std::unique_ptr<net::test_server::HttpResponse> HandleTestRequest(
-      const net::test_server::HttpRequest& request) {
-    last_request_headers_ = request.headers;
-
-    if (base::StartsWith(request.relative_url, kServerRedirectUrl,
-                         base::CompareCase::SENSITIVE)) {
-      // Extract the target URL and redirect there.
-      size_t query_string_pos = request.relative_url.find('?');
-      std::string redirect_target =
-          request.relative_url.substr(query_string_pos + 1);
-
-      std::unique_ptr<net::test_server::BasicHttpResponse> http_response(
-          new net::test_server::BasicHttpResponse);
-      http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
-      http_response->AddCustomHeader("Location", redirect_target);
-      return std::move(http_response);
-    } else if (request.relative_url == "/") {
-      std::unique_ptr<net::test_server::BasicHttpResponse> http_response(
-          new net::test_server::BasicHttpResponse);
-      http_response->set_code(net::HTTP_OK);
-      http_response->set_content("Success");
-      return std::move(http_response);
-    }
-    return nullptr;
-  }
-
-  const GURL& dm_url() const { return dm_url_; }
-
-  const net::test_server::HttpRequest::HeaderMap& last_request_headers() const {
-    return last_request_headers_;
-  }
-
- private:
-  // The dummy URL for DMServer.
-  GURL dm_url_;
-
-  // List of request headers received by the embedded server.
-  net::test_server::HttpRequest::HeaderMap last_request_headers_;
-
-  DISALLOW_COPY_AND_ASSIGN(PolicyHeaderServiceBrowserTest);
-};
-
-IN_PROC_BROWSER_TEST_F(PolicyHeaderServiceBrowserTest, NoPolicyHeader) {
-  // When fetching non-DMServer URLs, we should not add a policy header to the
-  // request.
-  DCHECK(!embedded_test_server()->base_url().spec().empty());
-  ui_test_utils::NavigateToURL(browser(), embedded_test_server()->base_url());
-  auto iter = last_request_headers().find(policy::kChromePolicyHeader);
-  EXPECT_EQ(iter, last_request_headers().end());
-}
-
-IN_PROC_BROWSER_TEST_F(PolicyHeaderServiceBrowserTest, PolicyHeader) {
-  // When fetching a DMServer URL, we should add a policy header to the
-  // request.
-  ui_test_utils::NavigateToURL(browser(), dm_url());
-
-  auto iter = last_request_headers().find(policy::kChromePolicyHeader);
-  ASSERT_NE(iter, last_request_headers().end());
-  EXPECT_EQ(iter->second, kTestPolicyHeader);
-}
-
-IN_PROC_BROWSER_TEST_F(PolicyHeaderServiceBrowserTest,
-                       PolicyHeaderForRedirect) {
-  // Build up a URL that results in a redirect to the DMServer URL to make
-  // sure the policy header is still added.
-  std::string redirect_url;
-  redirect_url += kServerRedirectUrl;
-  redirect_url += "?";
-  redirect_url += dm_url().spec();
-  ui_test_utils::NavigateToURL(browser(),
-                               embedded_test_server()->GetURL(redirect_url));
-
-  auto iter = last_request_headers().find(policy::kChromePolicyHeader);
-  ASSERT_NE(iter, last_request_headers().end());
-  EXPECT_EQ(iter->second, kTestPolicyHeader);
-}
-
 // Helper class to test window creation from NTP.
 class OpenWindowFromNTPBrowserTest : public InProcessBrowserTest,
                                      public InstantTestBase {
diff --git a/chrome/browser/chromeos/apps/apk_web_app_service.cc b/chrome/browser/chromeos/apps/apk_web_app_service.cc
index ba4cf69f..37d4fd1 100644
--- a/chrome/browser/chromeos/apps/apk_web_app_service.cc
+++ b/chrome/browser/chromeos/apps/apk_web_app_service.cc
@@ -7,12 +7,14 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/chromeos/apps/apk_web_app_service_factory.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/components/externally_installed_web_app_prefs.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
+#include "chrome/common/chrome_features.h"
 #include "components/arc/common/app.mojom.h"
 #include "components/arc/session/connection_holder.h"
 #include "components/pref_registry/pref_registry_syncable.h"
@@ -124,6 +126,9 @@
 
 void ApkWebAppService::OnPackageInstalled(
     const arc::mojom::ArcPackageInfo& package_info) {
+  if (!base::FeatureList::IsEnabled(features::kApkWebAppInstalls))
+    return;
+
   // This method is called when a) new packages are installed, and b) existing
   // packages are updated. In (b), there are two cases to handle: the package
   // could previously have been an Android app and has now become a web app, and
@@ -192,6 +197,9 @@
   // will trigger the uninstallation of the web app. Similarly, this method
   // removes the associated web_app_id before triggering uninstallation, so
   // OnExtensionUninstalled() will do nothing.
+  if (!base::FeatureList::IsEnabled(features::kApkWebAppInstalls))
+    return;
+
   DictionaryPrefUpdate web_apps_to_apks(profile_->GetPrefs(),
                                         kWebAppToApkDictPref);
 
@@ -217,6 +225,9 @@
 }
 
 void ApkWebAppService::OnPackageListInitialRefreshed() {
+  if (!base::FeatureList::IsEnabled(features::kApkWebAppInstalls))
+    return;
+
   // Scan through the list of apps to see if any were uninstalled while ARC
   // wasn't running.
   DictionaryPrefUpdate web_apps_to_apks(profile_->GetPrefs(),
@@ -259,6 +270,9 @@
     content::BrowserContext* browser_context,
     const extensions::Extension* extension,
     extensions::UninstallReason reason) {
+  if (!base::FeatureList::IsEnabled(features::kApkWebAppInstalls))
+    return;
+
   DictionaryPrefUpdate web_apps_to_apks(profile_->GetPrefs(),
                                         kWebAppToApkDictPref);
 
diff --git a/chrome/browser/chromeos/arc/intent_helper/arc_settings_service_browsertest.cc b/chrome/browser/chromeos/arc/intent_helper/arc_settings_service_browsertest.cc
index 4b89010..211cdb1 100644
--- a/chrome/browser/chromeos/arc/intent_helper/arc_settings_service_browsertest.cc
+++ b/chrome/browser/chromeos/arc/intent_helper/arc_settings_service_browsertest.cc
@@ -592,17 +592,25 @@
             1);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcSettingsServiceTest, ProxyBypassListTest) {
+// Chrome and ARC use different delimiters for the string representation of the
+// proxy bypass list. This test verifies that the string bypass list sent by
+// Chrome to ARC is formatted in a way that Android code understands.
+IN_PROC_BROWSER_TEST_F(ArcSettingsServiceTest,
+                       ProxyBypassListStringRepresentationTest) {
   fake_intent_helper_instance_->clear_broadcasts();
 
-  const char kChromeProxyBypassList[] = "test1.org;test2.org;";
+  net::ProxyBypassRules chrome_proxy_bypass_rules;
+  chrome_proxy_bypass_rules.AddRuleForHostname("", "test1.org", -1);
+  chrome_proxy_bypass_rules.AddRuleForHostname("", "test2.org", -1);
+
   const char kArcProxyBypassList[] = "test1.org,test2.org";
 
   base::Value proxy_config(base::Value::Type::DICTIONARY);
   proxy_config.SetKey("mode",
                       base::Value(ProxyPrefs::kFixedServersProxyModeName));
   proxy_config.SetKey("server", base::Value("proxy:8080"));
-  proxy_config.SetKey("bypass_list", base::Value(kChromeProxyBypassList));
+  proxy_config.SetKey("bypass_list",
+                      base::Value(chrome_proxy_bypass_rules.ToString()));
   SetProxyConfigForNetworkService(kDefaultServicePath, std::move(proxy_config));
   RunUntilIdle();
 
diff --git a/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc b/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc
index 09ed48e6..3468fc7 100644
--- a/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc
+++ b/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc
@@ -466,6 +466,24 @@
     observer.OnCloudDpsFailed(time, package_name, reason);
 }
 
+void ArcPolicyBridge::ReportDirectInstall(
+    base::Time time,
+    const std::vector<std::string>& package_names) {
+  const std::set<std::string> packages_set(package_names.begin(),
+                                           package_names.end());
+  for (Observer& observer : observers_)
+    observer.OnReportDirectInstall(time, packages_set);
+}
+
+void ArcPolicyBridge::ReportForceInstallMainLoopFailed(
+    base::Time time,
+    const std::vector<std::string>& package_names) {
+  const std::set<std::string> packages_set(package_names.begin(),
+                                           package_names.end());
+  for (Observer& observer : observers_)
+    observer.OnReportForceInstallMainLoopFailed(time, packages_set);
+}
+
 void ArcPolicyBridge::OnPolicyUpdated(const policy::PolicyNamespace& ns,
                                       const policy::PolicyMap& previous,
                                       const policy::PolicyMap& current) {
diff --git a/chrome/browser/chromeos/arc/policy/arc_policy_bridge.h b/chrome/browser/chromeos/arc/policy/arc_policy_bridge.h
index 94bf0e8c..6ecdbd5 100644
--- a/chrome/browser/chromeos/arc/policy/arc_policy_bridge.h
+++ b/chrome/browser/chromeos/arc/policy/arc_policy_bridge.h
@@ -80,6 +80,18 @@
                                   const std::string& package_name,
                                   mojom::InstallErrorReason reason) {}
 
+    // Called when CloudDPC scheduled direct install with Play Store for
+    // a set of packages.
+    virtual void OnReportDirectInstall(
+        base::Time time,
+        const std::set<std::string>& package_names) {}
+
+    // Called when in CloudDPC the main loop of retries to install apps failed
+    // to install some apps.
+    virtual void OnReportForceInstallMainLoopFailed(
+        base::Time time,
+        const std::set<std::string>& package_names) {}
+
    protected:
     Observer() = default;
     virtual ~Observer() = default;
@@ -128,6 +140,12 @@
   void ReportCloudDpsFailed(base::Time time,
                             const std::string& package_name,
                             mojom::InstallErrorReason reason) override;
+  void ReportDirectInstall(
+      base::Time time,
+      const std::vector<std::string>& package_names) override;
+  void ReportForceInstallMainLoopFailed(
+      base::Time time,
+      const std::vector<std::string>& package_names) override;
 
   // PolicyService::Observer overrides.
   void OnPolicyUpdated(const policy::PolicyNamespace& ns,
diff --git a/chrome/browser/chromeos/child_accounts/screen_time_controller.cc b/chrome/browser/chromeos/child_accounts/screen_time_controller.cc
index 8b088f1..ccc1f7e 100644
--- a/chrome/browser/chromeos/child_accounts/screen_time_controller.cc
+++ b/chrome/browser/chromeos/child_accounts/screen_time_controller.cc
@@ -20,7 +20,7 @@
 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/ash/media_client.h"
+#include "chrome/browser/ui/ash/media_client_impl.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
@@ -289,7 +289,7 @@
     ash::LoginScreen::Get()->ShowParentAccessButton(true);
 
   // Prevent media from continuing to play after device is locked.
-  MediaClient::Get()->SuspendMediaSessions();
+  MediaClientImpl::Get()->SuspendMediaSessions();
 }
 
 void ScreenTimeController::OnScreenLockByPolicyEnd() {
diff --git a/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part_browsertest.cc b/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part_browsertest.cc
index 7509cd14..6db4fc8 100644
--- a/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part_browsertest.cc
+++ b/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part_browsertest.cc
@@ -6,6 +6,8 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
+#include "chrome/browser/web_applications/system_web_app_manager.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chromeos/constants/chromeos_features.h"
@@ -17,6 +19,11 @@
 
 IN_PROC_BROWSER_TEST_F(ChromeContentBrowserClientChromeOsPartTest,
                        SettingsWindowFontSize) {
+  // Install the Settings App.
+  web_app::WebAppProvider::Get(browser()->profile())
+      ->system_web_app_manager()
+      .InstallSystemAppsForTesting();
+
   const content::WebPreferences kDefaultPrefs;
   const int kDefaultFontSize = kDefaultPrefs.default_font_size;
   const int kDefaultFixedFontSize = kDefaultPrefs.default_fixed_font_size;
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
index 691e6bd..89eb209d 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/feature_list.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/chromeos/file_manager/file_manager_string_util.h"
@@ -17,6 +18,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/system/statistics_provider.h"
+#include "components/arc/arc_features.h"
 #include "extensions/common/extension_l10n_util.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -46,6 +48,8 @@
   // TODO(crbug.com/868747): Find a better solution for demo mode.
   dict->SetBoolean("HIDE_SPACE_INFO",
                    chromeos::DemoSession::IsDeviceInDemoMode());
+  dict->SetBoolean("ARC_USB_STORAGE_UI_ENABLED",
+                   base::FeatureList::IsEnabled(arc::kUsbStorageUIFeature));
   dict->SetBoolean("CROSTINI_ENABLED",
                    crostini::IsCrostiniEnabled(
                        Profile::FromBrowserContext(browser_context())));
diff --git a/chrome/browser/chromeos/extensions/login_screen_ui/OWNERS b/chrome/browser/chromeos/extensions/login_screen_ui/OWNERS
new file mode 100644
index 0000000..a402ba0
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/login_screen_ui/OWNERS
@@ -0,0 +1,2 @@
+hendrich@chromium.org
+voit@google.com
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 5062f846..3f9936d 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -341,7 +341,9 @@
     OpenSniffedFiles, /* open_sniffed_files.js */
     FilesAppBrowserTest,
     ::testing::Values(TestCase("pdfOpenDownloads"),
-                      TestCase("pdfOpenDrive").EnableDriveFs()));
+                      TestCase("pdfOpenDrive").EnableDriveFs(),
+                      TestCase("textOpenDownloads"),
+                      TestCase("textOpenDrive").EnableDriveFs()));
 
 // NaCl fails to compile zip plugin.pexe too often on ASAN, crbug.com/867738
 // The tests are flaky on the debug bot and always time out first and then pass
@@ -520,6 +522,7 @@
 #endif
         TestCase("openQuickViewKeyboardUpDownChangesView"),
         TestCase("openQuickViewKeyboardLeftRightChangesView"),
+        TestCase("openQuickViewSniffedText"),
         TestCase("openQuickViewScrollText"),
         TestCase("openQuickViewScrollHtml"),
         TestCase("openQuickViewBackgroundColorText"),
@@ -897,7 +900,9 @@
                       TestCase("showGridViewDownloads").InGuestMode(),
                       TestCase("showGridViewDrive").DisableDriveFs(),
                       TestCase("showGridViewDrive").EnableDriveFs(),
-                      TestCase("showGridViewButtonSwitches")));
+                      TestCase("showGridViewButtonSwitches"),
+                      TestCase("showGridViewKeyboardSelectionA11y"),
+                      TestCase("showGridViewMouseSelectionA11y")));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     Providers, /* providers.js */
@@ -943,7 +948,9 @@
     FilesAppBrowserTest,
     ::testing::Values(TestCase("fileListAriaAttributes"),
                       TestCase("fileListFocusFirstItem"),
-                      TestCase("fileListSelectLastFocusedItem")));
+                      TestCase("fileListSelectLastFocusedItem"),
+                      TestCase("fileListKeyboardSelectionA11y"),
+                      TestCase("fileListMouseSelectionA11y")));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     Crostini, /* crostini.js */
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index 707b424..a5319789 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -1559,6 +1559,8 @@
   if (IsArcTest()) {
     arc::SetArcAvailableCommandLineForTesting(command_line);
   }
+  // Make sure to run the ARC storage UI toast tests.
+  enabled_features.emplace_back(arc::kUsbStorageUIFeature);
 
   if (IsDocumentsProviderTest()) {
     enabled_features.emplace_back(
diff --git a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
index 768f2e8..33f3b50 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
@@ -777,6 +777,14 @@
              IDS_FILE_BROWSER_COLUMN_DESC_SORT_MESSAGE);
   SET_STRING("COLUMN_SORTED_ASC", IDS_FILE_BROWSER_COLUMN_SORTED_ASC_MESSAGE);
   SET_STRING("COLUMN_SORTED_DESC", IDS_FILE_BROWSER_COLUMN_SORTED_DESC_MESSAGE);
+  SET_STRING("SELECTION_ADD_SINGLE_ENTRY",
+             IDS_FILE_BROWSER_SELECTION_ADD_SINGLE_ENTRY);
+  SET_STRING("SELECTION_REMOVE_SINGLE_ENTRY",
+             IDS_FILE_BROWSER_SELECTION_REMOVE_SINGLE_ENTRY);
+  SET_STRING("SELECTION_SINGLE_ENTRY", IDS_FILE_BROWSER_SELECTION_SINGLE_ENTRY);
+  SET_STRING("SELECTION_ADD_RANGE", IDS_FILE_BROWSER_SELECTION_ADD_RANGE);
+  SET_STRING("SELECTION_CANCELLATION", IDS_FILE_BROWSER_SELECTION_CANCELLATION);
+  SET_STRING("SELECTION_ALL_ENTRIES", IDS_FILE_BROWSER_SELECTION_ALL_ENTRIES);
   SET_STRING("SIZE_GB", IDS_FILE_BROWSER_SIZE_GB);
   SET_STRING("SIZE_KB", IDS_FILE_BROWSER_SIZE_KB);
   SET_STRING("SIZE_MB", IDS_FILE_BROWSER_SIZE_MB);
diff --git a/chrome/browser/chromeos/kiosk_next_home/app_controller_service.cc b/chrome/browser/chromeos/kiosk_next_home/app_controller_service.cc
index 5a44c492..03825da85 100644
--- a/chrome/browser/chromeos/kiosk_next_home/app_controller_service.cc
+++ b/chrome/browser/chromeos/kiosk_next_home/app_controller_service.cc
@@ -67,6 +67,9 @@
   DCHECK(profile);
   Observe(&app_service_proxy_->AppRegistryCache());
 
+  // ArcServiceManager is always set in production code.
+  arc::ArcServiceManager::Get()->arc_bridge_service()->app()->AddObserver(this);
+
   // Add the chrome://app-icon URL data source.
   // TODO(ltenorio): Move this to a more suitable location when we change
   // the Kiosk Next Home to WebUI.
@@ -74,7 +77,10 @@
                               std::make_unique<apps::AppIconSource>(profile));
 }
 
-AppControllerService::~AppControllerService() = default;
+AppControllerService::~AppControllerService() {
+  arc::ArcServiceManager::Get()->arc_bridge_service()->app()->RemoveObserver(
+      this);
+}
 
 void AppControllerService::BindRequest(mojom::AppControllerRequest request) {
   bindings_.AddBinding(this, std::move(request));
@@ -99,6 +105,8 @@
 
 void AppControllerService::SetClient(mojom::AppControllerClientPtr client) {
   client_ = std::move(client);
+
+  client_->OnArcStatusChanged(arc_status_);
 }
 
 void AppControllerService::LaunchApp(const std::string& app_id) {
@@ -174,6 +182,18 @@
   Observe(nullptr);
 }
 
+void AppControllerService::OnConnectionReady() {
+  arc_status_ = mojom::ArcStatus::kReady;
+  if (client_)
+    client_->OnArcStatusChanged(arc_status_);
+}
+
+void AppControllerService::OnConnectionClosed() {
+  arc_status_ = mojom::ArcStatus::kStopped;
+  if (client_)
+    client_->OnArcStatusChanged(arc_status_);
+}
+
 void AppControllerService::SetIntentConfigHelperForTesting(
     std::unique_ptr<IntentConfigHelper> helper) {
   intent_config_helper_ = std::move(helper);
diff --git a/chrome/browser/chromeos/kiosk_next_home/app_controller_service.h b/chrome/browser/chromeos/kiosk_next_home/app_controller_service.h
index eb65413..4ad42f3 100644
--- a/chrome/browser/chromeos/kiosk_next_home/app_controller_service.h
+++ b/chrome/browser/chromeos/kiosk_next_home/app_controller_service.h
@@ -12,6 +12,8 @@
 #include "base/macros.h"
 #include "chrome/browser/chromeos/kiosk_next_home/mojom/app_controller.mojom.h"
 #include "chrome/services/app_service/public/cpp/app_registry_cache.h"
+#include "components/arc/common/app.mojom.h"
+#include "components/arc/session/connection_observer.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 
@@ -34,9 +36,11 @@
 // Service implementation for the Kiosk Next AppController.
 // This class is responsible for managing Chrome OS apps and returning useful
 // information about them to the Kiosk Next Home.
-class AppControllerService : public mojom::AppController,
-                             public KeyedService,
-                             public apps::AppRegistryCache::Observer {
+class AppControllerService
+    : public mojom::AppController,
+      public KeyedService,
+      public apps::AppRegistryCache::Observer,
+      public arc::ConnectionObserver<arc::mojom::AppInstance> {
  public:
   // Returns the AppControllerService singleton attached to this |context|.
   static AppControllerService* Get(content::BrowserContext* context);
@@ -62,6 +66,10 @@
   void OnAppRegistryCacheWillBeDestroyed(
       apps::AppRegistryCache* cache) override;
 
+  // arc::ConnectionObserver:
+  void OnConnectionReady() override;
+  void OnConnectionClosed() override;
+
   // Allows overriding the intent config helper for tests.
   void SetIntentConfigHelperForTesting(
       std::unique_ptr<IntentConfigHelper> helper);
@@ -95,6 +103,9 @@
   // is necessary.
   std::map<std::string, std::string> android_package_map_;
 
+  // The current arc status that is reported to observers.
+  mojom::ArcStatus arc_status_ = mojom::ArcStatus::kStopped;
+
   DISALLOW_COPY_AND_ASSIGN(AppControllerService);
 };
 
diff --git a/chrome/browser/chromeos/kiosk_next_home/app_controller_service_unittest.cc b/chrome/browser/chromeos/kiosk_next_home/app_controller_service_unittest.cc
index 37c6be4..89502a75 100644
--- a/chrome/browser/chromeos/kiosk_next_home/app_controller_service_unittest.cc
+++ b/chrome/browser/chromeos/kiosk_next_home/app_controller_service_unittest.cc
@@ -53,15 +53,25 @@
       : binding_(this, std::move(request)) {}
 
   const std::vector<mojom::AppPtr>& app_updates() { return app_updates_; }
+  const std::vector<mojom::ArcStatus>& arc_status() { return arc_status_; }
 
   // mojom::AppControllerClient:
   void OnAppChanged(mojom::AppPtr app) override {
     app_updates_.push_back(std::move(app));
   }
 
+  void OnArcStatusChanged(mojom::ArcStatus status) override {
+    arc_status_.push_back(status);
+  }
+
+  // Flushes the internal mojo message pipe. Call this any time you trigger a
+  // new mojo method, so the test waits for the full communication to occur.
+  void Flush() { binding_.FlushForTesting(); }
+
  private:
   mojo::Binding<mojom::AppControllerClient> binding_;
   std::vector<mojom::AppPtr> app_updates_;
+  std::vector<mojom::ArcStatus> arc_status_;
 };
 
 // Mock instance for the AppServiceProxy. It only overrides a subset of the
@@ -101,21 +111,30 @@
     arc_test_.SetUp(profile());
     proxy_ = MockAppServiceProxy::OverrideRealProxyForProfile(profile());
 
-    app_controller_service_ = AppControllerService::Get(profile());
-
-    mojom::AppControllerClientPtr client_proxy;
-    client_ = std::make_unique<FakeAppControllerClient>(
-        mojo::MakeRequest(&client_proxy));
-    app_controller_service_->SetClient(std::move(client_proxy));
+    app_controller_service_ = std::make_unique<AppControllerService>(profile());
+    ResetClient();
   }
 
-  void TearDown() override { arc_test_.TearDown(); }
+  void TearDown() override {
+    // We need to mimic the destruction order from production code, first the
+    // AppControllerService, then arc.
+    app_controller_service_.reset();
+    arc_test_.TearDown();
+  }
 
   Profile* profile() { return profile_.get(); }
 
   MockAppServiceProxy* proxy() { return proxy_; }
 
-  AppControllerService* service() { return app_controller_service_; }
+  AppControllerService* service() { return app_controller_service_.get(); }
+
+  void ResetClient() {
+    mojom::AppControllerClientPtr client_proxy;
+    client_ = std::make_unique<FakeAppControllerClient>(
+        mojo::MakeRequest(&client_proxy));
+    app_controller_service_->SetClient(std::move(client_proxy));
+    client_->Flush();
+  }
 
   std::string GetAppIdFromAndroidPackage(const std::string& package) {
     return ArcAppListPrefs::GetAppId(package, kFakeActivity);
@@ -138,16 +157,22 @@
     arc_test_.app_instance()->set_android_id(android_id);
   }
 
-  void StopArc() { arc_test_.StopArcInstance(); }
+  void StopArc() {
+    arc_test_.StopArcInstance();
+    client_->Flush();
+  }
+
+  void RestartArc() {
+    arc_test_.RestartArcInstance();
+    client_->Flush();
+  }
 
   void AddAppDeltaToAppService(apps::mojom::AppPtr delta) {
     std::vector<apps::mojom::AppPtr> deltas;
     deltas.push_back(std::move(delta));
     proxy_->AppRegistryCache().OnApps(std::move(deltas));
 
-    // We just triggered some mojo calls by adding the above delta, we need to
-    // wait for them to finish before we continue with the test.
-    base::RunLoop().RunUntilIdle();
+    client_->Flush();
   }
 
   // Gets all apps from the AppControllerService instance being tested and
@@ -252,6 +277,15 @@
     }
   }
 
+  void ExpectArcStatusUpdates(
+      const std::vector<mojom::ArcStatus>& expected_updates) {
+    ASSERT_EQ(expected_updates.size(), client_->arc_status().size());
+
+    for (std::size_t i = 0; i < expected_updates.size(); ++i) {
+      EXPECT_EQ(expected_updates[i], client_->arc_status()[i]);
+    }
+  }
+
   void ExpectBridgeActionRecorded(BridgeAction action, int count) {
     // Since seeding apps may fire bridge actions, check only the bucket of
     // interest.
@@ -274,7 +308,7 @@
   std::unique_ptr<TestingProfile> profile_;
   ArcAppTest arc_test_;
   MockAppServiceProxy* proxy_ = nullptr;
-  AppControllerService* app_controller_service_ = nullptr;
+  std::unique_ptr<AppControllerService> app_controller_service_;
   std::unique_ptr<FakeAppControllerClient> client_;
   base::HistogramTester histogram_tester_;
 
@@ -715,6 +749,27 @@
   ExpectBridgeActionRecorded(BridgeAction::kNotifiedAppChange, 1);
 }
 
+TEST_F(AppControllerServiceTest, ClientIsNotifiedOfArcStatusWhenArcStops) {
+  ExpectArcStatusUpdates({mojom::ArcStatus::kReady});
+
+  StopArc();
+
+  ExpectArcStatusUpdates(
+      {mojom::ArcStatus::kReady, mojom::ArcStatus::kStopped});
+}
+
+TEST_F(AppControllerServiceTest, ClientIsNotifiedOfArcStatusWhenArcStarts) {
+  // Since our fake arc instance starts ready, we need to stop it first and
+  // reset the client.
+  StopArc();
+  ResetClient();
+  ExpectArcStatusUpdates({mojom::ArcStatus::kStopped});
+
+  RestartArc();
+  ExpectArcStatusUpdates(
+      {mojom::ArcStatus::kStopped, mojom::ArcStatus::kReady});
+}
+
 TEST_F(AppControllerServiceTest, LaunchAppCallsAppServiceCorrectly) {
   EXPECT_CALL(*proxy(), Launch("fake_app_id", ui::EventFlags::EF_NONE,
                                apps::mojom::LaunchSource::kFromKioskNextHome,
diff --git a/chrome/browser/chromeos/kiosk_next_home/kiosk_next_home_browsertest.cc b/chrome/browser/chromeos/kiosk_next_home/kiosk_next_home_browsertest.cc
index 5a89363f..703bf96 100644
--- a/chrome/browser/chromeos/kiosk_next_home/kiosk_next_home_browsertest.cc
+++ b/chrome/browser/chromeos/kiosk_next_home/kiosk_next_home_browsertest.cc
@@ -119,6 +119,14 @@
     return result;
   }
 
+  // Similar to RunJSAndGetStringResult, but for a double result.
+  double RunJSAndGetDoubleResult(const std::string& js_script) {
+    double result;
+    EXPECT_TRUE(content::ExecuteScriptAndExtractDouble(app_render_frame_host_,
+                                                       js_script, &result));
+    return result;
+  }
+
   LoginManagerMixin::TestUserInfo test_user_{
       AccountId::FromUserEmailGaiaId(kTestUser, kTestUserGaiaId)};
 
@@ -296,6 +304,26 @@
   EXPECT_EQ(access_token, FakeGaiaMixin::kFakeAllScopeAccessToken);
 }
 
+IN_PROC_BROWSER_TEST_F(KioskNextHomeRefreshTokenBrowserTest,
+                       FetchAccessTokenExpiration) {
+  // Access token fetchers take a 10% margin from token expirations before
+  // sending them to avoid returning an expired token.
+  int expected_expiration = FakeGaiaMixin::kFakeAccessTokenExpiration * 9 / 10;
+
+  double raw_expiration_time = RunJSAndGetDoubleResult(
+      R"(kioskNextHome.getChromeOsBridge().fetchAccessToken(['fake_scope'])
+           .then(tokenInfo => {
+                   domAutomationController.send(tokenInfo.expirationTime);
+                 }))");
+  base::Time expiration_time = base::Time::FromJsTime(raw_expiration_time);
+  base::TimeDelta actual_expiration = expiration_time - base::Time::Now();
+
+  // We give a 5 second margin of error for the token expiration. In this test
+  // we are mostly interested if the expiration reaches the Kiosk Next Home, not
+  // in per second precision.
+  EXPECT_NEAR(actual_expiration.InSeconds(), expected_expiration, 5);
+}
+
 IN_PROC_BROWSER_TEST_F(KioskNextHomeRefreshTokenBrowserTest, GetAccountId) {
   std::string id = RunJSAndGetStringResult(
       R"(kioskNextHome.getChromeOsBridge().getAccountId()
diff --git a/chrome/browser/chromeos/kiosk_next_home/mojom/app_controller.mojom b/chrome/browser/chromeos/kiosk_next_home/mojom/app_controller.mojom
index bdd17b8f..40e8974 100644
--- a/chrome/browser/chromeos/kiosk_next_home/mojom/app_controller.mojom
+++ b/chrome/browser/chromeos/kiosk_next_home/mojom/app_controller.mojom
@@ -28,10 +28,21 @@
   apps.mojom.Readiness readiness;
 };
 
+enum ArcStatus {
+  // Arc is still booting up or encountered an error.
+  kStopped,
+  // Arc is connected and ready to be used. Android ID is available.
+  kReady,
+};
+
 // Interface used by AppController clients to receive app related events.
 interface AppControllerClient {
   // Called when any field from the given app changes.
   OnAppChanged(App app);
+
+  // Called when the Arc status changes. Called at first when the client is
+  // set.
+  OnArcStatusChanged(ArcStatus status);
 };
 
 // Interface for managing Chrome OS apps from the Kiosk Next Home.
diff --git a/chrome/browser/chromeos/login/enterprise_enrollment_browsertest.cc b/chrome/browser/chromeos/login/enterprise_enrollment_browsertest.cc
index 6efe625..b7baad8e 100644
--- a/chrome/browser/chromeos/login/enterprise_enrollment_browsertest.cc
+++ b/chrome/browser/chromeos/login/enterprise_enrollment_browsertest.cc
@@ -58,6 +58,7 @@
 
 constexpr char kAdEncryptionTypesSelect[] = "encryptionList";
 constexpr char kAdMachineOrgUnitInput[] = "orgUnitInput";
+constexpr char kAdMoreOptionsButton[] = "moreOptionsBtn";
 constexpr char kAdMoreOptionsSaveButton[] = "moreOptionsSave";
 
 constexpr char kAdUserDomain[] = "user.domain.com";
@@ -220,7 +221,14 @@
   void CheckActiveDirectoryCredentialsShown() {
     EXPECT_TRUE(
         enrollment_ui_.IsStepDisplayed(test::ui::kEnrollmentStepADJoin));
-    test::OobeJS().ExpectVisiblePath({kAdDialog, kAdCredentialsStep});
+
+    std::initializer_list<base::StringPiece> ad_credentials{kAdDialog,
+                                                            kAdCredentialsStep};
+    test::OobeJS().ExpectVisiblePath(ad_credentials);
+    test::OobeJS().ExpectNE(
+        test::GetOobeElementPath(ad_credentials) + ".clientWidth", 0);
+    test::OobeJS().ExpectNE(
+        test::GetOobeElementPath(ad_credentials) + ".clientHeight", 0);
     test::OobeJS().ExpectHiddenPath({kAdDialog, kAdUnlockConfigurationStep});
   }
 
@@ -326,6 +334,8 @@
     test::OobeJS().TypeIntoPath(machine_name, {kAdDialog, kAdMachineNameInput});
     test::OobeJS().TypeIntoPath(username, {kAdDialog, kAdUsernameInput});
     test::OobeJS().TypeIntoPath(password, {kAdDialog, kAdPasswordInput});
+
+    test::OobeJS().TapOnPath({kAdDialog, kAdMoreOptionsButton});
     test::OobeJS().TypeIntoPath(machine_dn,
                                 {kAdDialog, kAdMachineOrgUnitInput});
 
@@ -334,6 +344,9 @@
                                          {kAdDialog, kAdEncryptionTypesSelect});
     }
     test::OobeJS().TapOnPath({kAdDialog, kAdMoreOptionsSaveButton});
+    test::OobeJS()
+        .CreateEnabledWaiter(true /* enabled */, {kAdDialog, kNextButton})
+        ->Wait();
     test::OobeJS().TapOnPath({kAdDialog, kNextButton});
   }
 
diff --git a/chrome/browser/chromeos/login/saml/saml_browsertest.cc b/chrome/browser/chromeos/login/saml/saml_browsertest.cc
index 95f716f..3402848 100644
--- a/chrome/browser/chromeos/login/saml/saml_browsertest.cc
+++ b/chrome/browser/chromeos/login/saml/saml_browsertest.cc
@@ -1432,17 +1432,21 @@
 
   // Mic should always be blocked.
   EXPECT_FALSE(web_contents_delegate->CheckMediaAccessPermission(
-      web_contents->GetMainFrame(), url1, blink::MEDIA_DEVICE_AUDIO_CAPTURE));
+      web_contents->GetMainFrame(), url1,
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
 
   // Camera should be allowed if allowed by the whitelist, otherwise blocked.
   EXPECT_TRUE(web_contents_delegate->CheckMediaAccessPermission(
-      web_contents->GetMainFrame(), url1, blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+      web_contents->GetMainFrame(), url1,
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
 
   EXPECT_TRUE(web_contents_delegate->CheckMediaAccessPermission(
-      web_contents->GetMainFrame(), url2, blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+      web_contents->GetMainFrame(), url2,
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
 
   EXPECT_FALSE(web_contents_delegate->CheckMediaAccessPermission(
-      web_contents->GetMainFrame(), url3, blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+      web_contents->GetMainFrame(), url3,
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
 
   // Camera should be blocked in the login screen, even if it's allowed via
   // content setting.
@@ -1454,7 +1458,8 @@
                                       std::string(), CONTENT_SETTING_ALLOW);
 
   EXPECT_FALSE(web_contents_delegate->CheckMediaAccessPermission(
-      web_contents->GetMainFrame(), url3, blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+      web_contents->GetMainFrame(), url3,
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
 }
 
 class SAMLPasswordAttributesTest : public SAMLPolicyTest,
diff --git a/chrome/browser/chromeos/login/screens/assistant_optin_flow_screen_browsertest.cc b/chrome/browser/chromeos/login/screens/assistant_optin_flow_screen_browsertest.cc
index 4a62513..9ea80648 100644
--- a/chrome/browser/chromeos/login/screens/assistant_optin_flow_screen_browsertest.cc
+++ b/chrome/browser/chromeos/login/screens/assistant_optin_flow_screen_browsertest.cc
@@ -70,8 +70,8 @@
   static constexpr int CONSENT_UI_FLAG_SKIP_ACTIVITY_CONTROL = 1;
   static constexpr int CONSENT_UI_FLAG_SKIP_THIRD_PARTY_DISCLOSURE = 1 << 1;
   static constexpr int CONSENT_UI_FLAG_ASK_EMAIL_OPT_IN = 1 << 2;
-  static constexpr int CONSENT_UI_FLAG_WAA_DISABLED_BY_DOMAIN = 1 << 3;
-  static constexpr int CONSENT_UI_FLAG_ASSISTANT_DISABLED_BY_DOMAIN = 1 << 4;
+  static constexpr int CONSENT_UI_FLAG_WAA_DISABLED_BY_POLICY = 1 << 3;
+  static constexpr int CONSENT_UI_FLAG_ASSISTANT_DISABLED_BY_POLICY = 1 << 4;
 
   enum class SpeakerIdEnrollmentMode {
     // On speaker enrollment request, the client will be notified that the
@@ -180,9 +180,9 @@
     auto* gaia_user_context_ui = settings_ui.mutable_gaia_user_context_ui();
     gaia_user_context_ui->set_is_gaia_user(true);
     gaia_user_context_ui->set_waa_disabled_by_dasher_domain(
-        (consent_ui_flags_ & CONSENT_UI_FLAG_WAA_DISABLED_BY_DOMAIN));
+        (consent_ui_flags_ & CONSENT_UI_FLAG_WAA_DISABLED_BY_POLICY));
     gaia_user_context_ui->set_assistant_disabled_by_dasher_domain(
-        (consent_ui_flags_ & CONSENT_UI_FLAG_ASSISTANT_DISABLED_BY_DOMAIN));
+        (consent_ui_flags_ & CONSENT_UI_FLAG_ASSISTANT_DISABLED_BY_POLICY));
 
     auto* consent_flow_ui = settings_ui.mutable_consent_flow_ui();
     consent_flow_ui->set_consent_status(
@@ -1139,9 +1139,9 @@
   EXPECT_TRUE(prefs->GetBoolean(arc::prefs::kVoiceInteractionContextEnabled));
 }
 
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest, WAADisabledByDasherDomain) {
+IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest, WAADisabledByPolicy) {
   assistant_settings_->set_consent_ui_flags(
-      FakeAssistantSettings::CONSENT_UI_FLAG_WAA_DISABLED_BY_DOMAIN);
+      FakeAssistantSettings::CONSENT_UI_FLAG_WAA_DISABLED_BY_POLICY);
 
   arc::VoiceInteractionControllerClient::Get()->NotifyStatusChanged(
       ash::mojom::VoiceInteractionState::STOPPED);
@@ -1152,14 +1152,14 @@
 
   ExpectCollectedOptIns({});
   PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
+  EXPECT_TRUE(prefs->GetBoolean(arc::prefs::kVoiceInteractionEnabled));
   EXPECT_FALSE(prefs->GetBoolean(arc::prefs::kVoiceInteractionHotwordEnabled));
   EXPECT_FALSE(prefs->GetBoolean(arc::prefs::kVoiceInteractionContextEnabled));
 }
 
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest,
-                       AssistantDisabledByDasherDomain) {
+IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest, AssistantDisabledByPolicy) {
   assistant_settings_->set_consent_ui_flags(
-      FakeAssistantSettings::CONSENT_UI_FLAG_ASSISTANT_DISABLED_BY_DOMAIN);
+      FakeAssistantSettings::CONSENT_UI_FLAG_ASSISTANT_DISABLED_BY_POLICY);
 
   arc::VoiceInteractionControllerClient::Get()->NotifyStatusChanged(
       ash::mojom::VoiceInteractionState::STOPPED);
@@ -1170,6 +1170,9 @@
 
   ExpectCollectedOptIns({});
   PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
+  EXPECT_TRUE(
+      prefs->GetBoolean(::assistant::prefs::kAssistantDisabledByPolicy));
+  EXPECT_FALSE(prefs->GetBoolean(arc::prefs::kVoiceInteractionEnabled));
   EXPECT_FALSE(prefs->GetBoolean(arc::prefs::kVoiceInteractionHotwordEnabled));
   EXPECT_FALSE(prefs->GetBoolean(arc::prefs::kVoiceInteractionContextEnabled));
 }
diff --git a/chrome/browser/chromeos/login/test/active_directory_login_mixin.cc b/chrome/browser/chromeos/login/test/active_directory_login_mixin.cc
index 7eea554..280f37b 100644
--- a/chrome/browser/chromeos/login/test/active_directory_login_mixin.cc
+++ b/chrome/browser/chromeos/login/test/active_directory_login_mixin.cc
@@ -106,7 +106,13 @@
   test::OobeJS().ExpectHiddenPath({kGaiaSigninId, kGaiaSigninDialogId});
 
   // Checks if Active Directory signin is visible.
-  test::OobeJS().ExpectVisiblePath({kGaiaSigninId, kAdOfflineAuthId});
+  std::initializer_list<base::StringPiece> ad_screen{kGaiaSigninId,
+                                                     kAdOfflineAuthId};
+  test::OobeJS().ExpectVisiblePath(ad_screen);
+  test::OobeJS().ExpectNE(test::GetOobeElementPath(ad_screen) + ".clientWidth",
+                          0);
+  test::OobeJS().ExpectNE(test::GetOobeElementPath(ad_screen) + ".clientHeight",
+                          0);
   test::OobeJS().ExpectHiddenPath(
       {kGaiaSigninId, kAdOfflineAuthId, kAdMachineInput});
   test::OobeJS().ExpectHiddenPath(
diff --git a/chrome/browser/chromeos/login/test/fake_gaia_mixin.cc b/chrome/browser/chromeos/login/test/fake_gaia_mixin.cc
index 3c7782e..f78d437 100644
--- a/chrome/browser/chromeos/login/test/fake_gaia_mixin.cc
+++ b/chrome/browser/chromeos/login/test/fake_gaia_mixin.cc
@@ -26,6 +26,7 @@
 const char FakeGaiaMixin::kFakeRefreshToken[] = "fake-refresh-token";
 const char FakeGaiaMixin::kEmptyUserServices[] = "[]";
 const char FakeGaiaMixin::kFakeAllScopeAccessToken[] = "fake-all-scope-token";
+const int FakeGaiaMixin::kFakeAccessTokenExpiration = 3600;
 
 const char FakeGaiaMixin::kFakeSIDCookie[] = "fake-SID-cookie";
 const char FakeGaiaMixin::kFakeLSIDCookie[] = "fake-LSID-cookie";
@@ -59,6 +60,7 @@
   token_info.audience = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
   token_info.email = user_email;
   token_info.any_scope = true;
+  token_info.expires_in = kFakeAccessTokenExpiration;
   fake_gaia_->IssueOAuthToken(refresh_token, token_info);
 }
 
@@ -75,6 +77,7 @@
   user_info_token.audience = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
 
   user_info_token.token = "fake-userinfo-token";
+  user_info_token.expires_in = kFakeAccessTokenExpiration;
   user_info_token.email = user_email;
   fake_gaia_->IssueOAuthToken(refresh_token, user_info_token);
 
@@ -83,6 +86,7 @@
     all_scopes_token.token = kFakeAllScopeAccessToken;
     all_scopes_token.audience =
         GaiaUrls::GetInstance()->oauth2_chrome_client_id();
+    all_scopes_token.expires_in = kFakeAccessTokenExpiration;
     all_scopes_token.email = user_email;
     all_scopes_token.any_scope = true;
     fake_gaia_->IssueOAuthToken(refresh_token, all_scopes_token);
@@ -105,6 +109,7 @@
   token_info.audience = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
 
   token_info.token = kTestUserinfoToken1;
+  token_info.expires_in = kFakeAccessTokenExpiration;
   token_info.email = kEnterpriseUser1;
   fake_gaia_->IssueOAuthToken(kTestRefreshToken1, token_info);
 
diff --git a/chrome/browser/chromeos/login/test/fake_gaia_mixin.h b/chrome/browser/chromeos/login/test/fake_gaia_mixin.h
index 9dfc1d5..3453dfa 100644
--- a/chrome/browser/chromeos/login/test/fake_gaia_mixin.h
+++ b/chrome/browser/chromeos/login/test/fake_gaia_mixin.h
@@ -30,6 +30,9 @@
   static const char kEmptyUserServices[];
   static const char kFakeAllScopeAccessToken[];
 
+  // How many seconds until the fake access tokens expire.
+  static const int kFakeAccessTokenExpiration;
+
   // FakeGaia is configured to return these cookies for kFakeUserEmail.
   static const char kFakeSIDCookie[];
   static const char kFakeLSIDCookie[];
diff --git a/chrome/browser/chromeos/login/test/oobe_base_test.h b/chrome/browser/chromeos/login/test/oobe_base_test.h
index 2e85f85..84bd725 100644
--- a/chrome/browser/chromeos/login/test/oobe_base_test.h
+++ b/chrome/browser/chromeos/login/test/oobe_base_test.h
@@ -52,7 +52,6 @@
   void WaitForGaiaPageBackButtonUpdate();
   void WaitForGaiaPageEvent(const std::string& event);
   void WaitForSigninScreen();
-  void WaitForEnrollmentSuccess();
   test::JSChecker SigninFrameJS();
 
   // Whether to use background networking. Note this is only effective when it
diff --git a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
index d9c61190..475910f 100644
--- a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
+++ b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
@@ -64,7 +64,7 @@
 
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type) override {
+                                  blink::mojom::MediaStreamType type) override {
     return MediaCaptureDevicesDispatcher::GetInstance()
         ->CheckMediaAccessPermission(render_frame_host, security_origin, type);
   }
diff --git a/chrome/browser/chromeos/login/ui/webui_login_view.cc b/chrome/browser/chromeos/login/ui/webui_login_view.cc
index 3f793bc..ecca49e 100644
--- a/chrome/browser/chromeos/login/ui/webui_login_view.cc
+++ b/chrome/browser/chromeos/login/ui/webui_login_view.cc
@@ -474,7 +474,7 @@
 bool WebUILoginView::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   return MediaCaptureDevicesDispatcher::GetInstance()
       ->CheckMediaAccessPermission(render_frame_host, security_origin, type);
 }
diff --git a/chrome/browser/chromeos/login/ui/webui_login_view.h b/chrome/browser/chromeos/login/ui/webui_login_view.h
index 6287597..630f098 100644
--- a/chrome/browser/chromeos/login/ui/webui_login_view.h
+++ b/chrome/browser/chromeos/login/ui/webui_login_view.h
@@ -151,7 +151,7 @@
       content::MediaResponseCallback callback) override;
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type) override;
+                                  blink::mojom::MediaStreamType type) override;
   bool PreHandleGestureEvent(content::WebContents* source,
                              const blink::WebGestureEvent& event) override;
 
diff --git a/chrome/browser/chromeos/policy/app_install_event_log_collector.cc b/chrome/browser/chromeos/policy/app_install_event_log_collector.cc
index 6dea779..69b2e7b7 100644
--- a/chrome/browser/chromeos/policy/app_install_event_log_collector.cc
+++ b/chrome/browser/chromeos/policy/app_install_event_log_collector.cc
@@ -184,6 +184,31 @@
                  std::move(event));
 }
 
+void AppInstallEventLogCollector::OnReportDirectInstall(
+    base::Time time,
+    const std::set<std::string>& package_names) {
+  for (const std::string& package_name : package_names) {
+    auto event = std::make_unique<em::AppInstallReportLogEvent>();
+    event->set_event_type(em::AppInstallReportLogEvent::DIRECT_INSTALL);
+    SetTimestampFromTime(event.get(), time);
+    delegate_->Add(package_name, true /* gather_disk_space_info */,
+                   std::move(event));
+  }
+}
+
+void AppInstallEventLogCollector::OnReportForceInstallMainLoopFailed(
+    base::Time time,
+    const std::set<std::string>& package_names) {
+  for (const std::string& package_name : package_names) {
+    auto event = std::make_unique<em::AppInstallReportLogEvent>();
+    event->set_event_type(
+        em::AppInstallReportLogEvent::CLOUDDPC_MAIN_LOOP_FAILED);
+    SetTimestampFromTime(event.get(), time);
+    delegate_->Add(package_name, true /* gather_disk_space_info */,
+                   std::move(event));
+  }
+}
+
 void AppInstallEventLogCollector::OnInstallationStarted(
     const std::string& package_name) {
   if (!pending_packages_.count(package_name)) {
diff --git a/chrome/browser/chromeos/policy/app_install_event_log_collector.h b/chrome/browser/chromeos/policy/app_install_event_log_collector.h
index 0b555ff..f2f00d6 100644
--- a/chrome/browser/chromeos/policy/app_install_event_log_collector.h
+++ b/chrome/browser/chromeos/policy/app_install_event_log_collector.h
@@ -87,6 +87,12 @@
   void OnCloudDpsFailed(base::Time time,
                         const std::string& package_name,
                         arc::mojom::InstallErrorReason reason) override;
+  void OnReportDirectInstall(
+      base::Time time,
+      const std::set<std::string>& package_names) override;
+  void OnReportForceInstallMainLoopFailed(
+      base::Time time,
+      const std::set<std::string>& package_names) override;
 
   // ArcAppListPrefs::Observer:
   void OnInstallationStarted(const std::string& package_name) override;
diff --git a/chrome/browser/chromeos/policy/app_install_event_log_collector_unittest.cc b/chrome/browser/chromeos/policy/app_install_event_log_collector_unittest.cc
index f0a9156..df13fdd 100644
--- a/chrome/browser/chromeos/policy/app_install_event_log_collector_unittest.cc
+++ b/chrome/browser/chromeos/policy/app_install_event_log_collector_unittest.cc
@@ -418,8 +418,16 @@
   collector->OnPendingPackagesChanged({kPackageName, kPackageName2});
 
   // Now kPackageName2 is in the pending set.
-  app_host->OnInstallationStarted(kPackageName2);
+  base::Time time = base::Time::Now();
+  collector->OnReportDirectInstall(time, {kPackageName2});
   EXPECT_EQ(3, delegate()->add_count());
+  EXPECT_EQ(em::AppInstallReportLogEvent::DIRECT_INSTALL,
+            delegate()->last_event().event_type());
+  EXPECT_EQ(kPackageName2, delegate()->last_request().package_name);
+  EXPECT_TRUE(delegate()->last_request().add_disk_space_info);
+
+  app_host->OnInstallationStarted(kPackageName2);
+  EXPECT_EQ(4, delegate()->add_count());
   EXPECT_EQ(em::AppInstallReportLogEvent::INSTALLATION_STARTED,
             delegate()->last_event().event_type());
   EXPECT_EQ(kPackageName2, delegate()->last_request().package_name);
@@ -429,12 +437,20 @@
   result.success = false;
   app_host->OnInstallationFinished(
       arc::mojom::InstallationResultPtr(result.Clone()));
-  EXPECT_EQ(4, delegate()->add_count());
+  EXPECT_EQ(5, delegate()->add_count());
   EXPECT_EQ(em::AppInstallReportLogEvent::INSTALLATION_FAILED,
             delegate()->last_event().event_type());
   EXPECT_EQ(kPackageName2, delegate()->last_request().package_name);
   EXPECT_TRUE(delegate()->last_request().add_disk_space_info);
 
+  time += base::TimeDelta::FromSeconds(1);
+  collector->OnReportForceInstallMainLoopFailed(time, {kPackageName2});
+  EXPECT_EQ(6, delegate()->add_count());
+  EXPECT_EQ(em::AppInstallReportLogEvent::CLOUDDPC_MAIN_LOOP_FAILED,
+            delegate()->last_event().event_type());
+  EXPECT_EQ(kPackageName2, delegate()->last_request().package_name);
+  EXPECT_TRUE(delegate()->last_request().add_disk_space_info);
+
   EXPECT_EQ(0, delegate()->add_for_all_count());
 }
 
diff --git a/chrome/browser/chromeos/policy/status_uploader.cc b/chrome/browser/chromeos/policy/status_uploader.cc
index f25904aa..a9576c4 100644
--- a/chrome/browser/chromeos/policy/status_uploader.cc
+++ b/chrome/browser/chromeos/policy/status_uploader.cc
@@ -167,14 +167,14 @@
 
 void StatusUploader::OnRequestUpdate(int render_process_id,
                                      int render_frame_id,
-                                     blink::MediaStreamType stream_type,
+                                     blink::mojom::MediaStreamType stream_type,
                                      const content::MediaRequestState state) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   // If a video or audio capture stream is opened, set a flag so we disallow
   // upload of potentially sensitive data.
   if (state == content::MEDIA_REQUEST_STATE_OPENING &&
-      (stream_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE ||
-       stream_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE)) {
+      (stream_type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE ||
+       stream_type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE)) {
     has_captured_media_ = true;
   }
 }
diff --git a/chrome/browser/chromeos/policy/status_uploader.h b/chrome/browser/chromeos/policy/status_uploader.h
index 9463260..3c4f701e 100644
--- a/chrome/browser/chromeos/policy/status_uploader.h
+++ b/chrome/browser/chromeos/policy/status_uploader.h
@@ -55,7 +55,7 @@
   // MediaCaptureDevicesDispatcher::Observer implementation
   void OnRequestUpdate(int render_process_id,
                        int render_frame_id,
-                       blink::MediaStreamType stream_type,
+                       blink::mojom::MediaStreamType stream_type,
                        const content::MediaRequestState state) override;
 
   // Returns true if the next status upload has been scheduled successfully.
diff --git a/chrome/browser/chromeos/policy/status_uploader_unittest.cc b/chrome/browser/chromeos/policy/status_uploader_unittest.cc
index 5e9cffc1..8568f93 100644
--- a/chrome/browser/chromeos/policy/status_uploader_unittest.cc
+++ b/chrome/browser/chromeos/policy/status_uploader_unittest.cc
@@ -331,7 +331,8 @@
 
   // Now mock video capture, and no session data should be allowed.
   MediaCaptureDevicesDispatcher::GetInstance()->OnMediaRequestStateChanged(
-      0, 0, 0, GURL("http://www.google.com"), blink::MEDIA_DEVICE_VIDEO_CAPTURE,
+      0, 0, 0, GURL("http://www.google.com"),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
       content::MEDIA_REQUEST_STATE_OPENING);
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(uploader->IsSessionDataUploadAllowed());
diff --git a/chrome/browser/chromeos/policy/upload_job_unittest.cc b/chrome/browser/chromeos/policy/upload_job_unittest.cc
index 040992a..88703f9 100644
--- a/chrome/browser/chromeos/policy/upload_job_unittest.cc
+++ b/chrome/browser/chromeos/policy/upload_job_unittest.cc
@@ -71,14 +71,14 @@
   // OAuth2TokenService:
   void FetchOAuth2Token(
       RequestImpl* request,
-      const std::string& account_id,
+      const CoreAccountId& account_id,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& client_id,
       const std::string& client_secret,
       const ScopeSet& scopes) override;
 
   // OAuth2TokenService:
-  void InvalidateAccessTokenImpl(const std::string& account_id,
+  void InvalidateAccessTokenImpl(const CoreAccountId& account_id,
                                  const std::string& client_id,
                                  const ScopeSet& scopes,
                                  const std::string& access_token) override;
@@ -103,7 +103,7 @@
 
 void MockOAuth2TokenService::FetchOAuth2Token(
     OAuth2TokenService::RequestImpl* request,
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& client_id,
     const std::string& client_secret,
@@ -142,7 +142,7 @@
 }
 
 void MockOAuth2TokenService::InvalidateAccessTokenImpl(
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     const std::string& client_id,
     const ScopeSet& scopes,
     const std::string& access_token) {
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service.cc b/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
index 267f21a..d23a13e 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
@@ -74,7 +74,7 @@
 
 void DeviceOAuth2TokenService::FetchOAuth2Token(
     RequestImpl* request,
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& client_id,
     const std::string& client_secret,
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service.h b/chrome/browser/chromeos/settings/device_oauth2_token_service.h
index 07a6348..6c4bd2b0d 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service.h
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service.h
@@ -48,7 +48,7 @@
   // Implementation of OAuth2TokenService.
   void FetchOAuth2Token(
       RequestImpl* request,
-      const std::string& account_id,
+      const CoreAccountId& account_id,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& client_id,
       const std::string& client_secret,
diff --git a/chrome/browser/chromeos/smb_client/smb_service.cc b/chrome/browser/chromeos/smb_client/smb_service.cc
index bfcfb24..397932a 100644
--- a/chrome/browser/chromeos/smb_client/smb_service.cc
+++ b/chrome/browser/chromeos/smb_client/smb_service.cc
@@ -158,9 +158,18 @@
 
 void SmbService::GatherSharesInNetwork(HostDiscoveryResponse discovery_callback,
                                        GatherSharesResponse shares_callback) {
-  shares_callback.Run(GetPreconfiguredSharePathsForDropdown());
-  share_finder_->GatherSharesInNetwork(std::move(discovery_callback),
-                                       std::move(shares_callback));
+  auto preconfigured_shares = GetPreconfiguredSharePathsForDropdown();
+  if (!preconfigured_shares.empty()) {
+    shares_callback.Run(std::move(preconfigured_shares), false);
+  }
+  share_finder_->GatherSharesInNetwork(
+      std::move(discovery_callback),
+      base::BindOnce(
+          [](GatherSharesResponse shares_callback,
+             const std::vector<SmbUrl>& shares_gathered) {
+            std::move(shares_callback).Run(shares_gathered, true);
+          },
+          std::move(shares_callback)));
 }
 
 void SmbService::UpdateCredentials(int32_t mount_id,
diff --git a/chrome/browser/chromeos/smb_client/smb_service.h b/chrome/browser/chromeos/smb_client/smb_service.h
index 4caac6d..4771e63 100644
--- a/chrome/browser/chromeos/smb_client/smb_service.h
+++ b/chrome/browser/chromeos/smb_client/smb_service.h
@@ -54,6 +54,9 @@
   using MountResponse = base::OnceCallback<void(SmbMountResult result)>;
   using StartReadDirIfSuccessfulCallback =
       base::OnceCallback<void(bool should_retry_start_read_dir)>;
+  using GatherSharesResponse =
+      base::RepeatingCallback<void(const std::vector<SmbUrl>& shares_gathered,
+                                   bool done)>;
 
   SmbService(Profile* profile, std::unique_ptr<base::TickClock> tick_clock);
   ~SmbService() override;
@@ -88,8 +91,11 @@
 
   // Gathers the hosts in the network using |share_finder_| and gets the shares
   // for each of the hosts found. |discovery_callback| is called as soon as host
-  // discovery is complete. |shares_callback| is called once per host and will
-  // contain the URLs to the shares found.
+  // discovery is complete. |shares_callback| may be called multiple times with
+  // new shares. |shares_callback| will be called with |done| == false when more
+  // shares are expected to be discovered. When share discovery is finished,
+  // |shares_callback| is called with |done| == true and will not be called
+  // again.
   void GatherSharesInNetwork(HostDiscoveryResponse discovery_callback,
                              GatherSharesResponse shares_callback);
 
diff --git a/chrome/browser/chromeos/smb_client/smb_share_finder.h b/chrome/browser/chromeos/smb_client/smb_share_finder.h
index f01ae81..d2d1fad 100644
--- a/chrome/browser/chromeos/smb_client/smb_share_finder.h
+++ b/chrome/browser/chromeos/smb_client/smb_share_finder.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_SMB_CLIENT_SMB_SHARE_FINDER_H_
 
 #include <string>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/macros.h"
@@ -18,16 +19,6 @@
 namespace chromeos {
 namespace smb_client {
 
-// The callback that will be passed to GatherSharesInNetwork. The shares found
-// will have a format of "smb://host/share". This will be called once per host.
-using GatherSharesResponse =
-    base::RepeatingCallback<void(const std::vector<SmbUrl>& shares_gathered)>;
-
-// The callback that will be passed to GatherSharesInNetwork. Used to implicitly
-// convert GatherSharesResponse to a OnceCallback.
-using GatherSharesInNetworkResponse =
-    base::OnceCallback<void(const std::vector<SmbUrl>& shares_gathered)>;
-
 // The callback run to indicate the scan for hosts on the network is complete.
 using HostDiscoveryResponse = base::OnceClosure;
 
@@ -35,6 +26,10 @@
 // available shares for each host found.
 class SmbShareFinder : public base::SupportsWeakPtr<SmbShareFinder> {
  public:
+  // The callback that will be passed to GatherSharesInNetwork.
+  using GatherSharesInNetworkResponse =
+      base::OnceCallback<void(const std::vector<SmbUrl>& shares_gathered)>;
+
   explicit SmbShareFinder(SmbProviderClient* client);
   ~SmbShareFinder();
 
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index be0c860..40f761e 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -398,7 +398,7 @@
 void DevToolsWindow::RemoveCreationCallbackForTest(
     const CreationCallback& callback) {
   for (size_t i = 0; i < g_creation_callbacks.Get().size(); ++i) {
-    if (g_creation_callbacks.Get().at(i).Equals(callback)) {
+    if (g_creation_callbacks.Get().at(i) == callback) {
       g_creation_callbacks.Get().erase(g_creation_callbacks.Get().begin() + i);
       return;
     }
diff --git a/chrome/browser/download/save_page_browsertest.cc b/chrome/browser/download/save_page_browsertest.cc
index 318c8407..53ae183e 100644
--- a/chrome/browser/download/save_page_browsertest.cc
+++ b/chrome/browser/download/save_page_browsertest.cc
@@ -1221,9 +1221,12 @@
 }
 
 // Tests that saving a page from file: URI works.
-// TODO(https://crbug.com/840063): Deflake and reenable.
 IN_PROC_BROWSER_TEST_P(SavePageOriginalVsSavedComparisonTest,
-                       DISABLED_ObjectElementsViaFile) {
+                       ObjectElementsViaFile) {
+  // TODO(lukasza): https://crbug.com/964364: Re-enable the test.
+  if (content::MimeHandlerViewMode::UsesCrossProcessFrame())
+    return;
+
   base::FilePath test_data_dir;
   ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
   GURL url(net::FilePathToFileURL(
diff --git a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
index fd08253..90d56ba 100644
--- a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
+++ b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
@@ -354,15 +354,18 @@
   // No need to test return value, if we got an empty list, we insert at end.
   if (params->selected_id_list)
     GetNodesFromVector(model, *params->selected_id_list, &nodes);
-  int highest_index = -1;  // -1 means insert at end of list.
+  int highest_index = -1;
   for (size_t i = 0; i < nodes.size(); ++i) {
     // + 1 so that we insert after the selection.
     int index = parent_node->GetIndexOf(nodes[i]) + 1;
     if (index > highest_index)
       highest_index = index;
   }
+  size_t insertion_index = (highest_index == -1)
+                               ? parent_node->children().size()
+                               : size_t{highest_index};
 
-  bookmarks::PasteFromClipboard(model, parent_node, highest_index);
+  bookmarks::PasteFromClipboard(model, parent_node, insertion_index);
   return true;
 }
 
@@ -435,11 +438,11 @@
   content::WebContents* web_contents = GetSenderWebContents();
   DCHECK_EQ(VIEW_TYPE_TAB_CONTENTS, GetViewType(web_contents));
 
-  int drop_index;
+  size_t drop_index;
   if (params->index)
-    drop_index = *params->index;
+    drop_index = size_t{*params->index};
   else
-    drop_index = drop_parent->child_count();
+    drop_index = drop_parent->children().size();
 
   BookmarkManagerPrivateDragEventRouter* router =
       BookmarkManagerPrivateDragEventRouter::FromWebContents(web_contents);
diff --git a/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc b/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc
index 2739f25..4545ad2 100644
--- a/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc
+++ b/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc
@@ -148,21 +148,22 @@
     parentId = model->other_node()->id();
   } else {
     if (!GetBookmarkIdAsInt64(*details.parent_id, &parentId))
-      return NULL;
+      return nullptr;
   }
   const BookmarkNode* parent = bookmarks::GetBookmarkNodeByID(model, parentId);
   if (!CanBeModified(parent))
-    return NULL;
+    return nullptr;
 
-  int index;
+  size_t index;
   if (!details.index.get()) {  // Optional (defaults to end).
-    index = parent->child_count();
+    index = parent->children().size();
   } else {
-    index = *details.index;
-    if (index > parent->child_count() || index < 0) {
+    if (*details.index < 0 ||
+        size_t{*details.index} > parent->children().size()) {
       error_ = bookmark_api_constants::kInvalidIndexError;
-      return NULL;
+      return nullptr;
     }
+    index = size_t{*details.index};
   }
 
   base::string16 title;  // Optional.
@@ -176,7 +177,7 @@
   GURL url(url_string);
   if (!url_string.empty() && !url.is_valid()) {
     error_ = bookmark_api_constants::kInvalidUrlError;
-    return NULL;
+    return nullptr;
   }
 
   const BookmarkNode* node;
@@ -275,15 +276,15 @@
 
 void BookmarkEventRouter::BookmarkNodeMoved(BookmarkModel* model,
                                             const BookmarkNode* old_parent,
-                                            int old_index,
+                                            size_t old_index,
                                             const BookmarkNode* new_parent,
-                                            int new_index) {
-  const BookmarkNode* node = new_parent->GetChild(new_index);
+                                            size_t new_index) {
+  const BookmarkNode* node = new_parent->children()[new_index].get();
   api::bookmarks::OnMoved::MoveInfo move_info;
   move_info.parent_id = base::NumberToString(new_parent->id());
-  move_info.index = new_index;
+  move_info.index = int{new_index};
   move_info.old_parent_id = base::NumberToString(old_parent->id());
-  move_info.old_index = old_index;
+  move_info.old_index = int{old_index};
 
   DispatchEvent(events::BOOKMARKS_ON_MOVED, api::bookmarks::OnMoved::kEventName,
                 api::bookmarks::OnMoved::Create(
@@ -292,8 +293,8 @@
 
 void BookmarkEventRouter::BookmarkNodeAdded(BookmarkModel* model,
                                             const BookmarkNode* parent,
-                                            int index) {
-  const BookmarkNode* node = parent->GetChild(index);
+                                            size_t index) {
+  const BookmarkNode* node = parent->children()[index].get();
   BookmarkTreeNode tree_node =
       bookmark_api_helpers::GetBookmarkTreeNode(managed_, node, false, false);
   DispatchEvent(events::BOOKMARKS_ON_CREATED,
@@ -305,12 +306,12 @@
 void BookmarkEventRouter::BookmarkNodeRemoved(
     BookmarkModel* model,
     const BookmarkNode* parent,
-    int index,
+    size_t index,
     const BookmarkNode* node,
     const std::set<GURL>& removed_urls) {
   api::bookmarks::OnRemoved::RemoveInfo remove_info;
   remove_info.parent_id = base::NumberToString(parent->id());
-  remove_info.index = index;
+  remove_info.index = int{index};
   bookmark_api_helpers::PopulateBookmarkTreeNode(managed_, node, true, false,
                                                  &remove_info.node);
 
@@ -640,15 +641,16 @@
   if (!CanBeModified(parent) || !CanBeModified(node))
     return false;
 
-  int index;
+  size_t index;
   if (params->destination.index.get()) {  // Optional (defaults to end).
-    index = *params->destination.index;
-    if (index > parent->child_count() || index < 0) {
+    if (*params->destination.index < 0 ||
+        size_t{*params->destination.index} > parent->children().size()) {
       error_ = bookmark_api_constants::kInvalidIndexError;
       return false;
     }
+    index = size_t{*params->destination.index};
   } else {
-    index = parent->child_count();
+    index = parent->children().size();
   }
 
   model->Move(node, parent, index);
diff --git a/chrome/browser/extensions/api/bookmarks/bookmarks_api.h b/chrome/browser/extensions/api/bookmarks/bookmarks_api.h
index 52cde39d..f832d8f 100644
--- a/chrome/browser/extensions/api/bookmarks/bookmarks_api.h
+++ b/chrome/browser/extensions/api/bookmarks/bookmarks_api.h
@@ -56,15 +56,15 @@
   void BookmarkModelBeingDeleted(bookmarks::BookmarkModel* model) override;
   void BookmarkNodeMoved(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* old_parent,
-                         int old_index,
+                         size_t old_index,
                          const bookmarks::BookmarkNode* new_parent,
-                         int new_index) override;
+                         size_t new_index) override;
   void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* parent,
-                         int index) override;
+                         size_t index) override;
   void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
                            const bookmarks::BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const bookmarks::BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override;
   void BookmarkAllUserNodesRemoved(bookmarks::BookmarkModel* model,
diff --git a/chrome/browser/extensions/api/declarative_content/declarative_content_is_bookmarked_condition_tracker.cc b/chrome/browser/extensions/api/declarative_content/declarative_content_is_bookmarked_condition_tracker.cc
index faef055e..02464070 100644
--- a/chrome/browser/extensions/api/declarative_content/declarative_content_is_bookmarked_condition_tracker.cc
+++ b/chrome/browser/extensions/api/declarative_content/declarative_content_is_bookmarked_condition_tracker.cc
@@ -221,11 +221,11 @@
 void DeclarativeContentIsBookmarkedConditionTracker::BookmarkNodeAdded(
     bookmarks::BookmarkModel* model,
     const bookmarks::BookmarkNode* parent,
-    int index) {
+    size_t index) {
   if (!extensive_bookmark_changes_in_progress_) {
     for (const auto& web_contents_tracker_pair : per_web_contents_tracker_) {
       web_contents_tracker_pair.second->BookmarkAddedForUrl(
-          parent->GetChild(index)->url());
+          parent->children()[index]->url());
     }
   }
 }
@@ -233,7 +233,7 @@
 void DeclarativeContentIsBookmarkedConditionTracker::BookmarkNodeRemoved(
     bookmarks::BookmarkModel* model,
     const bookmarks::BookmarkNode* parent,
-    int old_index,
+    size_t old_index,
     const bookmarks::BookmarkNode* node,
     const std::set<GURL>& no_longer_bookmarked) {
   if (!extensive_bookmark_changes_in_progress_) {
diff --git a/chrome/browser/extensions/api/declarative_content/declarative_content_is_bookmarked_condition_tracker.h b/chrome/browser/extensions/api/declarative_content/declarative_content_is_bookmarked_condition_tracker.h
index ab38810..7cbc0b2 100644
--- a/chrome/browser/extensions/api/declarative_content/declarative_content_is_bookmarked_condition_tracker.h
+++ b/chrome/browser/extensions/api/declarative_content/declarative_content_is_bookmarked_condition_tracker.h
@@ -124,10 +124,10 @@
   void BookmarkModelChanged() override;
   void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* parent,
-                         int index) override;
+                         size_t index) override;
   void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
                            const bookmarks::BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const bookmarks::BookmarkNode* node,
                            const std::set<GURL>& no_longer_bookmarked) override;
   void ExtensiveBookmarkChangesBeginning(
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc b/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
index 93b5c14..57dbca8 100644
--- a/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
+++ b/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/extensions/browser_action_test_util.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/extensions/extension_test_util.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -27,6 +28,7 @@
 #include "extensions/common/features/feature_channel.h"
 #include "extensions/common/manifest_constants.h"
 #include "extensions/test/extension_test_message_listener.h"
+#include "extensions/test/result_catcher.h"
 #include "extensions/test/test_extension_dir.h"
 #include "ui/base/window_open_disposition.h"
 
@@ -83,6 +85,19 @@
     return nullptr;
   }
 
+  const char* GetAPIName(ActionInfo::Type action_type) {
+    switch (action_type) {
+      case ActionInfo::TYPE_ACTION:
+        return "action";
+      case ActionInfo::TYPE_BROWSER:
+        return "browserAction";
+      case ActionInfo::TYPE_PAGE:
+        return "pageAction";
+    }
+    NOTREACHED();
+    return nullptr;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(ExtensionActionAPITest);
 };
@@ -269,6 +284,63 @@
             action->GetTitle(ExtensionAction::kDefaultTabId));
 }
 
+// Tests dispatching the onClicked event to listeners when the extension action
+// in the toolbar is pressed.
+IN_PROC_BROWSER_TEST_P(MultiActionAPITest, OnClickedDispatching) {
+  constexpr char kManifestTemplate[] =
+      R"({
+           "name": "Test Clicking",
+           "manifest_version": 2,
+           "version": "0.1",
+           "%s": {},
+           "background": { "scripts": ["background.js"] }
+         })";
+  constexpr char kBackgroundJsTemplate[] =
+      R"(chrome.%s.onClicked.addListener((tab) => {
+           // Check a few properties on the tabs object to make sure it's sane.
+           chrome.test.assertTrue(!!tab);
+           chrome.test.assertTrue(tab.id > 0);
+           chrome.test.assertTrue(tab.index > -1);
+           chrome.test.notifyPass();
+         });)";
+
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(
+      base::StringPrintf(kManifestTemplate, GetManifestKey(GetParam())));
+  test_dir.WriteFile(
+      FILE_PATH_LITERAL("background.js"),
+      base::StringPrintf(kBackgroundJsTemplate, GetAPIName(GetParam())));
+
+  // Though this says "BrowserActionTestUtil", it's actually used for all
+  // toolbar actions.
+  // TODO(devlin): Rename it to ToolbarActionTestUtil.
+  std::unique_ptr<BrowserActionTestUtil> toolbar_helper =
+      BrowserActionTestUtil::Create(browser());
+  EXPECT_EQ(0, toolbar_helper->NumberOfBrowserActions());
+  const Extension* extension = LoadExtension(test_dir.UnpackedPath());
+  ASSERT_TRUE(extension);
+  ASSERT_EQ(1, toolbar_helper->NumberOfBrowserActions());
+  EXPECT_EQ(extension->id(), toolbar_helper->GetExtensionId(0));
+
+  auto* action_manager = ExtensionActionManager::Get(profile());
+  ExtensionAction* action = action_manager->GetExtensionAction(*extension);
+  ASSERT_TRUE(action);
+
+  const int tab_id = SessionTabHelper::IdForTab(
+                         browser()->tab_strip_model()->GetActiveWebContents())
+                         .id();
+  bool is_visible = action->GetIsVisible(tab_id);
+  // The action should only be disabled if that was its default state.
+  EXPECT_EQ(is_visible ? ActionInfo::STATE_ENABLED : ActionInfo::STATE_DISABLED,
+            action->default_state());
+  if (!is_visible)
+    action->SetIsVisible(tab_id, true);
+
+  ResultCatcher result_catcher;
+  toolbar_helper->Press(0);
+  ASSERT_TRUE(result_catcher.GetNextResult()) << result_catcher.message();
+}
+
 INSTANTIATE_TEST_SUITE_P(,
                          MultiActionAPITest,
                          testing::Values(ActionInfo::TYPE_ACTION,
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc b/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
index 4d163e8e..50dee6a 100644
--- a/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
+++ b/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
@@ -760,7 +760,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(NetworkingPrivateChromeOSApiTest, GetErrorState) {
-  chromeos::NetworkHandler::Get()->network_state_handler()->SetLastErrorForTest(
+  chromeos::NetworkHandler::Get()->network_state_handler()->SetErrorForTest(
       kWifi1ServicePath, "TestErrorState");
   EXPECT_TRUE(RunNetworkingSubtest("getErrorState")) << message_;
 }
diff --git a/chrome/browser/extensions/api/permissions/permissions_api_helpers.cc b/chrome/browser/extensions/api/permissions/permissions_api_helpers.cc
index 34b0007c..0b6e2cf 100644
--- a/chrome/browser/extensions/api/permissions/permissions_api_helpers.cc
+++ b/chrome/browser/extensions/api/permissions/permissions_api_helpers.cc
@@ -114,21 +114,21 @@
   // Validate and partition the parsed APIs.
   for (const auto* api_permission : apis) {
     if (required_permissions.apis().count(api_permission->id())) {
-      result->required_apis.insert(api_permission->id());
+      result->required_apis.insert(api_permission->Clone());
       continue;
     }
 
     if (!optional_permissions.apis().count(api_permission->id())) {
-      result->unlisted_apis.insert(api_permission->id());
+      result->unlisted_apis.insert(api_permission->Clone());
       continue;
     }
 
     if (!api_permission->info()->supports_optional()) {
-      result->unsupported_optional_apis.insert(api_permission->id());
+      result->unsupported_optional_apis.insert(api_permission->Clone());
       continue;
     }
 
-    result->optional_apis.insert(api_permission->id());
+    result->optional_apis.insert(api_permission->Clone());
   }
 
   return true;
diff --git a/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc b/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc
index 12c46b9..9d9bbd62 100644
--- a/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc
+++ b/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc
@@ -9,12 +9,15 @@
 #include <memory>
 #include <utility>
 
+#include "base/json/json_reader.h"
 #include "base/macros.h"
 #include "base/values.h"
 #include "chrome/browser/extensions/permissions_test_util.h"
 #include "chrome/common/extensions/api/permissions.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/permissions/permission_set.h"
+#include "extensions/common/permissions/permissions_info.h"
+#include "extensions/common/permissions/usb_device_permission.h"
 #include "extensions/common/url_pattern_set.h"
 #include "extensions/common/user_script.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -484,4 +487,46 @@
   }
 }
 
+// Tests that unpacking a UsbDevicePermission with a list of USB device IDs
+// preserves the device list in the result object.
+TEST(ExtensionPermissionsAPIHelpers, Unpack_UsbDevicePermission) {
+  constexpr char kDeviceListJson[] = R"([{"productId":2,"vendorId":1}])";
+  constexpr char kUsbDevicesPermissionJson[] =
+      R"(usbDevices|[{"productId":2,"vendorId":1}])";
+
+  auto device_list = base::JSONReader::Read(kDeviceListJson);
+  ASSERT_TRUE(device_list) << "Failed to parse device list JSON.";
+
+  auto usb_device_permission = std::make_unique<UsbDevicePermission>(
+      PermissionsInfo::GetInstance()->GetByID(APIPermission::ID::kUsbDevice));
+  std::string error;
+  std::vector<std::string> unhandled_permissions;
+  bool from_value_result = usb_device_permission->FromValue(
+      &device_list.value(), &error, &unhandled_permissions);
+  ASSERT_TRUE(from_value_result);
+  EXPECT_TRUE(unhandled_permissions.empty());
+
+  APIPermissionSet api_permission_set;
+  api_permission_set.insert(usb_device_permission->Clone());
+  PermissionSet optional_permissions(std::move(api_permission_set),
+                                     ManifestPermissionSet(), URLPatternSet(),
+                                     URLPatternSet());
+
+  Permissions permissions_object;
+  permissions_object.permissions = std::make_unique<std::vector<std::string>>(
+      std::vector<std::string>({kUsbDevicesPermissionJson}));
+  constexpr bool kHasFileAccess = false;
+  std::unique_ptr<UnpackPermissionSetResult> unpack_result =
+      UnpackPermissionSet(permissions_object, PermissionSet(),
+                          optional_permissions, kHasFileAccess, &error);
+
+  ASSERT_TRUE(unpack_result) << error;
+
+  ASSERT_EQ(1U, unpack_result->optional_apis.size());
+  EXPECT_EQ(APIPermission::ID::kUsbDevice,
+            unpack_result->optional_apis.begin()->id());
+  EXPECT_TRUE(unpack_result->optional_apis.begin()->Contains(
+      usb_device_permission.get()));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 07e38b9..ad64900 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -403,6 +403,8 @@
   // Google Assistant.
   (*s_whitelist)[::assistant::prefs::kAssistantConsentStatus] =
       settings_api::PrefType::PREF_TYPE_NUMBER;
+  (*s_whitelist)[::assistant::prefs::kAssistantDisabledByPolicy] =
+      settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[arc::prefs::kVoiceInteractionEnabled] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[arc::prefs::kVoiceInteractionContextEnabled] =
diff --git a/chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.cc b/chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.cc
index a965ebb..561d3a7d 100644
--- a/chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.cc
+++ b/chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.cc
@@ -82,13 +82,17 @@
   if (tab_capture_registry &&
       tab_capture_registry->VerifyRequest(
           request.render_process_id, request.render_frame_id, extension_id)) {
-    if (request.audio_type == blink::MEDIA_GUM_TAB_AUDIO_CAPTURE) {
+    if (request.audio_type ==
+        blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE) {
       devices.push_back(blink::MediaStreamDevice(
-          blink::MEDIA_GUM_TAB_AUDIO_CAPTURE, std::string(), std::string()));
+          blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE, std::string(),
+          std::string()));
     }
-    if (request.video_type == blink::MEDIA_GUM_TAB_VIDEO_CAPTURE) {
+    if (request.video_type ==
+        blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE) {
       devices.push_back(blink::MediaStreamDevice(
-          blink::MEDIA_GUM_TAB_VIDEO_CAPTURE, std::string(), std::string()));
+          blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE, std::string(),
+          std::string()));
     }
   }
 
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc
index 911f68fd..9a140c1 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc
@@ -251,11 +251,11 @@
 void TabCaptureRegistry::OnRequestUpdate(
     int target_render_process_id,
     int target_render_frame_id,
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     const content::MediaRequestState new_state) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (stream_type != blink::MEDIA_GUM_TAB_VIDEO_CAPTURE &&
-      stream_type != blink::MEDIA_GUM_TAB_AUDIO_CAPTURE) {
+  if (stream_type != blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE &&
+      stream_type != blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE) {
     return;
   }
 
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_registry.h b/chrome/browser/extensions/api/tab_capture/tab_capture_registry.h
index e5733c0..5f02c88 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_registry.h
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_registry.h
@@ -99,7 +99,7 @@
   // MediaCaptureDevicesDispatcher::Observer implementation.
   void OnRequestUpdate(int target_render_process_id,
                        int target_render_frame_id,
-                       blink::MediaStreamType stream_type,
+                       blink::mojom::MediaStreamType stream_type,
                        const content::MediaRequestState state) override;
 
   // Send a StatusChanged event containing the current state of |request|.
diff --git a/chrome/browser/extensions/api/virtual_keyboard_private/OWNERS b/chrome/browser/extensions/api/virtual_keyboard_private/OWNERS
index 9834f7a..94fbd75 100644
--- a/chrome/browser/extensions/api/virtual_keyboard_private/OWNERS
+++ b/chrome/browser/extensions/api/virtual_keyboard_private/OWNERS
@@ -1,3 +1,3 @@
-file://ash/keyboard/ui/OWNERS
+file://ash/keyboard/OWNERS
 
 # COMPONENT: UI>Input>VirtualKeyboard
diff --git a/chrome/browser/extensions/bookmark_app_helper.cc b/chrome/browser/extensions/bookmark_app_helper.cc
index 507f5c0..4bc6f9c 100644
--- a/chrome/browser/extensions/bookmark_app_helper.cc
+++ b/chrome/browser/extensions/bookmark_app_helper.cc
@@ -57,10 +57,20 @@
 #include "extensions/browser/pref_names.h"
 #include "extensions/common/extension.h"
 #include "net/base/load_flags.h"
+#include "net/base/url_util.h"
 #include "net/url_request/url_request.h"
 #include "third_party/blink/public/common/manifest/manifest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
+#if defined(OS_CHROMEOS)
+#include "base/strings/string_util.h"
+#include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
+#include "components/arc/arc_service_manager.h"
+#include "components/arc/common/app.mojom.h"
+#include "components/arc/common/intent_helper.mojom.h"
+#include "components/arc/session/arc_bridge_service.h"
+#endif
+
 namespace extensions {
 
 namespace {
@@ -203,6 +213,20 @@
   std::vector<web_app::BitmapAndSource> downloaded_bitmaps_;
 };
 
+#if defined(OS_CHROMEOS)
+const char kChromeOsPlayPlatform[] = "chromeos_play";
+const char kPlayIntentPrefix[] =
+    "https://play.google.com/store/apps/details?id=";
+
+std::string ExtractQueryValueForName(const GURL& url, const std::string& name) {
+  for (net::QueryIterator it(url); !it.IsAtEnd(); it.Advance()) {
+    if (it.GetKey() == name)
+      return it.GetValue();
+  }
+  return std::string();
+}
+#endif  // defined(OS_CHROMEOS)
+
 }  // namespace
 
 BookmarkAppHelper::BookmarkAppHelper(
@@ -312,6 +336,11 @@
       contents_->GetVisibleURL().SchemeIs(content::kChromeUIScheme))
     web_app_icon_downloader_->SkipPageFavicons();
 
+  // If we tried to check for an intent to the Play Store, wait for the async
+  // reply.
+  if (DidCheckForIntentToPlayStore(*data.manifest))
+    return;
+
   web_app_icon_downloader_->Start();
 }
 
@@ -487,6 +516,78 @@
   callback_.Run(extension, web_app_info_);
 }
 
+bool BookmarkAppHelper::DidCheckForIntentToPlayStore(
+    const blink::Manifest& manifest) {
+#if defined(OS_CHROMEOS)
+  if (!base::FeatureList::IsEnabled(features::kApkWebAppInstalls))
+    return false;
+
+  if (for_installable_site_ != web_app::ForInstallableSite::kYes)
+    return false;
+
+  for (const auto& application : manifest.related_applications) {
+    std::string id = base::UTF16ToUTF8(application.id.string());
+    if (!base::EqualsASCII(application.platform.string(),
+                           kChromeOsPlayPlatform)) {
+      continue;
+    }
+
+    std::string id_from_app_url =
+        ExtractQueryValueForName(application.url, "id");
+
+    if (id.empty()) {
+      if (id_from_app_url.empty())
+        continue;
+      id = id_from_app_url;
+    }
+
+    // Attach the referrer value.
+    std::string referrer =
+        ExtractQueryValueForName(application.url, "referrer");
+    if (!referrer.empty())
+      referrer = "&referrer=" + referrer;
+
+    std::string intent = kPlayIntentPrefix + id + referrer;
+
+    auto* arc_service_manager = arc::ArcServiceManager::Get();
+    if (arc_service_manager) {
+      auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
+          arc_service_manager->arc_bridge_service()->app(), IsInstallable);
+      if (instance) {
+        instance->IsInstallable(
+            id,
+            base::BindOnce(&BookmarkAppHelper::OnDidCheckForIntentToPlayStore,
+                           weak_factory_.GetWeakPtr(), intent));
+        return true;
+      }
+    }
+  }
+#endif  // defined(OS_CHROMEOS)
+  return false;
+}
+
+void BookmarkAppHelper::OnDidCheckForIntentToPlayStore(
+    const std::string& intent,
+    bool should_intent_to_store) {
+#if defined(OS_CHROMEOS)
+  if (should_intent_to_store) {
+    auto* arc_service_manager = arc::ArcServiceManager::Get();
+    if (arc_service_manager) {
+      auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
+          arc_service_manager->arc_bridge_service()->intent_helper(),
+          HandleUrl);
+      if (instance) {
+        instance->HandleUrl(intent, arc::kPlayStorePackage);
+        callback_.Run(nullptr, web_app_info_);
+        return;
+      }
+    }
+  }
+#endif  // defined(OS_CHROMEOS)
+
+  web_app_icon_downloader_->Start();
+}
+
 void BookmarkAppHelper::Observe(int type,
                                 const content::NotificationSource& source,
                                 const content::NotificationDetails& details) {
diff --git a/chrome/browser/extensions/bookmark_app_helper.h b/chrome/browser/extensions/bookmark_app_helper.h
index 797b06b..52134ad 100644
--- a/chrome/browser/extensions/bookmark_app_helper.h
+++ b/chrome/browser/extensions/bookmark_app_helper.h
@@ -28,6 +28,10 @@
 class Profile;
 class SkBitmap;
 
+namespace blink {
+struct Manifest;
+}
+
 namespace content {
 class WebContents;
 }  // namespace content
@@ -163,6 +167,17 @@
   void OnShortcutCreationCompleted(const std::string& extension_id,
                                    bool shortcut_created);
 
+  void MaybeStartIconDownload();
+
+  // Returns true if we dispatched an asynchronous check for whether an intent
+  // to the Play Store should be made, and false otherwise.
+  bool DidCheckForIntentToPlayStore(const blink::Manifest& manifest);
+
+  // Called when the asynchronous check for whether an intent to the Play Store
+  // should be made returns.
+  void OnDidCheckForIntentToPlayStore(const std::string& intent,
+                                      bool should_intent_to_store);
+
   // Overridden from content::NotificationObserver:
   void Observe(int type,
                const content::NotificationSource& source,
diff --git a/chrome/browser/extensions/chrome_extension_host_delegate.cc b/chrome/browser/extensions/chrome_extension_host_delegate.cc
index 5becade..8bb650f 100644
--- a/chrome/browser/extensions/chrome_extension_host_delegate.cc
+++ b/chrome/browser/extensions/chrome_extension_host_delegate.cc
@@ -101,7 +101,7 @@
 bool ChromeExtensionHostDelegate::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const Extension* extension) {
   return MediaCaptureDevicesDispatcher::GetInstance()
       ->CheckMediaAccessPermission(render_frame_host, security_origin, type,
diff --git a/chrome/browser/extensions/chrome_extension_host_delegate.h b/chrome/browser/extensions/chrome_extension_host_delegate.h
index ee7f91e..f83b368 100644
--- a/chrome/browser/extensions/chrome_extension_host_delegate.h
+++ b/chrome/browser/extensions/chrome_extension_host_delegate.h
@@ -30,7 +30,7 @@
                                  const Extension* extension) override;
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type,
+                                  blink::mojom::MediaStreamType type,
                                   const Extension* extension) override;
   ExtensionHostQueue* GetExtensionHostQueue() const override;
   gfx::Size EnterPictureInPicture(content::WebContents* web_contents,
diff --git a/chrome/browser/extensions/chrome_extensions_interface_registration.cc b/chrome/browser/extensions/chrome_extensions_interface_registration.cc
index 6d4684f..5a1307a 100644
--- a/chrome/browser/extensions/chrome_extensions_interface_registration.cc
+++ b/chrome/browser/extensions/chrome_extensions_interface_registration.cc
@@ -92,9 +92,9 @@
          const std::string& source_id,
          base::OnceCallback<void(const base::Optional<std::string>&)>
              callback) {
-        content::GetMediaDeviceIDForHMAC(blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                                         salt, std::move(origin), source_id,
-                                         std::move(callback));
+        content::GetMediaDeviceIDForHMAC(
+            blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, salt,
+            std::move(origin), source_id, std::move(callback));
       },
       salt, std::move(origin), source_id, std::move(callback));
   base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index 062f641..cc96331 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "ash/public/cpp/ash_features.h"
+#include "ash/public/cpp/ash_pref_names.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/feature_list.h"
@@ -149,12 +150,8 @@
 ComponentLoader::ComponentExtensionInfo::~ComponentExtensionInfo() {}
 
 ComponentLoader::ComponentLoader(ExtensionServiceInterface* extension_service,
-                                 PrefService* profile_prefs,
-                                 PrefService* local_state,
                                  Profile* profile)
-    : profile_prefs_(profile_prefs),
-      local_state_(local_state),
-      profile_(profile),
+    : profile_(profile),
       extension_service_(extension_service),
       ignore_whitelist_for_testing_(false),
       weak_factory_(this) {}
@@ -521,6 +518,17 @@
 #endif
 
   if (!skip_session_components) {
+#if BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)
+    AddHangoutServicesExtension();
+#endif  // BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)
+
+    bool install_feedback = enable_background_extensions_during_testing;
+#if defined(GOOGLE_CHROME_BUILD)
+    install_feedback = true;
+#endif  // defined(GOOGLE_CHROME_BUILD)
+    if (install_feedback)
+      Add(IDR_FEEDBACK_MANIFEST, base::FilePath(FILE_PATH_LITERAL("feedback")));
+
 #if defined(OS_CHROMEOS)
     AddChromeCameraApp();
     AddVideoPlayerExtension();
@@ -532,25 +540,16 @@
 #if BUILDFLAG(ENABLE_NACL)
     AddZipArchiverExtension();
 #endif  // BUILDFLAG(ENABLE_NACL)
-#endif  // defined(OS_CHROMEOS)
 
-#if BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)
-    AddHangoutServicesExtension();
-#endif  // BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)
-    bool install_feedback = enable_background_extensions_during_testing;
-#if defined(GOOGLE_CHROME_BUILD)
-    install_feedback = true;
-#endif  // defined(GOOGLE_CHROME_BUILD)
-    if (install_feedback)
-      Add(IDR_FEEDBACK_MANIFEST, base::FilePath(FILE_PATH_LITERAL("feedback")));
-  }
-
-#if defined(OS_CHROMEOS)
-  if (!skip_session_components) {
 #if defined(KIOSK_NEXT)
     if (base::FeatureList::IsEnabled(ash::features::kKioskNextShell)) {
-      Add(IDR_KIOSK_NEXT_HOME_MANIFEST,
-          base::FilePath(FILE_PATH_LITERAL("chromeos/kiosk_next_home")));
+      // Always install KioskNextHome for testing if the feature is enabled.
+      if (enable_background_extensions_during_testing ||
+          profile_->GetPrefs()->GetBoolean(
+              ash::prefs::kKioskNextShellEnabled)) {
+        Add(IDR_KIOSK_NEXT_HOME_MANIFEST,
+            base::FilePath(FILE_PATH_LITERAL("chromeos/kiosk_next_home")));
+      }
     }
 #endif  // defined(KIOSK_NEXT)
 
@@ -579,8 +578,8 @@
 
     Add(IDR_ARC_SUPPORT_MANIFEST,
         base::FilePath(FILE_PATH_LITERAL("chromeos/arc_support")));
-  }
 #endif  // defined(OS_CHROMEOS)
+  }
 
 #if defined(GOOGLE_CHROME_BUILD)
 #if !defined(OS_CHROMEOS)  // http://crbug.com/314799
diff --git a/chrome/browser/extensions/component_loader.h b/chrome/browser/extensions/component_loader.h
index 3f8b640..d45b98a 100644
--- a/chrome/browser/extensions/component_loader.h
+++ b/chrome/browser/extensions/component_loader.h
@@ -22,7 +22,6 @@
 #include "build/build_config.h"
 #include "chrome/common/buildflags.h"
 
-class PrefService;
 class Profile;
 
 namespace extensions {
@@ -34,8 +33,6 @@
 class ComponentLoader {
  public:
   ComponentLoader(ExtensionServiceInterface* extension_service,
-                  PrefService* prefs,
-                  PrefService* local_state,
                   Profile* browser_context);
   virtual ~ComponentLoader();
 
@@ -215,8 +212,6 @@
   void FinishLoadSpeechSynthesisExtension(const char* extension_id);
 #endif
 
-  PrefService* profile_prefs_;
-  PrefService* local_state_;
   Profile* profile_;
 
   ExtensionServiceInterface* extension_service_;
diff --git a/chrome/browser/extensions/component_loader_unittest.cc b/chrome/browser/extensions/component_loader_unittest.cc
index 5c823c7..656efc3 100644
--- a/chrome/browser/extensions/component_loader_unittest.cc
+++ b/chrome/browser/extensions/component_loader_unittest.cc
@@ -8,7 +8,6 @@
 
 #include <string>
 
-#include "ash/public/cpp/ash_pref_names.h"
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/path_service.h"
@@ -17,9 +16,6 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/testing_profile.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/constants.h"
@@ -80,12 +76,8 @@
 class ComponentLoaderTest : public testing::Test {
  public:
   ComponentLoaderTest()
-      // Note: we pass the same pref service here, to stand in for both
-      // user prefs and local state.
       : extension_service_(&profile_),
         component_loader_(&extension_service_,
-                          &prefs_,
-                          &local_state_,
                           &profile_) {
     component_loader_.set_ignore_whitelist_for_testing(true);
   }
@@ -101,20 +93,12 @@
     ASSERT_TRUE(base::ReadFileToString(
         extension_path_.Append(kManifestFilename),
         &manifest_contents_));
-
-    // Register the local state prefs.
-#if defined(OS_CHROMEOS)
-    local_state_.registry()->RegisterBooleanPref(
-        ash::prefs::kAccessibilitySpokenFeedbackEnabled, false);
-#endif
   }
 
  protected:
   content::TestBrowserThreadBundle thread_bundle_;
   TestingProfile profile_;
   MockExtensionService extension_service_;
-  sync_preferences::TestingPrefServiceSyncable prefs_;
-  TestingPrefServiceSimple local_state_;
   ComponentLoader component_loader_;
 
   // The root directory of the text extension.
diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc
index 9d75c92..f8981cc 100644
--- a/chrome/browser/extensions/extension_apitest.cc
+++ b/chrome/browser/extensions/extension_apitest.cc
@@ -261,6 +261,11 @@
       }
       if (flags & kFlagLoadForLoginScreen)
         browser_test_flags |= ExtensionBrowserTest::kFlagLoadForLoginScreen;
+      if (flags & kFlagRunAsServiceWorkerBasedExtension) {
+        browser_test_flags |=
+            ExtensionBrowserTest::kFlagRunAsServiceWorkerBasedExtension;
+      }
+
       extension = LoadExtensionWithFlags(extension_path, browser_test_flags);
     }
     if (!extension) {
diff --git a/chrome/browser/extensions/extension_apitest.h b/chrome/browser/extensions/extension_apitest.h
index 90d4d8c..a72b420 100644
--- a/chrome/browser/extensions/extension_apitest.h
+++ b/chrome/browser/extensions/extension_apitest.h
@@ -66,6 +66,10 @@
     // usually provided for force-installed extension on the login screen. This
     // also sets the location to EXTERNAL_POLICY.
     kFlagLoadForLoginScreen = 1 << 8,
+
+    // Load the event page extension as a Service Worker based background
+    // extension.
+    kFlagRunAsServiceWorkerBasedExtension = 1 << 9,
   };
 
   ExtensionApiTest();
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index 1189ed4..5a63e5a 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -16,6 +16,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -67,8 +68,12 @@
 #include "extensions/browser/notification_types.h"
 #include "extensions/browser/test_extension_registry_observer.h"
 #include "extensions/browser/uninstall_reason.h"
+#include "extensions/common/constants.h"
 #include "extensions/common/extension_set.h"
+#include "extensions/common/file_test_util.h"
+#include "extensions/common/file_util.h"
 #include "extensions/common/switches.h"
+#include "extensions/common/value_builder.h"
 #include "net/url_request/url_request_file_job.h"
 
 #if defined(OS_CHROMEOS)
@@ -237,7 +242,12 @@
 
 const Extension* ExtensionBrowserTest::LoadExtensionWithFlags(
     const base::FilePath& path, int flags) {
-  return LoadExtensionWithInstallParam(path, flags, std::string());
+  base::FilePath extension_path = path;
+  if (flags & kFlagRunAsServiceWorkerBasedExtension) {
+    if (!CreateServiceWorkerBasedExtension(path, &extension_path))
+      return nullptr;
+  }
+  return LoadExtensionWithInstallParam(extension_path, flags, std::string());
 }
 
 const Extension* ExtensionBrowserTest::LoadExtensionWithInstallParam(
@@ -262,6 +272,123 @@
   return extension.get();
 }
 
+bool ExtensionBrowserTest::CreateServiceWorkerBasedExtension(
+    const base::FilePath& path,
+    base::FilePath* out_path) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+
+  // This dir will contain all files for the Service Worker based extension.
+  base::FilePath temp_extension_container;
+  if (!base::CreateTemporaryDirInDir(temp_dir_.GetPath(),
+                                     base::FilePath::StringType(),
+                                     &temp_extension_container)) {
+    ADD_FAILURE() << "Could not create temporary dir for test under "
+                  << temp_dir_.GetPath();
+    return false;
+  }
+
+  // Copy all files from test dir to temp dir.
+  if (!base::CopyDirectory(path, temp_extension_container,
+                           true /* recursive */)) {
+    ADD_FAILURE() << path.value() << " could not be copied to "
+                  << temp_extension_container.value();
+    return false;
+  }
+
+  const base::FilePath extension_root =
+      temp_extension_container.Append(path.BaseName());
+
+  std::string error;
+  std::unique_ptr<base::DictionaryValue> manifest_dict =
+      file_util::LoadManifest(extension_root, &error);
+  if (!manifest_dict) {
+    ADD_FAILURE() << path.value() << " could not load manifest: " << error;
+    return false;
+  }
+
+  // Retrieve the value of the "background" key and verify that it is
+  // non-persistent and specifies JS files.
+  // Persistent background pages or background pages that specify HTML files
+  // are not supported.
+  base::Value* background_dict =
+      manifest_dict->FindKeyOfType("background", base::Value::Type::DICTIONARY);
+  if (!background_dict) {
+    ADD_FAILURE() << path.value()
+                  << " 'background' key not found in manifest.json";
+    return false;
+  }
+  {
+    base::Value* background_persistent = background_dict->FindKeyOfType(
+        "persistent", base::Value::Type::BOOLEAN);
+    if (!background_persistent || background_persistent->GetBool()) {
+      ADD_FAILURE() << path.value()
+                    << ": Only event pages can be loaded as SW extension.";
+      return false;
+    }
+  }
+  base::Value* background_scripts_list =
+      background_dict->FindKeyOfType("scripts", base::Value::Type::LIST);
+  if (!background_scripts_list) {
+    ADD_FAILURE() << path.value()
+                  << ": Only event pages with JS script(s) can be loaded "
+                     "as SW extension.";
+    return false;
+  }
+
+  // Number of JS scripts must be > 1.
+  base::Value::ListStorage& scripts_list = background_scripts_list->GetList();
+  if (scripts_list.size() < 1) {
+    ADD_FAILURE() << path.value()
+                  << ": Only event pages with JS script(s) can be loaded "
+                     " as SW extension.";
+    return false;
+  }
+
+  // Generate combined script as Service Worker script using importScripts().
+  constexpr const char kGeneratedSWFileName[] = "generated_service_worker__.js";
+
+  std::vector<std::string> script_filenames;
+  for (const base::Value& script : scripts_list)
+    script_filenames.push_back(base::StrCat({"'", script.GetString(), "'"}));
+
+  base::FilePath combined_script_filepath =
+      extension_root.AppendASCII(kGeneratedSWFileName);
+  // Collision with generated script filename.
+  if (base::PathExists(combined_script_filepath)) {
+    ADD_FAILURE() << combined_script_filepath.value()
+                  << " already exists, make sure " << path.value()
+                  << " does not contained file named " << kGeneratedSWFileName;
+    return false;
+  }
+  std::string generated_sw_script_content = base::StringPrintf(
+      "importScripts(%s);", base::JoinString(script_filenames, ",").c_str());
+  if (!file_test_util::WriteFile(combined_script_filepath,
+                                 generated_sw_script_content)) {
+    ADD_FAILURE() << "Could not write combined Service Worker script to: "
+                  << combined_script_filepath.value();
+    return false;
+  }
+
+  // Remove the existing background specification and replace it with a service
+  // worker.
+  background_dict->RemoveKey("persistent");
+  background_dict->RemoveKey("scripts");
+  background_dict->SetStringPath("service_worker", kGeneratedSWFileName);
+
+  // Write out manifest.json.
+  DictionaryBuilder manifest_builder(*manifest_dict);
+  std::string manifest_contents = manifest_builder.ToJSON();
+  base::FilePath manifest_path = extension_root.Append(kManifestFilename);
+  if (!file_test_util::WriteFile(manifest_path, manifest_contents)) {
+    ADD_FAILURE() << "Could not write manifest file to "
+                  << manifest_path.value();
+    return false;
+  }
+
+  *out_path = extension_root;
+  return true;
+}
+
 const Extension* ExtensionBrowserTest::LoadExtensionAsComponentWithManifest(
     const base::FilePath& path,
     const base::FilePath::CharType* manifest_relative_path) {
diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h
index 714adf1..0ab486d 100644
--- a/chrome/browser/extensions/extension_browsertest.h
+++ b/chrome/browser/extensions/extension_browsertest.h
@@ -66,6 +66,9 @@
     // Pass the FOR_LOGIN_SCREEN flag when loading the extension. This flag is
     // usually provided for force-installed extension on the login screen.
     kFlagLoadForLoginScreen = 1 << 4,
+
+    // Load the provided extension as Service Worker based extension.
+    kFlagRunAsServiceWorkerBasedExtension = 1 << 5,
   };
 
   ExtensionBrowserTest();
@@ -121,6 +124,16 @@
       int flags,
       const std::string& install_param);
 
+  // Converts an extension from |path| to a Service Worker based extension and
+  // returns true on success.
+  // If successful, |out_path| contains path of the converted extension.
+  //
+  // NOTE: The conversion works only for extensions with background.scripts and
+  // background.persistent = false; persistent background pages and
+  // background.page are not supported.
+  bool CreateServiceWorkerBasedExtension(const base::FilePath& path,
+                                         base::FilePath* out_path);
+
   // Loads unpacked extension from |path| with manifest |manifest_relative_path|
   // and imitates that it is a component extension.
   // |manifest_relative_path| is relative to |path|.
diff --git a/chrome/browser/extensions/extension_keybinding_registry.cc b/chrome/browser/extensions/extension_keybinding_registry.cc
index 940eec2..e8323b04 100644
--- a/chrome/browser/extensions/extension_keybinding_registry.cc
+++ b/chrome/browser/extensions/extension_keybinding_registry.cc
@@ -20,7 +20,7 @@
 #include "extensions/common/manifest_constants.h"
 
 #if defined(OS_CHROMEOS)
-#include "chrome/browser/ui/ash/media_client.h"
+#include "chrome/browser/ui/ash/media_client_impl.h"
 #endif
 
 namespace {
@@ -109,7 +109,8 @@
       media_keys_listener_manager->EnableInternalMediaKeyHandling();
     } else {
 #if defined(OS_CHROMEOS)
-      MediaClient::Get()->DisableCustomMediaKeyHandler(browser_context_, this);
+      MediaClientImpl::Get()->DisableCustomMediaKeyHandler(browser_context_,
+                                                           this);
 #endif
     }
   }
@@ -195,7 +196,8 @@
       media_keys_listener_manager->DisableInternalMediaKeyHandling();
     } else {
 #if defined(OS_CHROMEOS)
-      MediaClient::Get()->EnableCustomMediaKeyHandler(browser_context_, this);
+      MediaClientImpl::Get()->EnableCustomMediaKeyHandler(browser_context_,
+                                                          this);
 #endif
     }
   }
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 38d9a21..3c6a7a7 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -32,7 +32,6 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/api/content_settings/content_settings_custom_extension_provider.h"
 #include "chrome/browser/extensions/api/content_settings/content_settings_service.h"
@@ -339,8 +338,7 @@
                    profile)));
   }
 
-  component_loader_.reset(new ComponentLoader(
-      this, profile->GetPrefs(), g_browser_process->local_state(), profile));
+  component_loader_ = std::make_unique<ComponentLoader>(this, profile);
 
   if (extensions_enabled_) {
     ExternalProviderImpl::CreateExternalProviders(
diff --git a/chrome/browser/extensions/extension_tabs_apitest.cc b/chrome/browser/extensions/extension_tabs_apitest.cc
index b0eaaa2..452e67b 100644
--- a/chrome/browser/extensions/extension_tabs_apitest.cc
+++ b/chrome/browser/extensions/extension_tabs_apitest.cc
@@ -221,6 +221,10 @@
   ASSERT_TRUE(RunExtensionTest("tabs/on_created")) << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(ExtensionApiTabTest, LazyBackgroundTabsOnCreated) {
+  ASSERT_TRUE(RunExtensionTest("tabs/lazy_background_on_created")) << message_;
+}
+
 IN_PROC_BROWSER_TEST_F(ExtensionApiTabTest, TabsOnUpdated) {
   ASSERT_TRUE(RunExtensionTest("tabs/on_updated")) << message_;
 }
diff --git a/chrome/browser/extensions/service_worker_apitest.cc b/chrome/browser/extensions/service_worker_apitest.cc
index b92e752..4b47ee65 100644
--- a/chrome/browser/extensions/service_worker_apitest.cc
+++ b/chrome/browser/extensions/service_worker_apitest.cc
@@ -1794,6 +1794,12 @@
   EXPECT_TRUE(moved_tab_listener.WaitUntilSatisfied());
 }
 
+IN_PROC_BROWSER_TEST_F(ServiceWorkerBasedBackgroundTest, TabsOnCreated) {
+  ASSERT_TRUE(RunExtensionTestWithFlags("tabs/lazy_background_on_created",
+                                        kFlagRunAsServiceWorkerBasedExtension))
+      << message_;
+}
+
 // Tests that console messages logged by extension service workers, both via
 // the typical console.* methods and via our custom bindings console, are
 // passed through the normal ServiceWorker console messaging and are
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 46ab16d..4272a99 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -148,6 +148,11 @@
     "expiry_milestone": 76
   },
   {
+    "name": "arc-usb-storage-ui",
+    "owners": [ "fukino" ],
+    "expiry_milestone": 78
+  },
+  {
     "name": "arc-vpn",
     "owners": [ "cros-networking@google.com" ],
     "expiry_milestone": 76
@@ -1752,7 +1757,7 @@
   },
   {
     "name": "enable-virtual-keyboard",
-    "owners": [ "//ash/keyboard/ui/OWNERS" ],
+    "owners": [ "//ash/keyboard/OWNERS" ],
     // Useful for debugging the virtual keyboard on non-tablet devices.
     "expiry_milestone": -1
   },
@@ -1992,7 +1997,7 @@
   {
     "name": "file-manager-feedback-panel",
     "owners": [ "adanilo" ],
-    "expiry_milestone": 77
+    "expiry_milestone": 78
   },
   {
     // See https://crbug.com/904630, offical builds only.
@@ -2082,6 +2087,12 @@
     "expiry_milestone": 76
   },
   {
+    "name": "gesture-properties-dbus-service",
+    "owners": [ "hcutts", "chromeos-tango@google.com" ],
+    // Used by developers for debugging and input device tuning.
+    "expiry_milestone": -1
+  },
+  {
     "name": "google-password-manager",
     "owners": [ "ioanap", "jdoerrie" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag-never-expire-list.json b/chrome/browser/flag-never-expire-list.json
index 7122317..d331d6c 100644
--- a/chrome/browser/flag-never-expire-list.json
+++ b/chrome/browser/flag-never-expire-list.json
@@ -59,6 +59,7 @@
   "force-text-direction",
   "force-ui-direction",
   "force-update-menu-type",
+  "gesture-properties-dbus-service",
   "ignore-gpu-blacklist",
   "ignore-previews-blocklist",
   "in-product-help-demo-mode-choice",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 8929091..041dfc1b 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2826,6 +2826,10 @@
 const char kArcUsbHostDescription[] =
     "Allow Android apps to use USB host feature on ChromeOS devices.";
 
+const char kArcUsbStorageUIName[] = "Enable ARC USB Storage UI";
+const char kArcUsbStorageUIDescription[] =
+    "Enable experimental UI for controlling ARC access to USB storage devices.";
+
 const char kArcVpnName[] = "Enable ARC VPN integration";
 const char kArcVpnDescription[] =
     "Allow Android VPN clients to tunnel Chrome traffic.";
@@ -3009,6 +3013,12 @@
     "If enabled and the device supports ARC, the user will be asked to update "
     "the encryption of user data when the user signs in.";
 
+const char kEnableGesturePropertiesDBusServiceName[] =
+    "Enable gesture properties D-Bus service";
+const char kEnableGesturePropertiesDBusServiceDescription[] =
+    "Enable a D-Bus service for accessing gesture properties, which are used "
+    "to configure input devices.";
+
 const char kEnableGoogleAssistantName[] = "Enable Google Assistant";
 const char kEnableGoogleAssistantDescription[] =
     "Enable an experimental Assistant implementation that will work on all "
@@ -3067,7 +3077,7 @@
     "user of text styling.";
 
 const char kFileManagerFeedbackPanelDescription[] =
-    "Enable new feedback panel in the Files app.";
+    "Enable feedback panel in the Files app.";
 const char kFileManagerFeedbackPanelName[] = "Files App. feedback panel";
 
 const char kFileManagerPiexWasmName[] = "Enable FilesApp piex-wasm module";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 0a3071f..76aef53 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1681,6 +1681,9 @@
 extern const char kArcUsbHostName[];
 extern const char kArcUsbHostDescription[];
 
+extern const char kArcUsbStorageUIName[];
+extern const char kArcUsbStorageUIDescription[];
+
 extern const char kArcVpnName[];
 extern const char kArcVpnDescription[];
 
@@ -1798,6 +1801,9 @@
 extern const char kEnableEncryptionMigrationName[];
 extern const char kEnableEncryptionMigrationDescription[];
 
+extern const char kEnableGesturePropertiesDBusServiceName[];
+extern const char kEnableGesturePropertiesDBusServiceDescription[];
+
 extern const char kEnableGoogleAssistantName[];
 extern const char kEnableGoogleAssistantDescription[];
 
diff --git a/chrome/browser/history/chrome_history_client.cc b/chrome/browser/history/chrome_history_client.cc
index 808f5cf..736a370 100644
--- a/chrome/browser/history/chrome_history_client.cc
+++ b/chrome/browser/history/chrome_history_client.cc
@@ -78,7 +78,7 @@
 void ChromeHistoryClient::BookmarkNodeRemoved(
     bookmarks::BookmarkModel* bookmark_model,
     const bookmarks::BookmarkNode* parent,
-    int old_index,
+    size_t old_index,
     const bookmarks::BookmarkNode* node,
     const std::set<GURL>& removed_urls) {
   BaseBookmarkModelObserver::BookmarkNodeRemoved(bookmark_model, parent,
diff --git a/chrome/browser/history/chrome_history_client.h b/chrome/browser/history/chrome_history_client.h
index 775c21b..e6ac564c 100644
--- a/chrome/browser/history/chrome_history_client.h
+++ b/chrome/browser/history/chrome_history_client.h
@@ -46,7 +46,7 @@
   void BookmarkModelBeingDeleted(bookmarks::BookmarkModel* model) override;
   void BookmarkNodeRemoved(bookmarks::BookmarkModel* bookmark_model,
                            const bookmarks::BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const bookmarks::BookmarkNode* node,
                            const std::set<GURL>& removed_url) override;
   void BookmarkAllUserNodesRemoved(bookmarks::BookmarkModel* bookmark_model,
diff --git a/chrome/browser/importer/profile_writer.cc b/chrome/browser/importer/profile_writer.cc
index b5f31b4..275d348 100644
--- a/chrome/browser/importer/profile_writer.cc
+++ b/chrome/browser/importer/profile_writer.cc
@@ -170,9 +170,8 @@
       if (!top_level_folder) {
         base::string16 name =
             GenerateUniqueFolderName(model,top_level_folder_name);
-        top_level_folder = model->AddFolder(bookmark_bar,
-                                            bookmark_bar->child_count(),
-                                            name);
+        top_level_folder = model->AddFolder(
+            bookmark_bar, bookmark_bar->children().size(), name);
       }
       parent = top_level_folder;
     }
@@ -197,21 +196,20 @@
           break;
         }
       }
-      if (!child)
-        child = model->AddFolder(parent, parent->child_count(), *folder_name);
+      if (!child) {
+        child =
+            model->AddFolder(parent, parent->children().size(), *folder_name);
+      }
       parent = child;
     }
 
     folders_added_to.insert(parent);
     if (bookmark->is_folder) {
-      model->AddFolder(parent, parent->child_count(), bookmark->title);
+      model->AddFolder(parent, parent->children().size(), bookmark->title);
     } else {
-      model->AddURLWithCreationTimeAndMetaInfo(parent,
-                                               parent->child_count(),
-                                               bookmark->title,
-                                               bookmark->url,
-                                               bookmark->creation_time,
-                                               NULL);
+      model->AddURLWithCreationTimeAndMetaInfo(
+          parent, parent->children().size(), bookmark->title, bookmark->url,
+          bookmark->creation_time, NULL);
     }
   }
 
diff --git a/chrome/browser/media/capture_access_handler_base.cc b/chrome/browser/media/capture_access_handler_base.cc
index 96969286..1f0d971 100644
--- a/chrome/browser/media/capture_access_handler_base.cc
+++ b/chrome/browser/media/capture_access_handler_base.cc
@@ -71,13 +71,14 @@
     int render_process_id,
     int render_frame_id,
     int page_request_id,
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     content::MediaRequestState state) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if ((stream_type != blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE) &&
-      (stream_type != blink::MEDIA_GUM_TAB_VIDEO_CAPTURE) &&
-      (stream_type != blink::MEDIA_DISPLAY_VIDEO_CAPTURE) &&
-      (stream_type != blink::MEDIA_DISPLAY_AUDIO_CAPTURE)) {
+  if ((stream_type !=
+       blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE) &&
+      (stream_type != blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE) &&
+      (stream_type != blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE) &&
+      (stream_type != blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE)) {
     return;
   }
 
diff --git a/chrome/browser/media/capture_access_handler_base.h b/chrome/browser/media/capture_access_handler_base.h
index 0477b8f5..aa67fb6f 100644
--- a/chrome/browser/media/capture_access_handler_base.h
+++ b/chrome/browser/media/capture_access_handler_base.h
@@ -23,7 +23,7 @@
   void UpdateMediaRequestState(int render_process_id,
                                int render_frame_id,
                                int page_request_id,
-                               blink::MediaStreamType stream_type,
+                               blink::mojom::MediaStreamType stream_type,
                                content::MediaRequestState state) override;
 
   // Returns true if there is any ongoing insecured capturing. Returns false
diff --git a/chrome/browser/media/cast_mirroring_service_host.cc b/chrome/browser/media/cast_mirroring_service_host.cc
index 9d6ee49..151e886 100644
--- a/chrome/browser/media/cast_mirroring_service_host.cc
+++ b/chrome/browser/media/cast_mirroring_service_host.cc
@@ -45,7 +45,7 @@
 namespace {
 
 void CreateVideoCaptureHostOnIO(const std::string& device_id,
-                                blink::MediaStreamType type,
+                                blink::mojom::MediaStreamType type,
                                 media::mojom::VideoCaptureHostRequest request) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   scoped_refptr<base::SingleThreadTaskRunner> device_task_runner =
@@ -62,20 +62,20 @@
       std::move(request));
 }
 
-blink::MediaStreamType ConvertVideoStreamType(
+blink::mojom::MediaStreamType ConvertVideoStreamType(
     content::DesktopMediaID::Type type) {
   switch (type) {
     case content::DesktopMediaID::TYPE_NONE:
-      return blink::MediaStreamType::MEDIA_NO_SERVICE;
+      return blink::mojom::MediaStreamType::NO_SERVICE;
     case content::DesktopMediaID::TYPE_WEB_CONTENTS:
-      return blink::MediaStreamType::MEDIA_GUM_TAB_VIDEO_CAPTURE;
+      return blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE;
     case content::DesktopMediaID::TYPE_SCREEN:
     case content::DesktopMediaID::TYPE_WINDOW:
-      return blink::MediaStreamType::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE;
+      return blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE;
   }
 
   // To suppress compiler warning on Windows.
-  return blink::MediaStreamType::MEDIA_NO_SERVICE;
+  return blink::mojom::MediaStreamType::NO_SERVICE;
 }
 
 // Get the content::WebContents associated with the given |id|.
diff --git a/chrome/browser/media/chromeos_login_media_access_handler.cc b/chrome/browser/media/chromeos_login_media_access_handler.cc
index 8fb8636f..f1728f7 100644
--- a/chrome/browser/media/chromeos_login_media_access_handler.cc
+++ b/chrome/browser/media/chromeos_login_media_access_handler.cc
@@ -23,7 +23,7 @@
 
 bool ChromeOSLoginMediaAccessHandler::SupportsStreamType(
     content::WebContents* web_contents,
-    const blink::MediaStreamType type,
+    const blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
   if (!web_contents)
     return false;
@@ -34,9 +34,9 @@
 bool ChromeOSLoginMediaAccessHandler::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
-  if (type != blink::MEDIA_DEVICE_VIDEO_CAPTURE)
+  if (type != blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE)
     return false;
 
   // When creating new user (including supervised user), we must be able to use
@@ -82,12 +82,13 @@
     const extensions::Extension* extension) {
   bool audio_allowed = false;
   bool video_allowed =
-      request.video_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE &&
+      request.video_type ==
+          blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE &&
       CheckMediaAccessPermission(
           content::RenderFrameHost::FromID(request.render_process_id,
                                            request.render_frame_id),
-          request.security_origin, blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-          extension);
+          request.security_origin,
+          blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, extension);
 
   CheckDevicesAndRunCallback(web_contents, request, std::move(callback),
                              audio_allowed, video_allowed);
diff --git a/chrome/browser/media/chromeos_login_media_access_handler.h b/chrome/browser/media/chromeos_login_media_access_handler.h
index e1d17f0..5c1dc19 100644
--- a/chrome/browser/media/chromeos_login_media_access_handler.h
+++ b/chrome/browser/media/chromeos_login_media_access_handler.h
@@ -16,12 +16,12 @@
 
   // MediaAccessHandler implementation.
   bool SupportsStreamType(content::WebContents* web_contents,
-                          const blink::MediaStreamType type,
+                          const blink::mojom::MediaStreamType type,
                           const extensions::Extension* extension) override;
   bool CheckMediaAccessPermission(
       content::RenderFrameHost* render_frame_host,
       const GURL& security_origin,
-      blink::MediaStreamType type,
+      blink::mojom::MediaStreamType type,
       const extensions::Extension* extension) override;
   void HandleRequest(content::WebContents* web_contents,
                      const content::MediaStreamRequest& request,
diff --git a/chrome/browser/media/extension_media_access_handler.cc b/chrome/browser/media/extension_media_access_handler.cc
index 767a33f2..3832cdb 100644
--- a/chrome/browser/media/extension_media_access_handler.cc
+++ b/chrome/browser/media/extension_media_access_handler.cc
@@ -46,22 +46,22 @@
 
 bool ExtensionMediaAccessHandler::SupportsStreamType(
     content::WebContents* web_contents,
-    const blink::MediaStreamType type,
+    const blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
   return extension &&
          (extension->is_platform_app() ||
           IsMediaRequestWhitelistedForExtension(extension)) &&
-         (type == blink::MEDIA_DEVICE_AUDIO_CAPTURE ||
-          type == blink::MEDIA_DEVICE_VIDEO_CAPTURE);
+         (type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE ||
+          type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
 }
 
 bool ExtensionMediaAccessHandler::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
   return extension->permissions_data()->HasAPIPermission(
-      type == blink::MEDIA_DEVICE_AUDIO_CAPTURE
+      type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE
           ? extensions::APIPermission::kAudioCapture
           : extensions::APIPermission::kVideoCapture);
 }
@@ -74,13 +74,15 @@
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
   bool audio_allowed =
-      request.audio_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE &&
+      request.audio_type ==
+          blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE &&
       extension->permissions_data()->HasAPIPermission(
           extensions::APIPermission::kAudioCapture) &&
       GetDevicePolicy(profile, extension->url(), prefs::kAudioCaptureAllowed,
                       prefs::kAudioCaptureAllowedUrls) != ALWAYS_DENY;
   bool video_allowed =
-      request.video_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE &&
+      request.video_type ==
+          blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE &&
       extension->permissions_data()->HasAPIPermission(
           extensions::APIPermission::kVideoCapture) &&
       GetDevicePolicy(profile, extension->url(), prefs::kVideoCaptureAllowed,
diff --git a/chrome/browser/media/extension_media_access_handler.h b/chrome/browser/media/extension_media_access_handler.h
index 9241fcf..21f807e 100644
--- a/chrome/browser/media/extension_media_access_handler.h
+++ b/chrome/browser/media/extension_media_access_handler.h
@@ -15,12 +15,12 @@
 
   // MediaAccessHandler implementation.
   bool SupportsStreamType(content::WebContents* web_contents,
-                          const blink::MediaStreamType type,
+                          const blink::mojom::MediaStreamType type,
                           const extensions::Extension* extension) override;
   bool CheckMediaAccessPermission(
       content::RenderFrameHost* render_frame_host,
       const GURL& security_origin,
-      blink::MediaStreamType type,
+      blink::mojom::MediaStreamType type,
       const extensions::Extension* extension) override;
   void HandleRequest(content::WebContents* web_contents,
                      const content::MediaStreamRequest& request,
diff --git a/chrome/browser/media/media_access_handler.h b/chrome/browser/media/media_access_handler.h
index 142ffb88..d47d291 100644
--- a/chrome/browser/media/media_access_handler.h
+++ b/chrome/browser/media/media_access_handler.h
@@ -28,7 +28,7 @@
 
   // Check if the media stream type is supported by MediaAccessHandler.
   virtual bool SupportsStreamType(content::WebContents* web_contents,
-                                  const blink::MediaStreamType type,
+                                  const blink::mojom::MediaStreamType type,
                                   const extensions::Extension* extension) = 0;
 
   // Check media access permission. |extension| is set to NULL if request was
@@ -36,7 +36,7 @@
   virtual bool CheckMediaAccessPermission(
       content::RenderFrameHost* render_frame_host,
       const GURL& security_origin,
-      blink::MediaStreamType type,
+      blink::mojom::MediaStreamType type,
       const extensions::Extension* extension) = 0;
 
   // Process media access requests. |extension| is set to NULL if request was
@@ -47,11 +47,12 @@
                              const extensions::Extension* extension) = 0;
 
   // Update media request state. Called on UI thread.
-  virtual void UpdateMediaRequestState(int render_process_id,
-                                       int render_frame_id,
-                                       int page_request_id,
-                                       blink::MediaStreamType stream_type,
-                                       content::MediaRequestState state) {}
+  virtual void UpdateMediaRequestState(
+      int render_process_id,
+      int render_frame_id,
+      int page_request_id,
+      blink::mojom::MediaStreamType stream_type,
+      content::MediaRequestState state) {}
 
   // Return true if there is any ongoing insecured capturing. The capturing is
   // deemed secure if all connected video sinks are reported secure and the
diff --git a/chrome/browser/media/offscreen_tab.cc b/chrome/browser/media/offscreen_tab.cc
index e9107afc..ecf4ec49 100644
--- a/chrome/browser/media/offscreen_tab.cc
+++ b/chrome/browser/media/offscreen_tab.cc
@@ -354,11 +354,11 @@
 bool OffscreenTab::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   DCHECK_EQ(offscreen_tab_web_contents_.get(),
             content::WebContents::FromRenderFrameHost(render_frame_host));
-  return type == blink::MEDIA_GUM_TAB_AUDIO_CAPTURE ||
-         type == blink::MEDIA_GUM_TAB_VIDEO_CAPTURE;
+  return type == blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE ||
+         type == blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE;
 }
 
 void OffscreenTab::DidShowFullscreenWidget() {
diff --git a/chrome/browser/media/offscreen_tab.h b/chrome/browser/media/offscreen_tab.h
index 85b8853..32a62ef 100644
--- a/chrome/browser/media/offscreen_tab.h
+++ b/chrome/browser/media/offscreen_tab.h
@@ -130,7 +130,7 @@
       content::MediaResponseCallback callback) final;
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type) final;
+                                  blink::mojom::MediaStreamType type) final;
 
   // content::WebContentsObserver overrides
   void DidShowFullscreenWidget() final;
diff --git a/chrome/browser/media/public_session_media_access_handler.cc b/chrome/browser/media/public_session_media_access_handler.cc
index 48a4048..db0c7f3a 100644
--- a/chrome/browser/media/public_session_media_access_handler.cc
+++ b/chrome/browser/media/public_session_media_access_handler.cc
@@ -24,7 +24,7 @@
 
 bool PublicSessionMediaAccessHandler::SupportsStreamType(
     content::WebContents* web_contents,
-    const blink::MediaStreamType type,
+    const blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
   return extension_media_access_handler_.SupportsStreamType(web_contents, type,
                                                             extension);
@@ -33,7 +33,7 @@
 bool PublicSessionMediaAccessHandler::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
   return extension_media_access_handler_.CheckMediaAccessPermission(
       render_frame_host, security_origin, type, extension);
@@ -60,9 +60,9 @@
                      std::move(callback), base::RetainedRef(extension)));
 
   extensions::PermissionIDSet requested_permissions;
-  if (request.audio_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE)
+  if (request.audio_type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE)
     requested_permissions.insert(extensions::APIPermission::kAudioCapture);
-  if (request.video_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE)
+  if (request.video_type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE)
     requested_permissions.insert(extensions::APIPermission::kVideoCapture);
 
   extensions::permission_helper::HandlePermissionRequest(
@@ -81,9 +81,9 @@
   // If the user denies audio or video capture, here it gets filtered out from
   // the request before being passed on to the actual implementation.
   if (!allowed_permissions.ContainsID(extensions::APIPermission::kAudioCapture))
-    request_copy.audio_type = blink::MEDIA_NO_SERVICE;
+    request_copy.audio_type = blink::mojom::MediaStreamType::NO_SERVICE;
   if (!allowed_permissions.ContainsID(extensions::APIPermission::kVideoCapture))
-    request_copy.video_type = blink::MEDIA_NO_SERVICE;
+    request_copy.video_type = blink::mojom::MediaStreamType::NO_SERVICE;
 
   // Pass the request through to the original class.
   extension_media_access_handler_.HandleRequest(web_contents, request_copy,
diff --git a/chrome/browser/media/public_session_media_access_handler.h b/chrome/browser/media/public_session_media_access_handler.h
index 616a3d8..28b8e70 100644
--- a/chrome/browser/media/public_session_media_access_handler.h
+++ b/chrome/browser/media/public_session_media_access_handler.h
@@ -32,12 +32,12 @@
 
   // MediaAccessHandler implementation.
   bool SupportsStreamType(content::WebContents* web_contents,
-                          const blink::MediaStreamType type,
+                          const blink::mojom::MediaStreamType type,
                           const extensions::Extension* extension) override;
   bool CheckMediaAccessPermission(
       content::RenderFrameHost* render_frame_host,
       const GURL& security_origin,
-      blink::MediaStreamType type,
+      blink::mojom::MediaStreamType type,
       const extensions::Extension* extension) override;
   void HandleRequest(content::WebContents* web_contents,
                      const content::MediaStreamRequest& request,
diff --git a/chrome/browser/media/public_session_tab_capture_access_handler.cc b/chrome/browser/media/public_session_tab_capture_access_handler.cc
index a5e4571..8e87fe7 100644
--- a/chrome/browser/media/public_session_tab_capture_access_handler.cc
+++ b/chrome/browser/media/public_session_tab_capture_access_handler.cc
@@ -23,7 +23,7 @@
 
 bool PublicSessionTabCaptureAccessHandler::SupportsStreamType(
     content::WebContents* web_contents,
-    const blink::MediaStreamType type,
+    const blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
   return tab_capture_access_handler_.SupportsStreamType(web_contents, type,
                                                         extension);
@@ -32,7 +32,7 @@
 bool PublicSessionTabCaptureAccessHandler::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
   return tab_capture_access_handler_.CheckMediaAccessPermission(
       render_frame_host, security_origin, type, extension);
@@ -46,8 +46,10 @@
   // This class handles requests for Public Sessions only, outside of them just
   // pass the request through to the original class.
   if (!profiles::ArePublicSessionRestrictionsEnabled() || !extension ||
-      (request.audio_type != blink::MEDIA_GUM_TAB_AUDIO_CAPTURE &&
-       request.video_type != blink::MEDIA_GUM_TAB_VIDEO_CAPTURE)) {
+      (request.audio_type !=
+           blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE &&
+       request.video_type !=
+           blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE)) {
     return tab_capture_access_handler_.HandleRequest(
         web_contents, request, std::move(callback), extension);
   }
@@ -75,8 +77,8 @@
   // If the user denied tab capture, here the request gets filtered out before
   // being passed on to the actual implementation.
   if (!allowed_permissions.ContainsID(extensions::APIPermission::kTabCapture)) {
-    request_copy.audio_type = blink::MEDIA_NO_SERVICE;
-    request_copy.video_type = blink::MEDIA_NO_SERVICE;
+    request_copy.audio_type = blink::mojom::MediaStreamType::NO_SERVICE;
+    request_copy.video_type = blink::mojom::MediaStreamType::NO_SERVICE;
   }
 
   // Pass the request through to the original class.
diff --git a/chrome/browser/media/public_session_tab_capture_access_handler.h b/chrome/browser/media/public_session_tab_capture_access_handler.h
index 5527447e..9d80791 100644
--- a/chrome/browser/media/public_session_tab_capture_access_handler.h
+++ b/chrome/browser/media/public_session_tab_capture_access_handler.h
@@ -31,12 +31,12 @@
 
   // MediaAccessHandler implementation.
   bool SupportsStreamType(content::WebContents* web_contents,
-                          const blink::MediaStreamType type,
+                          const blink::mojom::MediaStreamType type,
                           const extensions::Extension* extension) override;
   bool CheckMediaAccessPermission(
       content::RenderFrameHost* render_frame_host,
       const GURL& security_origin,
-      blink::MediaStreamType type,
+      blink::mojom::MediaStreamType type,
       const extensions::Extension* extension) override;
   void HandleRequest(content::WebContents* web_contents,
                      const content::MediaStreamRequest& request,
diff --git a/chrome/browser/media/router/event_page_request_manager_unittest.cc b/chrome/browser/media/router/event_page_request_manager_unittest.cc
index 3201b9c..89749f0 100644
--- a/chrome/browser/media/router/event_page_request_manager_unittest.cc
+++ b/chrome/browser/media/router/event_page_request_manager_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/macros.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "chrome/browser/media/router/mojo/media_router_mojo_metrics.h"
@@ -17,7 +18,6 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/process_manager.h"
 #include "extensions/browser/process_manager_factory.h"
-#include "media/base/gmock_callback_support.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/media/router/media_router_feature.cc b/chrome/browser/media/router/media_router_feature.cc
index 9c42d30..5d7b8ea0 100644
--- a/chrome/browser/media/router/media_router_feature.cc
+++ b/chrome/browser/media/router/media_router_feature.cc
@@ -42,6 +42,8 @@
 const base::Feature kCastMediaRouteProvider{"CastMediaRouteProvider",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kCastAllowAllIPsFeature{"CastAllowAllIPs",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
 #endif
 
 #if defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS)
@@ -67,7 +69,7 @@
   // The component extension cannot be loaded in guest sessions.
   // TODO(crbug.com/756243): Figure out why.
   return !Profile::FromBrowserContext(context)->IsGuestSession();
-#else  // !(defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS))
+#else   // !(defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS))
   return false;
 #endif  // defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS)
 }
@@ -84,9 +86,6 @@
                                PrefRegistry::PUBLIC);
 }
 
-const base::Feature kCastAllowAllIPsFeature{"CastAllowAllIPs",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
-
 bool GetCastAllowAllIPsPref(PrefService* pref_service) {
   auto* pref = pref_service->FindPreference(prefs::kMediaRouterCastAllowAllIPs);
 
diff --git a/chrome/browser/media/router/media_router_feature.h b/chrome/browser/media/router/media_router_feature.h
index a3a2c1a..5a65a39 100644
--- a/chrome/browser/media/router/media_router_feature.h
+++ b/chrome/browser/media/router/media_router_feature.h
@@ -21,6 +21,16 @@
 
 #if !defined(OS_ANDROID)
 
+extern const base::Feature kDialMediaRouteProvider;
+// TODO(crbug.com/969091): This feature is now enabled by default, and the flag
+// should be removed.
+extern const base::Feature kEnableCastDiscovery;
+extern const base::Feature kCastMediaRouteProvider;
+// If enabled, allows Media Router to connect to Cast devices on all IP
+// addresses, not just RFC1918/RFC4193 private addresses. Workaround for
+// https://crbug.com/813974.
+extern const base::Feature kCastAllowAllIPsFeature;
+
 namespace prefs {
 // Pref name for the enterprise policy for allowing Cast devices on all IPs.
 constexpr char kMediaRouterCastAllowAllIPs[] =
@@ -37,11 +47,6 @@
 // Registers Media Router related preferences with per-profile pref |registry|.
 void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
-// If enabled, allows Media Router to connect to Cast devices on all IP
-// addresses, not just RFC1918/RFC4193 private addresses. Workaround for
-// https://crbug.com/813974.
-extern const base::Feature kCastAllowAllIPsFeature;
-
 // Returns true if CastMediaSinkService can connect to Cast devices on
 // all IPs, as determined by local state |pref_service| / feature flag.
 bool GetCastAllowAllIPsPref(PrefService* pref_service);
@@ -51,10 +56,6 @@
 // randomly generated string and stored in |pref_service|.
 std::string GetReceiverIdHashToken(PrefService* pref_service);
 
-extern const base::Feature kEnableDialSinkQuery;
-extern const base::Feature kEnableCastDiscovery;
-extern const base::Feature kCastMediaRouteProvider;
-
 // Returns true if browser side DIAL Media Route Provider is enabled.
 bool DialMediaRouteProviderEnabled();
 
@@ -67,6 +68,8 @@
 
 // Returns true if the Views implementation of the Cast dialog should be used.
 // Returns false if the WebUI implementation should be used.
+// TODO(crbug.com/969098): The feature is now enabled by default. Remove this
+// function.
 bool ShouldUseViewsDialog();
 
 // Returns true if Mirroring Service should be used for mirroring.
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
index c2b07329..f5ef0c00 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -40,7 +41,6 @@
 #include "extensions/browser/process_manager.h"
 #include "extensions/browser/process_manager_factory.h"
 #include "extensions/common/extension.h"
-#include "media/base/gmock_callback_support.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
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 690e32b8..93faa12 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
@@ -454,8 +454,8 @@
       mock_callback;
   auto callback = mock_callback.Get();
   PresentationInfo connection(presentation_url1_, kPresentationId);
-  EXPECT_CALL(*router_, OnAddPresentationConnectionStateChangedCallbackInvoked(
-                            Equals(callback)));
+  EXPECT_CALL(*router_,
+              OnAddPresentationConnectionStateChangedCallbackInvoked(callback));
   delegate_impl_->ListenForConnectionStateChange(
       main_frame_process_id_, main_frame_routing_id_, connection, callback);
 }
diff --git a/chrome/browser/media/router/providers/cast/cast_media_route_provider_unittest.cc b/chrome/browser/media/router/providers/cast/cast_media_route_provider_unittest.cc
index 55ba7b05..fdf8fdb 100644
--- a/chrome/browser/media/router/providers/cast/cast_media_route_provider_unittest.cc
+++ b/chrome/browser/media/router/providers/cast/cast_media_route_provider_unittest.cc
@@ -22,6 +22,24 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+// TODO(crbug.com/969812): Fix memory leaks in tests and re-enable on LSAN.
+#ifdef LEAK_SANITIZER
+#define MAYBE_CreateRoute DISABLED_CreateRoute
+#define MAYBE_CreateRouteFailsInvalidSource \
+  DISABLED_CreateRouteFailsInvalidSource
+#define MAYBE_StartObservingMediaSinks DISABLED_StartObservingMediaSinks
+#define MAYBE_TerminateRoute DISABLED_TerminateRoute
+#define MAYBE_BroadcastRequest DISABLED_BroadcastRequest
+#define MAYBE_CreateRouteFailsInvalidSink DISABLED_CreateRouteFailsInvalidSink
+#else
+#define MAYBE_CreateRoute CreateRoute
+#define MAYBE_CreateRouteFailsInvalidSource CreateRouteFailsInvalidSource
+#define MAYBE_StartObservingMediaSinks StartObservingMediaSinks
+#define MAYBE_TerminateRoute TerminateRoute
+#define MAYBE_BroadcastRequest BroadcastRequest
+#define MAYBE_CreateRouteFailsInvalidSink CreateRouteFailsInvalidSink
+#endif
+
 using ::testing::_;
 
 namespace media_router {
@@ -122,7 +140,7 @@
   DISALLOW_COPY_AND_ASSIGN(CastMediaRouteProviderTest);
 };
 
-TEST_F(CastMediaRouteProviderTest, StartObservingMediaSinks) {
+TEST_F(CastMediaRouteProviderTest, MAYBE_StartObservingMediaSinks) {
   MediaSource::Id non_cast_source("not-a-cast-source:foo");
   EXPECT_CALL(app_discovery_service_, DoStartObservingMediaSinks(_)).Times(0);
   provider_->StartObservingMediaSinks(non_cast_source);
@@ -135,7 +153,7 @@
   EXPECT_TRUE(app_discovery_service_.callbacks().empty());
 }
 
-TEST_F(CastMediaRouteProviderTest, BroadcastRequest) {
+TEST_F(CastMediaRouteProviderTest, MAYBE_BroadcastRequest) {
   media_sink_service_.AddOrUpdateSink(CreateCastSink(1));
   media_sink_service_.AddOrUpdateSink(CreateCastSink(2));
   MediaSource::Id source_id(
@@ -153,7 +171,7 @@
   EXPECT_TRUE(app_discovery_service_.callbacks().empty());
 }
 
-TEST_F(CastMediaRouteProviderTest, CreateRouteFailsInvalidSink) {
+TEST_F(CastMediaRouteProviderTest, MAYBE_CreateRouteFailsInvalidSink) {
   // Sink does not exist.
   provider_->CreateRoute(
       kCastSource, "sinkId", kPresentationId, origin_, kTabId, kRouteTimeout,
@@ -163,7 +181,7 @@
                      RouteRequestResult::ResultCode::SINK_NOT_FOUND));
 }
 
-TEST_F(CastMediaRouteProviderTest, CreateRouteFailsInvalidSource) {
+TEST_F(CastMediaRouteProviderTest, MAYBE_CreateRouteFailsInvalidSource) {
   MediaSinkInternal sink = CreateCastSink(1);
   media_sink_service_.AddOrUpdateSink(sink);
 
@@ -175,7 +193,7 @@
                      RouteRequestResult::ResultCode::NO_SUPPORTED_PROVIDER));
 }
 
-TEST_F(CastMediaRouteProviderTest, CreateRoute) {
+TEST_F(CastMediaRouteProviderTest, MAYBE_CreateRoute) {
   MediaSinkInternal sink = CreateCastSink(1);
   media_sink_service_.AddOrUpdateSink(sink);
 
@@ -188,7 +206,7 @@
           base::Unretained(this)));
 }
 
-TEST_F(CastMediaRouteProviderTest, TerminateRoute) {
+TEST_F(CastMediaRouteProviderTest, MAYBE_TerminateRoute) {
   MediaSinkInternal sink = CreateCastSink(1);
   media_sink_service_.AddOrUpdateSink(sink);
 
diff --git a/chrome/browser/media/router/test/test_helper.h b/chrome/browser/media/router/test/test_helper.h
index eff3925..b4fe5b6 100644
--- a/chrome/browser/media/router/test/test_helper.h
+++ b/chrome/browser/media/router/test/test_helper.h
@@ -33,12 +33,6 @@
 
 namespace media_router {
 
-// Matcher for objects that uses Equals() member function for equality check.
-// Use only for comparing base::MockCallback.
-MATCHER_P(Equals, other, "") {
-  return arg.Equals(other);
-}
-
 // Matcher for IssueInfo title.
 MATCHER_P(IssueTitleEquals, title, "") {
   return arg.info().title == title;
diff --git a/chrome/browser/media/webrtc/desktop_capture_access_handler.cc b/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
index 6e826a4..9fe0127 100644
--- a/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
+++ b/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
@@ -148,7 +148,8 @@
   blink::MediaStreamDevices devices;
   std::unique_ptr<content::MediaStreamUI> ui;
 
-  DCHECK_EQ(request.video_type, blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE);
+  DCHECK_EQ(request.video_type,
+            blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE);
 
   UpdateExtensionTrusted(request, extension);
 
@@ -216,7 +217,7 @@
       if (extension)
         application_name = base::UTF8ToUTF16(extension->name());
       base::string16 confirmation_text = l10n_util::GetStringFUTF16(
-          request.audio_type == blink::MEDIA_NO_SERVICE
+          request.audio_type == blink::mojom::MediaStreamType::NO_SERVICE
               ? IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TEXT
               : IDS_MEDIA_SCREEN_AND_AUDIO_CAPTURE_CONFIRMATION_TEXT,
           application_name);
@@ -240,7 +241,8 @@
 #endif  // !defined(OS_CHROMEOS)
 
       bool capture_audio =
-          (request.audio_type == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE &&
+          (request.audio_type ==
+               blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE &&
            loopback_audio_supported);
 
       // Determine if the extension is required to display a notification.
@@ -249,10 +251,10 @@
 
       ui = GetDevicesForDesktopCapture(
           web_contents, &devices, screen_id,
-          blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
-          blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE, capture_audio,
-          request.disable_local_echo, display_notification, application_title,
-          application_title);
+          blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+          blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE,
+          capture_audio, request.disable_local_echo, display_notification,
+          application_title, application_title);
       DCHECK(!devices.empty());
     }
 
@@ -276,16 +278,16 @@
 
 bool DesktopCaptureAccessHandler::SupportsStreamType(
     content::WebContents* web_contents,
-    const blink::MediaStreamType type,
+    const blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
-  return type == blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE ||
-         type == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE;
+  return type == blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE ||
+         type == blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE;
 }
 
 bool DesktopCaptureAccessHandler::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
   return false;
 }
@@ -298,7 +300,8 @@
   blink::MediaStreamDevices devices;
   std::unique_ptr<content::MediaStreamUI> ui;
 
-  if (request.video_type != blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE) {
+  if (request.video_type !=
+      blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE) {
     std::move(callback).Run(
         devices, blink::mojom::MediaStreamRequestResult::INVALID_STATE,
         std::move(ui));
@@ -359,7 +362,8 @@
 
   // This value essentially from whether getUserMedia requests audio stream.
   const bool audio_requested =
-      request.audio_type == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE;
+      request.audio_type ==
+      blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE;
 
   // This value shows for a given capture type, whether the system or our code
   // can support audio sharing. Currently audio is only supported for screen and
@@ -381,8 +385,9 @@
       display_notification_ && ShouldDisplayNotification(extension);
 
   ui = GetDevicesForDesktopCapture(
-      web_contents, &devices, media_id, blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
-      blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE, capture_audio,
+      web_contents, &devices, media_id,
+      blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+      blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE, capture_audio,
       request.disable_local_echo, display_notification,
       GetApplicationTitle(web_contents, extension),
       GetApplicationTitle(web_contents, extension));
@@ -424,7 +429,7 @@
     int render_process_id,
     int render_frame_id,
     int page_request_id,
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     content::MediaRequestState state) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
@@ -461,8 +466,8 @@
       content::DesktopMediaID media_id(
           content::DesktopMediaID::TYPE_WEB_CONTENTS,
           content::DesktopMediaID::kNullId, web_contents_id);
-      media_id.audio_share =
-          pending_request.request.audio_type != blink::MEDIA_NO_SERVICE;
+      media_id.audio_share = pending_request.request.audio_type !=
+                             blink::mojom::MediaStreamType::NO_SERVICE;
       OnPickerDialogResults(web_contents, media_id);
       return;
     }
@@ -483,9 +488,10 @@
   picker_params.app_name =
       GetApplicationTitle(web_contents, pending_request.extension);
   picker_params.target_name = picker_params.app_name;
-  picker_params.request_audio =
-      (pending_request.request.audio_type == blink::MEDIA_NO_SERVICE) ? false
-                                                                      : true;
+  picker_params.request_audio = (pending_request.request.audio_type ==
+                                 blink::mojom::MediaStreamType::NO_SERVICE)
+                                    ? false
+                                    : true;
   pending_request.picker->Show(picker_params, std::move(source_lists),
                                done_callback);
 
diff --git a/chrome/browser/media/webrtc/desktop_capture_access_handler.h b/chrome/browser/media/webrtc/desktop_capture_access_handler.h
index 9c8edeac9..6fa872e 100644
--- a/chrome/browser/media/webrtc/desktop_capture_access_handler.h
+++ b/chrome/browser/media/webrtc/desktop_capture_access_handler.h
@@ -37,12 +37,12 @@
 
   // MediaAccessHandler implementation.
   bool SupportsStreamType(content::WebContents* web_contents,
-                          const blink::MediaStreamType type,
+                          const blink::mojom::MediaStreamType type,
                           const extensions::Extension* extension) override;
   bool CheckMediaAccessPermission(
       content::RenderFrameHost* render_frame_host,
       const GURL& security_origin,
-      blink::MediaStreamType type,
+      blink::mojom::MediaStreamType type,
       const extensions::Extension* extension) override;
   void HandleRequest(content::WebContents* web_contents,
                      const content::MediaStreamRequest& request,
@@ -51,7 +51,7 @@
   void UpdateMediaRequestState(int render_process_id,
                                int render_frame_id,
                                int page_request_id,
-                               blink::MediaStreamType stream_type,
+                               blink::mojom::MediaStreamType stream_type,
                                content::MediaRequestState state) override;
 
  private:
diff --git a/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc b/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc
index 12f59419f..70ed3a0 100644
--- a/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc
+++ b/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc
@@ -46,13 +46,13 @@
          true /* expect_tabs */, request_audio /* expect_audio */,
          fake_desktop_media_id_response /* selected_source */}};
     picker_factory_->SetTestFlags(test_flags, base::size(test_flags));
-    blink::MediaStreamType audio_type =
-        request_audio ? blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE
-                      : blink::MEDIA_NO_SERVICE;
+    blink::mojom::MediaStreamType audio_type =
+        request_audio ? blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE
+                      : blink::mojom::MediaStreamType::NO_SERVICE;
     content::MediaStreamRequest request(
         0, 0, 0, GURL("http://origin/"), false, request_type, std::string(),
-        std::string(), audio_type, blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
-        false);
+        std::string(), audio_type,
+        blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE, false);
 
     base::RunLoop wait_loop;
     content::MediaResponseCallback callback = base::BindOnce(
@@ -102,7 +102,8 @@
                  false /*request_audio*/);
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(1u, devices.size());
-  EXPECT_EQ(blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, devices[0].type);
+  EXPECT_EQ(blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+            devices[0].type);
 }
 
 TEST_F(DesktopCaptureAccessHandlerTest,
@@ -116,8 +117,10 @@
                  true /* request_audio */);
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(2u, devices.size());
-  EXPECT_EQ(blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, devices[0].type);
-  EXPECT_EQ(blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE, devices[1].type);
+  EXPECT_EQ(blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+            devices[0].type);
+  EXPECT_EQ(blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE,
+            devices[1].type);
 }
 
 TEST_F(DesktopCaptureAccessHandlerTest, ChangeSourcePermissionDenied) {
@@ -134,8 +137,8 @@
   const int render_process_id = 0;
   const int render_frame_id = 0;
   const int page_request_id = 0;
-  const blink::MediaStreamType stream_type =
-      blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE;
+  const blink::mojom::MediaStreamType stream_type =
+      blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE;
   FakeDesktopMediaPickerFactory::TestFlags test_flags[] = {
       {false /* expect_screens */, false /* expect_windows*/,
        true /* expect_tabs */, false /* expect_audio */,
@@ -144,7 +147,8 @@
   content::MediaStreamRequest request(
       render_process_id, render_frame_id, page_request_id,
       GURL("http://origin/"), false, blink::MEDIA_DEVICE_UPDATE, std::string(),
-      std::string(), blink::MEDIA_NO_SERVICE, stream_type, false);
+      std::string(), blink::mojom::MediaStreamType::NO_SERVICE, stream_type,
+      false);
   content::MediaResponseCallback callback;
   access_handler_->HandleRequest(web_contents(), request, std::move(callback),
                                  nullptr /* extension */);
@@ -173,8 +177,8 @@
   picker_factory_->SetTestFlags(test_flags, base::size(test_flags));
   content::MediaStreamRequest request(
       0, 0, 0, GURL("http://origin/"), false, blink::MEDIA_DEVICE_UPDATE,
-      std::string(), std::string(), blink::MEDIA_NO_SERVICE,
-      blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, false);
+      std::string(), std::string(), blink::mojom::MediaStreamType::NO_SERVICE,
+      blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE, false);
   content::MediaResponseCallback callback;
   access_handler_->HandleRequest(web_contents(), request, std::move(callback),
                                  nullptr /* extension */);
@@ -210,8 +214,8 @@
   for (size_t i = 0; i < kTestFlagCount; ++i) {
     content::MediaStreamRequest request(
         0, 0, 0, GURL("http://origin/"), false, blink::MEDIA_DEVICE_UPDATE,
-        std::string(), std::string(), blink::MEDIA_NO_SERVICE,
-        blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, false);
+        std::string(), std::string(), blink::mojom::MediaStreamType::NO_SERVICE,
+        blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE, false);
     content::MediaResponseCallback callback = base::BindOnce(
         [](base::RunLoop* wait_loop,
            blink::mojom::MediaStreamRequestResult* request_result,
@@ -232,7 +236,8 @@
   EXPECT_TRUE(test_flags[0].picker_deleted);
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(1u, devices.size());
-  EXPECT_EQ(blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, devices[0].type);
+  EXPECT_EQ(blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+            devices[0].type);
 
   blink::MediaStreamDevice first_device = devices[0];
   EXPECT_TRUE(test_flags[1].picker_created);
@@ -241,7 +246,8 @@
   EXPECT_TRUE(test_flags[1].picker_deleted);
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(1u, devices.size());
-  EXPECT_EQ(blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, devices[0].type);
+  EXPECT_EQ(blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+            devices[0].type);
   EXPECT_FALSE(devices[0].IsSameDevice(first_device));
 
   access_handler_.reset();
diff --git a/chrome/browser/media/webrtc/desktop_capture_devices_util.cc b/chrome/browser/media/webrtc/desktop_capture_devices_util.cc
index ed93cb8..118b8703 100644
--- a/chrome/browser/media/webrtc/desktop_capture_devices_util.cc
+++ b/chrome/browser/media/webrtc/desktop_capture_devices_util.cc
@@ -136,8 +136,8 @@
     content::WebContents* web_contents,
     blink::MediaStreamDevices* devices,
     const content::DesktopMediaID& media_id,
-    blink::MediaStreamType devices_video_type,
-    blink::MediaStreamType devices_audio_type,
+    blink::mojom::MediaStreamType devices_video_type,
+    blink::mojom::MediaStreamType devices_audio_type,
     bool capture_audio,
     bool disable_local_echo,
     bool display_notification,
diff --git a/chrome/browser/media/webrtc/desktop_capture_devices_util.h b/chrome/browser/media/webrtc/desktop_capture_devices_util.h
index 2f021f4..2bdfe18c 100644
--- a/chrome/browser/media/webrtc/desktop_capture_devices_util.h
+++ b/chrome/browser/media/webrtc/desktop_capture_devices_util.h
@@ -20,8 +20,8 @@
     content::WebContents* web_contents,
     blink::MediaStreamDevices* devices,
     const content::DesktopMediaID& media_id,
-    blink::MediaStreamType devices_video_type,
-    blink::MediaStreamType devices_audio_type,
+    blink::mojom::MediaStreamType devices_video_type,
+    blink::mojom::MediaStreamType devices_audio_type,
     bool capture_audio,
     bool disable_local_echo,
     bool display_notification,
diff --git a/chrome/browser/media/webrtc/display_media_access_handler.cc b/chrome/browser/media/webrtc/display_media_access_handler.cc
index 16176bc..46e317e 100644
--- a/chrome/browser/media/webrtc/display_media_access_handler.cc
+++ b/chrome/browser/media/webrtc/display_media_access_handler.cc
@@ -58,9 +58,9 @@
 
 bool DisplayMediaAccessHandler::SupportsStreamType(
     content::WebContents* web_contents,
-    const blink::MediaStreamType stream_type,
+    const blink::mojom::MediaStreamType stream_type,
     const extensions::Extension* extension) {
-  return stream_type == blink::MEDIA_DISPLAY_VIDEO_CAPTURE;
+  return stream_type == blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE;
   // This class handles MEDIA_DISPLAY_AUDIO_CAPTURE as well, but only if it is
   // accompanied by MEDIA_DISPLAY_VIDEO_CAPTURE request as per spec.
   // https://w3c.github.io/mediacapture-screen-share/#mediadevices-additions
@@ -71,7 +71,7 @@
 bool DisplayMediaAccessHandler::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
   return false;
 }
@@ -118,7 +118,7 @@
     int render_process_id,
     int render_frame_id,
     int page_request_id,
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     content::MediaRequestState state) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
@@ -166,7 +166,8 @@
       url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
   picker_params.target_name = picker_params.app_name;
   picker_params.request_audio =
-      pending_request.request.audio_type == blink::MEDIA_DISPLAY_AUDIO_CAPTURE;
+      pending_request.request.audio_type ==
+      blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE;
   picker_params.approve_audio_by_default = false;
   pending_request.picker->Show(picker_params, std::move(source_lists),
                                done_callback);
@@ -201,10 +202,11 @@
         web_contents->GetLastCommittedURL(),
         url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
     ui = GetDevicesForDesktopCapture(
-        web_contents, &devices, media_id, blink::MEDIA_DISPLAY_VIDEO_CAPTURE,
-        blink::MEDIA_DISPLAY_AUDIO_CAPTURE, media_id.audio_share,
-        false /* disable_local_echo */, display_notification_, visible_url,
-        visible_url);
+        web_contents, &devices, media_id,
+        blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
+        blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE,
+        media_id.audio_share, false /* disable_local_echo */,
+        display_notification_, visible_url, visible_url);
   }
 
   std::move(pending_request.callback)
diff --git a/chrome/browser/media/webrtc/display_media_access_handler.h b/chrome/browser/media/webrtc/display_media_access_handler.h
index 2a641e84..5d8ae7c5 100644
--- a/chrome/browser/media/webrtc/display_media_access_handler.h
+++ b/chrome/browser/media/webrtc/display_media_access_handler.h
@@ -35,12 +35,12 @@
 
   // MediaAccessHandler implementation.
   bool SupportsStreamType(content::WebContents* web_contents,
-                          const blink::MediaStreamType stream_type,
+                          const blink::mojom::MediaStreamType stream_type,
                           const extensions::Extension* extension) override;
   bool CheckMediaAccessPermission(
       content::RenderFrameHost* render_frame_host,
       const GURL& security_origin,
-      blink::MediaStreamType type,
+      blink::mojom::MediaStreamType type,
       const extensions::Extension* extension) override;
   void HandleRequest(content::WebContents* web_contents,
                      const content::MediaStreamRequest& request,
@@ -49,7 +49,7 @@
   void UpdateMediaRequestState(int render_process_id,
                                int render_frame_id,
                                int page_request_id,
-                               blink::MediaStreamType stream_type,
+                               blink::mojom::MediaStreamType stream_type,
                                content::MediaRequestState state) override;
 
  private:
diff --git a/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc b/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
index 62042a7a..5f0f9800 100644
--- a/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
+++ b/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
@@ -48,9 +48,9 @@
     content::MediaStreamRequest request(
         0, 0, 0, GURL("http://origin/"), false, blink::MEDIA_GENERATE_STREAM,
         std::string(), std::string(),
-        request_audio ? blink::MEDIA_DISPLAY_AUDIO_CAPTURE
-                      : blink::MEDIA_NO_SERVICE,
-        blink::MEDIA_DISPLAY_VIDEO_CAPTURE, false);
+        request_audio ? blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE
+                      : blink::mojom::MediaStreamType::NO_SERVICE,
+        blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, false);
 
     base::RunLoop wait_loop;
     content::MediaResponseCallback callback = base::BindOnce(
@@ -98,7 +98,8 @@
                  &result, &devices, false /* request_audio */);
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(1u, devices.size());
-  EXPECT_EQ(blink::MEDIA_DISPLAY_VIDEO_CAPTURE, devices[0].type);
+  EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
+            devices[0].type);
   EXPECT_TRUE(devices[0].display_media_info.has_value());
 }
 
@@ -111,9 +112,11 @@
   ProcessRequest(fake_media_id, &result, &devices, true /* request_audio */);
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(2u, devices.size());
-  EXPECT_EQ(blink::MEDIA_DISPLAY_VIDEO_CAPTURE, devices[0].type);
+  EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
+            devices[0].type);
   EXPECT_TRUE(devices[0].display_media_info.has_value());
-  EXPECT_EQ(blink::MEDIA_DISPLAY_AUDIO_CAPTURE, devices[1].type);
+  EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE,
+            devices[1].type);
   EXPECT_TRUE(devices[1].input.IsValid());
 }
 
@@ -130,10 +133,10 @@
   const int render_process_id = 0;
   const int render_frame_id = 0;
   const int page_request_id = 0;
-  const blink::MediaStreamType video_stream_type =
-      blink::MEDIA_DISPLAY_VIDEO_CAPTURE;
-  const blink::MediaStreamType audio_stream_type =
-      blink::MEDIA_DISPLAY_AUDIO_CAPTURE;
+  const blink::mojom::MediaStreamType video_stream_type =
+      blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE;
+  const blink::mojom::MediaStreamType audio_stream_type =
+      blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE;
   FakeDesktopMediaPickerFactory::TestFlags test_flags[] = {
       {true /* expect_screens */, true /* expect_windows*/,
        true /* expect_tabs */, true /* expect_audio */,
@@ -172,8 +175,8 @@
   picker_factory_->SetTestFlags(test_flags, base::size(test_flags));
   content::MediaStreamRequest request(
       0, 0, 0, GURL("http://origin/"), false, blink::MEDIA_GENERATE_STREAM,
-      std::string(), std::string(), blink::MEDIA_NO_SERVICE,
-      blink::MEDIA_DISPLAY_VIDEO_CAPTURE, false);
+      std::string(), std::string(), blink::mojom::MediaStreamType::NO_SERVICE,
+      blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, false);
   content::MediaResponseCallback callback;
   access_handler_->HandleRequest(web_contents(), request, std::move(callback),
                                  nullptr /* extension */);
@@ -209,8 +212,8 @@
   for (size_t i = 0; i < kTestFlagCount; ++i) {
     content::MediaStreamRequest request(
         0, 0, 0, GURL("http://origin/"), false, blink::MEDIA_GENERATE_STREAM,
-        std::string(), std::string(), blink::MEDIA_NO_SERVICE,
-        blink::MEDIA_DISPLAY_VIDEO_CAPTURE, false);
+        std::string(), std::string(), blink::mojom::MediaStreamType::NO_SERVICE,
+        blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, false);
     content::MediaResponseCallback callback = base::BindOnce(
         [](base::RunLoop* wait_loop,
            blink::mojom::MediaStreamRequestResult* request_result,
@@ -231,7 +234,8 @@
   EXPECT_TRUE(test_flags[0].picker_deleted);
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(1u, devices.size());
-  EXPECT_EQ(blink::MEDIA_DISPLAY_VIDEO_CAPTURE, devices[0].type);
+  EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
+            devices[0].type);
 
   blink::MediaStreamDevice first_device = devices[0];
   EXPECT_TRUE(test_flags[1].picker_created);
@@ -240,7 +244,8 @@
   EXPECT_TRUE(test_flags[1].picker_deleted);
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(1u, devices.size());
-  EXPECT_EQ(blink::MEDIA_DISPLAY_VIDEO_CAPTURE, devices[0].type);
+  EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
+            devices[0].type);
   EXPECT_FALSE(devices[0].IsSameDevice(first_device));
 
   access_handler_.reset();
diff --git a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc
index 98bc28ca..1bda058 100644
--- a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc
+++ b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc
@@ -183,7 +183,8 @@
 
   // Kill switch for getDisplayMedia() on browser side to prevent renderer from
   // bypassing blink side checks.
-  if (request.video_type == blink::MEDIA_DISPLAY_VIDEO_CAPTURE &&
+  if (request.video_type ==
+          blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE &&
       !base::FeatureList::IsEnabled(blink::features::kRTCGetDisplayMedia)) {
     std::move(callback).Run(
         blink::MediaStreamDevices(),
@@ -209,7 +210,7 @@
 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return CheckMediaAccessPermission(render_frame_host, security_origin, type,
                                     nullptr);
@@ -218,7 +219,7 @@
 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   for (const auto& handler : media_access_handlers_) {
@@ -265,12 +266,12 @@
 
 std::string MediaCaptureDevicesDispatcher::GetDefaultDeviceIDForProfile(
     Profile* profile,
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   PrefService* prefs = profile->GetPrefs();
-  if (type == blink::MEDIA_DEVICE_AUDIO_CAPTURE)
+  if (type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE)
     return prefs->GetString(prefs::kDefaultAudioCaptureDevice);
-  else if (type == blink::MEDIA_DEVICE_VIDEO_CAPTURE)
+  else if (type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE)
     return prefs->GetString(prefs::kDefaultVideoCaptureDevice);
   else
     return std::string();
@@ -346,7 +347,7 @@
     int render_frame_id,
     int page_request_id,
     const GURL& security_origin,
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     content::MediaRequestState state) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   base::PostTaskWithTraits(
@@ -394,7 +395,7 @@
     int render_frame_id,
     int page_request_id,
     const GURL& security_origin,
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     content::MediaRequestState state) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   for (const auto& handler : media_access_handlers_) {
@@ -409,7 +410,7 @@
 
 #if defined(OS_CHROMEOS)
   if (IsOriginForCasting(security_origin) &&
-      IsVideoInputMediaType(stream_type)) {
+      blink::IsVideoInputMediaType(stream_type)) {
     // Notify ash that casting state has changed.
     if (state == content::MEDIA_REQUEST_STATE_DONE) {
       ash::Shell::Get()->OnCastingSessionStartedOrStopped(true);
@@ -460,11 +461,11 @@
     int render_process_id,
     int render_frame_id,
     int page_request_id,
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     bool is_secure) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  if (!IsVideoScreenCaptureMediaType(stream_type))
+  if (!blink::IsVideoScreenCaptureMediaType(stream_type))
     return;
 
   base::PostTaskWithTraits(
@@ -479,10 +480,10 @@
     int render_process_id,
     int render_frame_id,
     int page_request_id,
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     bool is_secure) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(IsVideoScreenCaptureMediaType(stream_type));
+  DCHECK(blink::IsVideoScreenCaptureMediaType(stream_type));
 
   for (const auto& handler : media_access_handlers_) {
     handler->UpdateVideoScreenCaptureStatus(render_process_id, render_frame_id,
diff --git a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.h b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.h
index 5d9cbce..51f9d9d 100644
--- a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.h
+++ b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.h
@@ -51,7 +51,7 @@
     // Handle an information update related to a media stream request.
     virtual void OnRequestUpdate(int render_process_id,
                                  int render_frame_id,
-                                 blink::MediaStreamType stream_type,
+                                 blink::mojom::MediaStreamType stream_type,
                                  const content::MediaRequestState state) {}
 
     // Handle an information update that a new stream is being created.
@@ -89,12 +89,12 @@
   // access permission. Note that this does not query the user.
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type);
+                                  blink::mojom::MediaStreamType type);
 
   // Same as above but for an |extension|, which may not be NULL.
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type,
+                                  blink::mojom::MediaStreamType type,
                                   const extensions::Extension* extension);
 
   // Helper to get the default devices which can be used by the media request.
@@ -109,11 +109,12 @@
 
   // Helper to get default device IDs. If the returned value is an empty string,
   // it means that there is no default device for the given device |type|. The
-  // only supported |type| values are blink::MEDIA_DEVICE_AUDIO_CAPTURE and
-  // blink::MEDIA_DEVICE_VIDEO_CAPTURE.
+  // only supported |type| values are
+  // blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE and
+  // blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE.
   // Must be called on the UI thread.
   std::string GetDefaultDeviceIDForProfile(Profile* profile,
-                                           blink::MediaStreamType type);
+                                           blink::mojom::MediaStreamType type);
 
   // Helpers for picking particular requested devices, identified by raw id.
   // If the device requested is not available it will return NULL.
@@ -139,14 +140,14 @@
                                   int render_frame_id,
                                   int page_request_id,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType stream_type,
+                                  blink::mojom::MediaStreamType stream_type,
                                   content::MediaRequestState state) override;
   void OnCreatingAudioStream(int render_process_id,
                              int render_frame_id) override;
   void OnSetCapturingLinkSecured(int render_process_id,
                                  int render_frame_id,
                                  int page_request_id,
-                                 blink::MediaStreamType stream_type,
+                                 blink::mojom::MediaStreamType stream_type,
                                  bool is_secure) override;
 
   scoped_refptr<MediaStreamCaptureIndicator> GetMediaStreamCaptureIndicator();
@@ -170,18 +171,19 @@
   // Called by the MediaObserver() functions, executed on UI thread.
   void NotifyAudioDevicesChangedOnUIThread();
   void NotifyVideoDevicesChangedOnUIThread();
-  void UpdateMediaRequestStateOnUIThread(int render_process_id,
-                                         int render_frame_id,
-                                         int page_request_id,
-                                         const GURL& security_origin,
-                                         blink::MediaStreamType stream_type,
-                                         content::MediaRequestState state);
+  void UpdateMediaRequestStateOnUIThread(
+      int render_process_id,
+      int render_frame_id,
+      int page_request_id,
+      const GURL& security_origin,
+      blink::mojom::MediaStreamType stream_type,
+      content::MediaRequestState state);
   void OnCreatingAudioStreamOnUIThread(int render_process_id,
                                        int render_frame_id);
   void UpdateVideoScreenCaptureStatus(int render_process_id,
                                       int render_frame_id,
                                       int page_request_id,
-                                      blink::MediaStreamType stream_type,
+                                      blink::mojom::MediaStreamType stream_type,
                                       bool is_secure);
 
   // Only for testing, a list of cached audio capture devices.
diff --git a/chrome/browser/media/webrtc/media_stream_capture_indicator.cc b/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
index 74205cf..615b725d 100644
--- a/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
+++ b/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
@@ -115,7 +115,7 @@
   void NotifyStopped();
 
  private:
-  int& GetStreamCount(blink::MediaStreamType type);
+  int& GetStreamCount(blink::mojom::MediaStreamType type);
 
   // content::WebContentsObserver overrides.
   void WebContentsDestroyed() override {
@@ -231,26 +231,26 @@
 }
 
 int& MediaStreamCaptureIndicator::WebContentsDeviceUsage::GetStreamCount(
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   switch (type) {
-    case blink::MEDIA_DEVICE_AUDIO_CAPTURE:
+    case blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE:
       return audio_stream_count_;
 
-    case blink::MEDIA_DEVICE_VIDEO_CAPTURE:
+    case blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE:
       return video_stream_count_;
 
-    case blink::MEDIA_GUM_TAB_AUDIO_CAPTURE:
-    case blink::MEDIA_GUM_TAB_VIDEO_CAPTURE:
+    case blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE:
+    case blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE:
       return mirroring_stream_count_;
 
-    case blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE:
-    case blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE:
-    case blink::MEDIA_DISPLAY_VIDEO_CAPTURE:
-    case blink::MEDIA_DISPLAY_AUDIO_CAPTURE:
+    case blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE:
+    case blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE:
+    case blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE:
+    case blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE:
       return desktop_stream_count_;
 
-    case blink::MEDIA_NO_SERVICE:
-    case blink::NUM_MEDIA_TYPES:
+    case blink::mojom::MediaStreamType::NO_SERVICE:
+    case blink::mojom::MediaStreamType::NUM_MEDIA_TYPES:
       NOTREACHED();
       return video_stream_count_;
   }
diff --git a/chrome/browser/media/webrtc/media_stream_devices_controller.cc b/chrome/browser/media/webrtc/media_stream_devices_controller.cc
index 4b32fc62..b858fb9 100644
--- a/chrome/browser/media/webrtc/media_stream_devices_controller.cc
+++ b/chrome/browser/media/webrtc/media_stream_devices_controller.cc
@@ -56,10 +56,12 @@
 bool ContentTypeIsRequested(ContentSettingsType type,
                             const content::MediaStreamRequest& request) {
   if (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC)
-    return request.audio_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE;
+    return request.audio_type ==
+           blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE;
 
   if (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)
-    return request.video_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE;
+    return request.video_type ==
+           blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE;
 
   return false;
 }
@@ -351,8 +353,10 @@
       // not empty, return the desired device if it's available. Otherwise,
       // return no device.
       if (audio_allowed &&
-          request_.audio_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE) {
-        DCHECK_EQ(blink::MEDIA_NO_SERVICE, request_.video_type);
+          request_.audio_type ==
+              blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
+        DCHECK_EQ(blink::mojom::MediaStreamType::NO_SERVICE,
+                  request_.video_type);
         if (!request_.requested_audio_device_id.empty()) {
           device =
               MediaCaptureDevicesDispatcher::GetInstance()
@@ -362,8 +366,10 @@
                        ->GetFirstAvailableAudioDevice();
         }
       } else if (video_allowed &&
-                 request_.video_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE) {
-        DCHECK_EQ(blink::MEDIA_NO_SERVICE, request_.audio_type);
+                 request_.video_type ==
+                     blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
+        DCHECK_EQ(blink::mojom::MediaStreamType::NO_SERVICE,
+                  request_.audio_type);
         // Pepper API opens only one device at a time.
         if (!request_.requested_video_device_id.empty()) {
           device =
diff --git a/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc b/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
index e38cf2fe..ed67e41 100644
--- a/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
+++ b/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
@@ -117,7 +117,7 @@
 
   // Checks whether the devices returned in OnMediaStreamResponse contains a
   // microphone and/or camera device.
-  bool CheckDevicesListContains(blink::MediaStreamType type) {
+  bool CheckDevicesListContains(blink::mojom::MediaStreamType type) {
     for (const auto& device : media_stream_devices_) {
       if (device.type == type) {
         return true;
@@ -136,12 +136,12 @@
       const std::string& audio_id,
       const std::string& video_id,
       blink::MediaStreamRequestType request_type) {
-    blink::MediaStreamType audio_type = audio_id.empty()
-                                            ? blink::MEDIA_NO_SERVICE
-                                            : blink::MEDIA_DEVICE_AUDIO_CAPTURE;
-    blink::MediaStreamType video_type = video_id.empty()
-                                            ? blink::MEDIA_NO_SERVICE
-                                            : blink::MEDIA_DEVICE_VIDEO_CAPTURE;
+    blink::mojom::MediaStreamType audio_type =
+        audio_id.empty() ? blink::mojom::MediaStreamType::NO_SERVICE
+                         : blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE;
+    blink::mojom::MediaStreamType video_type =
+        video_id.empty() ? blink::mojom::MediaStreamType::NO_SERVICE
+                         : blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE;
     EXPECT_EQ(example_url(),
               GetWebContents()->GetMainFrame()->GetLastCommittedURL());
     int render_process_id =
@@ -188,7 +188,7 @@
 
     blink::MediaStreamDevices audio_devices;
     blink::MediaStreamDevice fake_audio_device(
-        blink::MEDIA_DEVICE_AUDIO_CAPTURE, example_audio_id_,
+        blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, example_audio_id_,
         "Fake Audio Device");
     audio_devices.push_back(fake_audio_device);
     MediaCaptureDevicesDispatcher::GetInstance()->SetTestAudioCaptureDevices(
@@ -196,7 +196,7 @@
 
     blink::MediaStreamDevices video_devices;
     blink::MediaStreamDevice fake_video_device(
-        blink::MEDIA_DEVICE_VIDEO_CAPTURE, example_video_id_,
+        blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, example_video_id_,
         "Fake Video Device");
     video_devices.push_back(fake_video_device);
     MediaCaptureDevicesDispatcher::GetInstance()->SetTestVideoCaptureDevices(
@@ -558,9 +558,9 @@
 
   // Simulate that an a video stream is now being captured.
   blink::MediaStreamDevices video_devices(1);
-  video_devices[0] =
-      blink::MediaStreamDevice(blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                               example_video_id(), example_video_id());
+  video_devices[0] = blink::MediaStreamDevice(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, example_video_id(),
+      example_video_id());
   MediaCaptureDevicesDispatcher* dispatcher =
       MediaCaptureDevicesDispatcher::GetInstance();
   dispatcher->SetTestVideoCaptureDevices(video_devices);
@@ -704,9 +704,11 @@
     // Check the media stream result is expected and the devices returned are
     // expected;
     ASSERT_EQ(test.ExpectedMediaStreamResult(), media_stream_result());
-    ASSERT_EQ(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE),
+    ASSERT_EQ(CheckDevicesListContains(
+                  blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE),
               test.ExpectMicAllowed() && test.ExpectCamAllowed());
-    ASSERT_EQ(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE),
+    ASSERT_EQ(CheckDevicesListContains(
+                  blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE),
               test.ExpectMicAllowed() && test.ExpectCamAllowed());
   }
 }
@@ -723,8 +725,10 @@
   ASSERT_EQ(0, prompt_factory()->TotalRequestCount());
 
   ASSERT_EQ(blink::mojom::MediaStreamRequestResult::OK, media_stream_result());
-  ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
-  ASSERT_TRUE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+  ASSERT_FALSE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
+  ASSERT_TRUE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
 }
 
 IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
@@ -747,8 +751,10 @@
 
   // Accept the prompt.
   ASSERT_EQ(blink::mojom::MediaStreamRequestResult::OK, media_stream_result());
-  ASSERT_TRUE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
-  ASSERT_TRUE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+  ASSERT_TRUE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
+  ASSERT_TRUE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
 
   // Check that re-requesting allows without prompting.
   prompt_factory()->ResetCounts();
@@ -759,8 +765,10 @@
   ASSERT_EQ(0, prompt_factory()->TotalRequestCount());
 
   ASSERT_EQ(blink::mojom::MediaStreamRequestResult::OK, media_stream_result());
-  ASSERT_TRUE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
-  ASSERT_TRUE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+  ASSERT_TRUE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
+  ASSERT_TRUE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
 }
 
 IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
@@ -779,8 +787,10 @@
 
   ASSERT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
             media_stream_result());
-  ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
-  ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+  ASSERT_FALSE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
+  ASSERT_FALSE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
 }
 
 IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest, WebContentsDestroyed) {
@@ -802,8 +812,10 @@
 
   ASSERT_EQ(blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN,
             media_stream_result());
-  ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
-  ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+  ASSERT_FALSE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
+  ASSERT_FALSE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
 }
 
 // Request and block microphone and camera access with kill switch.
@@ -834,8 +846,10 @@
 
   ASSERT_EQ(blink::mojom::MediaStreamRequestResult::KILL_SWITCH_ON,
             media_stream_result());
-  ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
-  ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+  ASSERT_FALSE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
+  ASSERT_FALSE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
 }
 
 IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
@@ -867,8 +881,10 @@
 
   ASSERT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
             media_stream_result());
-  ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
-  ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+  ASSERT_FALSE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
+  ASSERT_FALSE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
   EXPECT_EQ(TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED,
             GetContentSettings()->GetMicrophoneCameraState());
 }
@@ -902,8 +918,10 @@
 
   ASSERT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
             media_stream_result());
-  ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
-  ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+  ASSERT_FALSE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
+  ASSERT_FALSE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
   EXPECT_EQ(TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED,
             GetContentSettings()->GetMicrophoneCameraState());
 }
@@ -920,8 +938,10 @@
                      base::Unretained(this)));
 
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, media_stream_result());
-  EXPECT_TRUE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
-  EXPECT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+  EXPECT_TRUE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
+  EXPECT_FALSE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
 }
 
 IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
@@ -936,6 +956,8 @@
                      base::Unretained(this)));
 
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, media_stream_result());
-  EXPECT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
-  EXPECT_TRUE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+  EXPECT_FALSE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
+  EXPECT_TRUE(CheckDevicesListContains(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
 }
diff --git a/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc b/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
index 5e50258..9a49230 100644
--- a/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
+++ b/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
@@ -78,30 +78,30 @@
 
 bool PermissionBubbleMediaAccessHandler::SupportsStreamType(
     content::WebContents* web_contents,
-    const blink::MediaStreamType type,
+    const blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
 #if defined(OS_ANDROID)
-  return type == blink::MEDIA_DEVICE_VIDEO_CAPTURE ||
-         type == blink::MEDIA_DEVICE_AUDIO_CAPTURE ||
-         type == blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE ||
-         type == blink::MEDIA_DISPLAY_VIDEO_CAPTURE;
+  return type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE ||
+         type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE ||
+         type == blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE ||
+         type == blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE;
 #else
-  return type == blink::MEDIA_DEVICE_VIDEO_CAPTURE ||
-         type == blink::MEDIA_DEVICE_AUDIO_CAPTURE;
+  return type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE ||
+         type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE;
 #endif
 }
 
 bool PermissionBubbleMediaAccessHandler::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
   content::WebContents* web_contents =
       content::WebContents::FromRenderFrameHost(render_frame_host);
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
   ContentSettingsType content_settings_type =
-      type == blink::MEDIA_DEVICE_AUDIO_CAPTURE
+      type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE
           ? CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
           : CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA;
 
@@ -122,7 +122,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
 #if defined(OS_ANDROID)
-  if (IsScreenCaptureMediaType(request.video_type) &&
+  if (blink::IsScreenCaptureMediaType(request.video_type) &&
       !base::FeatureList::IsEnabled(
           chrome::android::kUserMediaScreenCapturing)) {
     // If screen capturing isn't enabled on Android, we'll use "invalid state"
@@ -162,7 +162,7 @@
   const content::MediaStreamRequest& request =
       it->second.begin()->second.request;
 #if defined(OS_ANDROID)
-  if (IsScreenCaptureMediaType(request.video_type)) {
+  if (blink::IsScreenCaptureMediaType(request.video_type)) {
     ScreenCaptureInfoBarDelegateAndroid::Create(
         web_contents, request,
         base::Bind(&PermissionBubbleMediaAccessHandler::OnAccessRequestResponse,
@@ -181,7 +181,7 @@
     int render_process_id,
     int render_frame_id,
     int page_request_id,
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     content::MediaRequestState state) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (state != content::MEDIA_REQUEST_STATE_CLOSING)
@@ -236,7 +236,8 @@
   // this function again when done.
   if (result == blink::mojom::MediaStreamRequestResult::OK) {
     const content::MediaStreamRequest& request = request_it->second.request;
-    if (request.audio_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE) {
+    if (request.audio_type ==
+        blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
       const SystemPermission system_audio_permission =
           system_media_permissions::CheckSystemAudioCapturePermission();
       UMA_HISTOGRAM_ENUMERATION(
@@ -259,7 +260,8 @@
         system_media_permissions::SystemAudioCapturePermissionBlocked();
       }
     }
-    if (request.video_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE) {
+    if (request.video_type ==
+        blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
       const SystemPermission system_video_permission =
           system_media_permissions::CheckSystemVideoCapturePermission();
       UMA_HISTOGRAM_ENUMERATION(
diff --git a/chrome/browser/media/webrtc/permission_bubble_media_access_handler.h b/chrome/browser/media/webrtc/permission_bubble_media_access_handler.h
index 96ee52c..87f4893 100644
--- a/chrome/browser/media/webrtc/permission_bubble_media_access_handler.h
+++ b/chrome/browser/media/webrtc/permission_bubble_media_access_handler.h
@@ -23,12 +23,12 @@
 
   // MediaAccessHandler implementation.
   bool SupportsStreamType(content::WebContents* web_contents,
-                          const blink::MediaStreamType type,
+                          const blink::mojom::MediaStreamType type,
                           const extensions::Extension* extension) override;
   bool CheckMediaAccessPermission(
       content::RenderFrameHost* render_frame_host,
       const GURL& security_origin,
-      blink::MediaStreamType type,
+      blink::mojom::MediaStreamType type,
       const extensions::Extension* extension) override;
   void HandleRequest(content::WebContents* web_contents,
                      const content::MediaStreamRequest& request,
@@ -37,7 +37,7 @@
   void UpdateMediaRequestState(int render_process_id,
                                int render_frame_id,
                                int page_request_id,
-                               blink::MediaStreamType stream_type,
+                               blink::mojom::MediaStreamType stream_type,
                                content::MediaRequestState state) override;
 
  private:
diff --git a/chrome/browser/media/webrtc/screen_capture_infobar_delegate_android.cc b/chrome/browser/media/webrtc/screen_capture_infobar_delegate_android.cc
index 1e92045..d891933 100644
--- a/chrome/browser/media/webrtc/screen_capture_infobar_delegate_android.cc
+++ b/chrome/browser/media/webrtc/screen_capture_infobar_delegate_android.cc
@@ -39,7 +39,8 @@
     : web_contents_(web_contents),
       request_(request),
       callback_(std::move(callback)) {
-  DCHECK_EQ(blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, request.video_type);
+  DCHECK_EQ(blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+            request.video_type);
 }
 
 ScreenCaptureInfoBarDelegateAndroid::~ScreenCaptureInfoBarDelegateAndroid() {
@@ -95,9 +96,9 @@
   if (result == blink::mojom::MediaStreamRequestResult::OK) {
     content::DesktopMediaID screen_id = content::DesktopMediaID(
         content::DesktopMediaID::TYPE_SCREEN, webrtc::kFullDesktopScreenId);
-    devices.push_back(
-        blink::MediaStreamDevice(blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
-                                 screen_id.ToString(), "Screen"));
+    devices.push_back(blink::MediaStreamDevice(
+        blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+        screen_id.ToString(), "Screen"));
 
     ui = MediaCaptureDevicesDispatcher::GetInstance()
              ->GetMediaStreamCaptureIndicator()
diff --git a/chrome/browser/media/webrtc/tab_capture_access_handler.cc b/chrome/browser/media/webrtc/tab_capture_access_handler.cc
index 5e326502..8a3fe7e1 100644
--- a/chrome/browser/media/webrtc/tab_capture_access_handler.cc
+++ b/chrome/browser/media/webrtc/tab_capture_access_handler.cc
@@ -22,16 +22,16 @@
 
 bool TabCaptureAccessHandler::SupportsStreamType(
     content::WebContents* web_contents,
-    const blink::MediaStreamType type,
+    const blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
-  return type == blink::MEDIA_GUM_TAB_VIDEO_CAPTURE ||
-         type == blink::MEDIA_GUM_TAB_AUDIO_CAPTURE;
+  return type == blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE ||
+         type == blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE;
 }
 
 bool TabCaptureAccessHandler::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
   return false;
 }
@@ -62,16 +62,20 @@
   const bool tab_capture_allowed = tab_capture_registry->VerifyRequest(
       request.render_process_id, request.render_frame_id, extension_id);
 
-  if (request.audio_type == blink::MEDIA_GUM_TAB_AUDIO_CAPTURE &&
+  if (request.audio_type ==
+          blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE &&
       tab_capture_allowed) {
     devices.push_back(blink::MediaStreamDevice(
-        blink::MEDIA_GUM_TAB_AUDIO_CAPTURE, std::string(), std::string()));
+        blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE, std::string(),
+        std::string()));
   }
 
-  if (request.video_type == blink::MEDIA_GUM_TAB_VIDEO_CAPTURE &&
+  if (request.video_type ==
+          blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE &&
       tab_capture_allowed) {
     devices.push_back(blink::MediaStreamDevice(
-        blink::MEDIA_GUM_TAB_VIDEO_CAPTURE, std::string(), std::string()));
+        blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE, std::string(),
+        std::string()));
   }
 
   if (!devices.empty()) {
diff --git a/chrome/browser/media/webrtc/tab_capture_access_handler.h b/chrome/browser/media/webrtc/tab_capture_access_handler.h
index 378c94e..26fd89ad 100644
--- a/chrome/browser/media/webrtc/tab_capture_access_handler.h
+++ b/chrome/browser/media/webrtc/tab_capture_access_handler.h
@@ -15,12 +15,12 @@
 
   // MediaAccessHandler implementation.
   bool SupportsStreamType(content::WebContents* web_contents,
-                          const blink::MediaStreamType type,
+                          const blink::mojom::MediaStreamType type,
                           const extensions::Extension* extension) override;
   bool CheckMediaAccessPermission(
       content::RenderFrameHost* render_frame_host,
       const GURL& security_origin,
-      blink::MediaStreamType type,
+      blink::mojom::MediaStreamType type,
       const extensions::Extension* extension) override;
   void HandleRequest(content::WebContents* web_contents,
                      const content::MediaStreamRequest& request,
diff --git a/chrome/browser/media/webrtc/webrtc_logging_handler_host.cc b/chrome/browser/media/webrtc/webrtc_logging_handler_host.cc
index 9788a4a..98dd8a6 100644
--- a/chrome/browser/media/webrtc/webrtc_logging_handler_host.cc
+++ b/chrome/browser/media/webrtc/webrtc_logging_handler_host.cc
@@ -230,7 +230,7 @@
         stop_callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(stop_rtp_dump_callback_.is_null() ||
-         stop_rtp_dump_callback_.Equals(stop_callback));
+         stop_rtp_dump_callback_ == stop_callback);
 
   stop_rtp_dump_callback_ = stop_callback;
 
diff --git a/chrome/browser/notifications/scheduler/internal/icon_store_unittest.cc b/chrome/browser/notifications/scheduler/internal/icon_store_unittest.cc
index 3bb4ef3c..f5ea9e8 100644
--- a/chrome/browser/notifications/scheduler/internal/icon_store_unittest.cc
+++ b/chrome/browser/notifications/scheduler/internal/icon_store_unittest.cc
@@ -16,6 +16,15 @@
 #include "components/leveldb_proto/testing/fake_db.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+// TODO(crbug.com/961073): Fix memory leaks in tests and re-enable on LSAN.
+#ifdef LEAK_SANITIZER
+#define MAYBE_AddDuplicate DISABLED_AddDuplicate
+#define MAYBE_Add DISABLED_Add
+#else
+#define MAYBE_AddDuplicate AddDuplicate
+#define MAYBE_Add Add
+#endif
+
 namespace notifications {
 namespace {
 
@@ -117,7 +126,7 @@
   EXPECT_FALSE(load_result());
 }
 
-TEST_F(IconStoreTest, Add) {
+TEST_F(IconStoreTest, MAYBE_Add) {
   InitDb();
 
   auto new_entry = std::make_unique<IconEntry>();
@@ -134,7 +143,7 @@
   VerifyEntry(kEntryId2, kEntryData);
 }
 
-TEST_F(IconStoreTest, AddDuplicate) {
+TEST_F(IconStoreTest, MAYBE_AddDuplicate) {
   InitDb();
 
   auto new_entry = std::make_unique<IconEntry>();
diff --git a/chrome/browser/offline_pages/offline_page_bookmark_observer.cc b/chrome/browser/offline_pages/offline_page_bookmark_observer.cc
index e673d74..ab1c6f6 100644
--- a/chrome/browser/offline_pages/offline_page_bookmark_observer.cc
+++ b/chrome/browser/offline_pages/offline_page_bookmark_observer.cc
@@ -26,7 +26,7 @@
 void OfflinePageBookmarkObserver::BookmarkNodeRemoved(
     bookmarks::BookmarkModel* model,
     const bookmarks::BookmarkNode* parent,
-    int old_index,
+    size_t old_index,
     const bookmarks::BookmarkNode* node,
     const std::set<GURL>& removed_urls) {
   if (!offline_page_model_) {
diff --git a/chrome/browser/offline_pages/offline_page_bookmark_observer.h b/chrome/browser/offline_pages/offline_page_bookmark_observer.h
index d970012..44902613 100644
--- a/chrome/browser/offline_pages/offline_page_bookmark_observer.h
+++ b/chrome/browser/offline_pages/offline_page_bookmark_observer.h
@@ -33,7 +33,7 @@
   void BookmarkModelChanged() override;
   void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
                            const bookmarks::BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const bookmarks::BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override;
 
diff --git a/chrome/browser/offline_pages/recent_tab_helper.cc b/chrome/browser/offline_pages/recent_tab_helper.cc
index 0a42fa5..378e1cd0 100644
--- a/chrome/browser/offline_pages/recent_tab_helper.cc
+++ b/chrome/browser/offline_pages/recent_tab_helper.cc
@@ -258,8 +258,6 @@
       << OfflinePageModel::CanSaveURL(web_contents()->GetLastCommittedURL())
       << ", " << (current_offline_page == nullptr) << ")";
 
-  UMA_HISTOGRAM_BOOLEAN("OfflinePages.CanSaveRecentPage", can_save);
-
   if (!can_save)
     snapshot_controller_->Stop();
   // Last N should be disabled when:
diff --git a/chrome/browser/offline_pages/recent_tab_helper_unittest.cc b/chrome/browser/offline_pages/recent_tab_helper_unittest.cc
index 21fd95c9..041d424 100644
--- a/chrome/browser/offline_pages/recent_tab_helper_unittest.cc
+++ b/chrome/browser/offline_pages/recent_tab_helper_unittest.cc
@@ -338,8 +338,6 @@
 TEST_F(RecentTabHelperTest, LastNCaptureAfterLoad) {
   // Navigate and finish loading. Nothing should be saved.
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
 
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
   // Move the snapshot controller's time forward so it gets past timeouts.
@@ -363,8 +361,6 @@
 TEST_F(RecentTabHelperTest, NoLastNCaptureIfTabHiddenTooEarlyInPageLoad) {
   // Commit the navigation and hide the tab. Nothing should be saved.
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
   recent_tab_helper()->OnVisibilityChanged(content::Visibility::HIDDEN);
   RunUntilIdle();
   EXPECT_EQ(0U, page_added_count());
@@ -388,7 +384,6 @@
       std::make_unique<TestDelegate>(this, kTabId, false));
 
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectTotalCount("OfflinePages.CanSaveRecentPage", 0);
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
   FastForwardSnapshotController();
   recent_tab_helper()->OnVisibilityChanged(content::Visibility::HIDDEN);
@@ -410,8 +405,6 @@
 
   // Navigate and finish loading then hide the tab. Nothing should be saved.
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
 
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
   FastForwardSnapshotController();
@@ -438,8 +431,6 @@
 
   // Navigate and finish loading then hide the tab. Nothing should be saved.
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
   FastForwardSnapshotController();
   recent_tab_helper()->OnVisibilityChanged(content::Visibility::HIDDEN);
@@ -473,8 +464,7 @@
 // up with one snapshot, the 1st being replaced by the 2nd.
 TEST_F(RecentTabHelperTest, TwoCapturesSamePageLoad) {
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
+
   // Set page loading state to the 1st snapshot-able stage. No capture so far.
   recent_tab_helper()->DocumentAvailableInMainFrame();
   FastForwardSnapshotController();
@@ -557,8 +547,6 @@
 TEST_F(RecentTabHelperTest, TwoCapturesDifferentPageLoadsSameUrl) {
   // Fully load the page. Hide the tab and check for a snapshot.
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
   FastForwardSnapshotController();
   recent_tab_helper()->OnVisibilityChanged(content::Visibility::HIDDEN);
@@ -574,8 +562,6 @@
   // Reload the same URL until the page is minimally loaded. The previous
   // snapshot should have been removed.
   NavigateAndCommitTyped(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         2);
   recent_tab_helper()->DocumentAvailableInMainFrame();
   FastForwardSnapshotController();
   EXPECT_EQ(1U, page_added_count());
@@ -600,8 +586,6 @@
 TEST_F(RecentTabHelperTest, TwoCapturesWhere2ndFailsDifferentPageLoadsSameUrl) {
   // Fully load the page then hide the tab. A capture is expected.
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
   FastForwardSnapshotController();
   recent_tab_helper()->OnVisibilityChanged(content::Visibility::HIDDEN);
@@ -622,8 +606,6 @@
   // Fully load the page once more then hide the tab again. A capture happens
   // and fails but no snapshot should remain.
   NavigateAndCommitTyped(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         2);
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
   FastForwardSnapshotController();
   recent_tab_helper()->OnVisibilityChanged(content::Visibility::HIDDEN);
@@ -640,8 +622,6 @@
 TEST_F(RecentTabHelperTest, TwoCapturesDifferentPageLoadsDifferentUrls) {
   // Fully load the first URL then hide the tab and check for a snapshot.
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
 
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
   FastForwardSnapshotController();
@@ -656,8 +636,6 @@
 
   // Fully load the second URL. The previous snapshot should have been deleted.
   NavigateAndCommitTyped(kTestPageUrlOther);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         2);
 
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
   FastForwardSnapshotController();
@@ -684,8 +662,6 @@
   // Fully loads the page with intermediary steps where the tab is hidden. Then
   // check that two last_n snapshots were created but only one was kept.
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
   recent_tab_helper()->DocumentAvailableInMainFrame();
   FastForwardSnapshotController();
   recent_tab_helper()->OnVisibilityChanged(content::Visibility::HIDDEN);
@@ -752,8 +728,6 @@
                                                      123L, "");
   RunUntilIdle();
   ASSERT_EQ(0U, GetAllPages().size());
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage",
-                                         false, 1);
   histogram_tester()->ExpectTotalCount("OfflinePages.LastN.IsSavingSamePage",
                                        0);
 }
@@ -764,8 +738,6 @@
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndDisableFeature(kOffliningRecentPagesFeature);
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
   FastForwardSnapshotController();
   recent_tab_helper()->OnVisibilityChanged(content::Visibility::HIDDEN);
@@ -788,8 +760,6 @@
   // Commit the navigation and request the snapshot from downloads. No captures
   // so far.
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
   const ClientId client_id = NewDownloadClientId();
   recent_tab_helper()->ObserveAndDownloadCurrentPage(client_id, 153L, "");
   FastForwardSnapshotController();
@@ -823,8 +793,6 @@
 // kept.
 TEST_F(RecentTabHelperTest, DownloadRequestLaterInLoad) {
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
   recent_tab_helper()->DocumentAvailableInMainFrame();
   FastForwardSnapshotController();
   ASSERT_EQ(0U, GetAllPages().size());
@@ -851,8 +819,6 @@
 // is completed. Should end up with one offline page.
 TEST_F(RecentTabHelperTest, DownloadRequestAfterFullyLoad) {
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
   FastForwardSnapshotController();
   ASSERT_EQ(0U, GetAllPages().size());
@@ -874,8 +840,6 @@
 // is completed. Should end up with one offline page.
 TEST_F(RecentTabHelperTest, DownloadRequestAfterFullyLoadWithOrigin) {
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
   FastForwardSnapshotController();
   ASSERT_EQ(0U, GetAllPages().size());
@@ -897,8 +861,6 @@
 // fully loaded page.
 TEST_F(RecentTabHelperTest, SimultaneousCapturesFromLastNAndDownloads) {
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
   FastForwardSnapshotController();
   recent_tab_helper()->OnVisibilityChanged(content::Visibility::HIDDEN);
@@ -930,8 +892,6 @@
 // signals are poor signals for those).
 TEST_F(RecentTabHelperTest, DuplicateTabHiddenEventsShouldTriggerNewSnapshots) {
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
   recent_tab_helper()->DocumentAvailableInMainFrame();
   FastForwardSnapshotController();
   recent_tab_helper()->OnVisibilityChanged(content::Visibility::HIDDEN);
@@ -983,8 +943,6 @@
 TEST_F(RecentTabHelperTest, OverlappingDownloadRequestsAreIgnored) {
   // Navigates and commits then make two download snapshot requests.
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
   const ClientId client_id_1 = NewDownloadClientId();
   const int64_t offline_id_1 = 153L;
   recent_tab_helper()->ObserveAndDownloadCurrentPage(client_id_1, offline_id_1,
@@ -1027,8 +985,6 @@
 TEST_F(RecentTabHelperTest, SaveSameDocumentNavigationSnapshots) {
   // Navigates and load fully then hide the tab so that a snapshot is created.
   NavigateAndCommit(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
   FastForwardSnapshotController();
   recent_tab_helper()->OnVisibilityChanged(content::Visibility::HIDDEN);
@@ -1043,8 +999,6 @@
   // Another snapshot should be created to the updated URL.
   const GURL kTestPageUrlWithFragment(kTestPageUrl.spec() + "#aaa");
   NavigateAndCommit(kTestPageUrlWithFragment);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
   recent_tab_helper()->OnVisibilityChanged(content::Visibility::HIDDEN);
   RunUntilIdle();
   EXPECT_EQ(2U, page_added_count());
@@ -1139,14 +1093,10 @@
 }
 
 TEST_F(RecentTabHelperTest, NoSaveOfflinePageCacheForPost) {
-  // Navigate and finish loading. Nothing should be saved, but we should record
-  // that it is possible to save the page.
+  // Navigate and finish loading, then move the snapshot controller's time
+  // forward so it gets past timeouts. Nothing should be saved.
   NavigateAndCommitPost(kTestPageUrl);
-  histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
-                                         1);
-
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
-  // Move the snapshot controller's time forward so it gets past timeouts.
   FastForwardSnapshotController();
   ASSERT_EQ(0U, GetAllPages().size());
 
diff --git a/chrome/browser/password_manager/touch_to_fill_controller.cc b/chrome/browser/password_manager/touch_to_fill_controller.cc
new file mode 100644
index 0000000..84e77b44
--- /dev/null
+++ b/chrome/browser/password_manager/touch_to_fill_controller.cc
@@ -0,0 +1,92 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/password_manager/touch_to_fill_controller.h"
+
+#include <utility>
+
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/autofill/manual_filling_controller.h"
+#include "components/autofill/core/browser/ui/suggestion.h"
+#include "components/password_manager/core/common/password_manager_features.h"
+
+using content::WebContents;
+
+// static
+TouchToFillController* TouchToFillController::GetOrCreate(
+    WebContents* web_contents) {
+  DCHECK(web_contents) << "Need valid WebContents to attach controller to!";
+  DCHECK(TouchToFillController::AllowedForWebContents(web_contents));
+
+  TouchToFillController::CreateForWebContents(web_contents);
+  return TouchToFillController::FromWebContents(web_contents);
+}
+
+// static
+std::unique_ptr<TouchToFillController> TouchToFillController::CreateForTesting(
+    base::WeakPtr<ManualFillingController> mf_controller) {
+  // Using `new` to access a non-public constructor.
+  return base::WrapUnique(new TouchToFillController(mf_controller));
+}
+
+TouchToFillController::~TouchToFillController() = default;
+
+// static
+bool TouchToFillController::AllowedForWebContents(WebContents* web_contents) {
+  return base::FeatureList::IsEnabled(
+      password_manager::features::kTouchToFillAndroid);
+}
+
+void TouchToFillController::Show(
+    const std::vector<autofill::Suggestion>& suggestions) {
+  autofill::AccessorySheetData::Builder builder(
+      autofill::AccessoryTabType::TOUCH_TO_FILL,
+      // TODO(crbug.com/957532): Update title once mocks are finalized.
+      base::ASCIIToUTF16("Touch to Fill"));
+  for (const auto& suggestion : suggestions) {
+    // This needs to stay in sync with how the PasswordAutofillManager creates
+    // Suggestions out of PasswordFormFillData.
+    const base::string16& username = suggestion.value;
+    const base::string16& password = suggestion.additional_label;
+    // This is only set if the credential's realm differs from the realm of the
+    // form.
+    const base::string16& maybe_realm = suggestion.label;
+
+    builder.AddUserInfo().AppendSimpleField(username);
+    builder.AppendField(password, password, /*is_obfuscated=*/true,
+                        /*is_selectable=*/false);
+    builder.AppendField(maybe_realm, maybe_realm, /*is_obfuscated=*/false,
+                        /*is_selectable=*/false);
+  }
+
+  mf_controller_->ShowTouchToFillSheet(std::move(builder).Build());
+}
+
+void TouchToFillController::OnFillingTriggered(
+    const autofill::UserInfo::Field& selection) {
+  // TODO(crbug.com/957532): Implement this method.
+  NOTIMPLEMENTED();
+}
+
+void TouchToFillController::OnOptionSelected(
+    autofill::AccessoryAction selected_action) {
+  // TODO(crbug.com/957532): Implement this method.
+  NOTIMPLEMENTED();
+}
+
+TouchToFillController::TouchToFillController(WebContents* web_contents)
+    : mf_controller_(ManualFillingController::GetOrCreate(web_contents)) {
+  DCHECK(mf_controller_);
+}
+
+TouchToFillController::TouchToFillController(
+    base::WeakPtr<ManualFillingController> mf_controller)
+    : mf_controller_(std::move(mf_controller)) {
+  DCHECK(mf_controller_);
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(TouchToFillController)
diff --git a/chrome/browser/password_manager/touch_to_fill_controller.h b/chrome/browser/password_manager/touch_to_fill_controller.h
new file mode 100644
index 0000000..75670db
--- /dev/null
+++ b/chrome/browser/password_manager/touch_to_fill_controller.h
@@ -0,0 +1,70 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PASSWORD_MANAGER_TOUCH_TO_FILL_CONTROLLER_H_
+#define CHROME_BROWSER_PASSWORD_MANAGER_TOUCH_TO_FILL_CONTROLLER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/autofill/accessory_controller.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace autofill {
+struct Suggestion;
+}
+
+namespace content {
+class WebContents;
+}
+
+class ManualFillingController;
+
+class TouchToFillController
+    : public base::SupportsWeakPtr<TouchToFillController>,
+      public content::WebContentsUserData<TouchToFillController>,
+      public AccessoryController {
+ public:
+  // Returns a reference to the unique TouchToFillController associated
+  // with |web_contents|. A new instance is created if the first time this
+  // function is called. Only valid to be called if
+  // TouchToFillController::AllowedForWebContents(web_contents).
+  static TouchToFillController* GetOrCreate(content::WebContents* web_contents);
+
+  // Allow injecting a custom ManualFillingController for testing.
+  static std::unique_ptr<TouchToFillController> CreateForTesting(
+      base::WeakPtr<ManualFillingController> mf_controller);
+
+  TouchToFillController(const TouchToFillController&) = delete;
+  TouchToFillController& operator=(const TouchToFillController&) = delete;
+  ~TouchToFillController() override;
+
+  // Returns true if the touch to fill controller may exist for |web_contents|.
+  // Otherwise it returns false.
+  static bool AllowedForWebContents(content::WebContents* web_contents);
+
+  // Instructs the controller to show the provided |suggestions| to the user.
+  void Show(const std::vector<autofill::Suggestion>& suggestions);
+
+  // AccessoryController:
+  void OnFillingTriggered(const autofill::UserInfo::Field& selection) override;
+  void OnOptionSelected(autofill::AccessoryAction selected_action) override;
+
+ private:
+  friend class content::WebContentsUserData<TouchToFillController>;
+
+  explicit TouchToFillController(content::WebContents* web_contents);
+
+  // Constructor corresponding to CreateForTesting().
+  explicit TouchToFillController(
+      base::WeakPtr<ManualFillingController> mf_controller);
+
+  // The manual filling controller object to forward client requests to.
+  base::WeakPtr<ManualFillingController> mf_controller_;
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+#endif  // CHROME_BROWSER_PASSWORD_MANAGER_TOUCH_TO_FILL_CONTROLLER_H_
diff --git a/chrome/browser/password_manager/touch_to_fill_controller_unittest.cc b/chrome/browser/password_manager/touch_to_fill_controller_unittest.cc
new file mode 100644
index 0000000..07552771
--- /dev/null
+++ b/chrome/browser/password_manager/touch_to_fill_controller_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/password_manager/touch_to_fill_controller.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/autofill/mock_manual_filling_controller.h"
+#include "components/autofill/core/browser/ui/suggestion.h"
+#include "components/password_manager/core/common/password_manager_features.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class TouchToFillControllerTest : public testing::Test {
+ protected:
+  MockManualFillingController& manual_filling_controller() {
+    return mock_manual_filling_controller_;
+  }
+
+  TouchToFillController* touch_to_fill_controller() {
+    return touch_to_fill_controller_.get();
+  }
+
+ private:
+  testing::StrictMock<MockManualFillingController>
+      mock_manual_filling_controller_;
+  std::unique_ptr<TouchToFillController> touch_to_fill_controller_ =
+      TouchToFillController::CreateForTesting(
+          mock_manual_filling_controller_.AsWeakPtr());
+};
+
+TEST_F(TouchToFillControllerTest, AllowedForWebContents) {
+  for (bool is_touch_to_fill_enabled : {false, true}) {
+    SCOPED_TRACE(testing::Message()
+                 << "is_touch_to_fill_enabled: " << std::boolalpha
+                 << is_touch_to_fill_enabled);
+    base::test::ScopedFeatureList scoped_feature_list;
+    scoped_feature_list.InitWithFeatureState(
+        password_manager::features::kTouchToFillAndroid,
+        is_touch_to_fill_enabled);
+    EXPECT_EQ(is_touch_to_fill_enabled,
+              TouchToFillController::AllowedForWebContents(nullptr));
+  }
+}
+
+TEST_F(TouchToFillControllerTest, Show) {
+  // Test the appropriate translation of autofill suggestions into
+  // AccessorySheetData. Use masked passwords to mirror production behavior.
+  // Test both empty and non-empty realms.
+  const base::string16 alice_user = base::ASCIIToUTF16("Alice");
+  const base::string16 alice_pass = base::ASCIIToUTF16("*****");
+  const base::string16 alice_realm;
+
+  const base::string16 bob_user = base::ASCIIToUTF16("Bob");
+  const base::string16 bob_pass = base::ASCIIToUTF16("***");
+  const base::string16 bob_realm = base::ASCIIToUTF16("https://example.com");
+
+  autofill::Suggestion alice(alice_user);
+  alice.additional_label = alice_pass;
+
+  autofill::Suggestion bob(bob_user);
+  bob.additional_label = bob_pass;
+  bob.label = bob_realm;
+
+  EXPECT_CALL(manual_filling_controller(),
+              ShowTouchToFillSheet(
+                  autofill::AccessorySheetData::Builder(
+                      autofill::AccessoryTabType::TOUCH_TO_FILL,
+                      base::ASCIIToUTF16("Touch to Fill"))
+                      .AddUserInfo()
+                      .AppendField(alice_user, alice_user, false, true)
+                      .AppendField(alice_pass, alice_pass, true, false)
+                      .AppendField(alice_realm, alice_realm, false, false)
+                      .AddUserInfo()
+                      .AppendField(bob_user, bob_user, false, true)
+                      .AppendField(bob_pass, bob_pass, true, false)
+                      .AppendField(bob_realm, bob_realm, false, false)
+                      .Build()));
+
+  touch_to_fill_controller()->Show({alice, bob});
+}
diff --git a/chrome/browser/policy/cloud/policy_header_navigation_throttle.cc b/chrome/browser/policy/cloud/policy_header_navigation_throttle.cc
deleted file mode 100644
index a259fcc..0000000
--- a/chrome/browser/policy/cloud/policy_header_navigation_throttle.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/policy/cloud/policy_header_navigation_throttle.h"
-
-#include "chrome/browser/policy/cloud/policy_header_service_factory.h"
-#include "components/policy/core/common/cloud/policy_header_service.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/web_contents.h"
-
-namespace policy {
-
-namespace {
-void AddHeader(content::NavigationHandle* handle) {
-  content::BrowserContext* context =
-      handle->GetWebContents()->GetBrowserContext();
-
-  if (context->IsOffTheRecord())
-    return;
-
-  PolicyHeaderService* policy_header_service =
-      PolicyHeaderServiceFactory::GetForBrowserContext(context);
-  if (!policy_header_service)
-    return;
-
-  policy_header_service->AddPolicyHeaders(
-      handle->GetURL(),
-      base::BindOnce(&content::NavigationHandle::SetRequestHeader,
-                     base::Unretained(handle)));
-}
-}  // namespace
-
-PolicyHeaderNavigationThrottle::~PolicyHeaderNavigationThrottle() = default;
-
-PolicyHeaderNavigationThrottle::PolicyHeaderNavigationThrottle(
-    content::NavigationHandle* handle)
-    : NavigationThrottle(handle) {}
-
-content::NavigationThrottle::ThrottleCheckResult
-PolicyHeaderNavigationThrottle::WillStartRequest() {
-  AddHeader(navigation_handle());
-  return content::NavigationThrottle::PROCEED;
-}
-
-content::NavigationThrottle::ThrottleCheckResult
-PolicyHeaderNavigationThrottle::WillRedirectRequest() {
-  AddHeader(navigation_handle());
-  return content::NavigationThrottle::PROCEED;
-}
-
-const char* PolicyHeaderNavigationThrottle::GetNameForLogging() {
-  return "PolicyHeaderNavigationThrottle";
-}
-
-}  // namespace policy
diff --git a/chrome/browser/policy/cloud/policy_header_navigation_throttle.h b/chrome/browser/policy/cloud/policy_header_navigation_throttle.h
deleted file mode 100644
index c7f6cd8..0000000
--- a/chrome/browser/policy/cloud/policy_header_navigation_throttle.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_POLICY_CLOUD_POLICY_HEADER_NAVIGATION_THROTTLE_H_
-#define CHROME_BROWSER_POLICY_CLOUD_POLICY_HEADER_NAVIGATION_THROTTLE_H_
-
-#include <memory>
-
-#include "content/public/browser/navigation_throttle.h"
-
-namespace content {
-class NavigationHandle;
-}  // namespace content
-
-namespace policy {
-
-class PolicyHeaderNavigationThrottle : public content::NavigationThrottle {
- public:
-  explicit PolicyHeaderNavigationThrottle(content::NavigationHandle*);
-  ~PolicyHeaderNavigationThrottle() override;
-
-  // content::NavigationThrottle:
-  ThrottleCheckResult WillStartRequest() override;
-  ThrottleCheckResult WillRedirectRequest() override;
-  const char* GetNameForLogging() override;
-};
-
-}  // namespace policy
-
-#endif  // CHROME_BROWSER_POLICY_CLOUD_POLICY_HEADER_NAVIGATION_THROTTLE_H_
diff --git a/chrome/browser/policy/cloud/policy_header_service_factory.cc b/chrome/browser/policy/cloud/policy_header_service_factory.cc
deleted file mode 100644
index b05fb5ed..0000000
--- a/chrome/browser/policy/cloud/policy_header_service_factory.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/policy/cloud/policy_header_service_factory.h"
-
-#include <memory>
-#include <utility>
-
-#include "build/build_config.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/policy/chrome_browser_policy_connector.h"
-#include "chrome/browser/profiles/profile.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/policy/core/browser/browser_policy_connector.h"
-#include "components/policy/core/common/cloud/cloud_policy_store.h"
-#include "components/policy/core/common/cloud/device_management_service.h"
-#include "components/policy/core/common/cloud/policy_header_service.h"
-
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/policy/active_directory_policy_manager.h"
-#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
-#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
-#else
-#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
-#endif
-
-namespace policy {
-
-namespace {
-
-class PolicyHeaderServiceWrapper : public KeyedService {
- public:
-  explicit PolicyHeaderServiceWrapper(
-      std::unique_ptr<PolicyHeaderService> service)
-      : policy_header_service_(std::move(service)) {}
-
-  PolicyHeaderService* policy_header_service() const {
-    return policy_header_service_.get();
-  }
-
-  void Shutdown() override {
-    // Shutdown our core object so it can unregister any observers before the
-    // services we depend on are shutdown.
-    policy_header_service_.reset();
-  }
-
- private:
-  std::unique_ptr<PolicyHeaderService> policy_header_service_;
-};
-
-}  // namespace
-
-PolicyHeaderServiceFactory::PolicyHeaderServiceFactory()
-    : BrowserContextKeyedServiceFactory(
-          "PolicyHeaderServiceFactory",
-          BrowserContextDependencyManager::GetInstance()) {}
-
-PolicyHeaderServiceFactory::~PolicyHeaderServiceFactory() {
-}
-
-// static
-PolicyHeaderService* PolicyHeaderServiceFactory::GetForBrowserContext(
-    content::BrowserContext* context) {
-  PolicyHeaderServiceWrapper* wrapper =
-      static_cast<PolicyHeaderServiceWrapper*>(
-          GetInstance()->GetServiceForBrowserContext(context, true));
-  if (wrapper)
-    return wrapper->policy_header_service();
-  else
-    return NULL;
-}
-
-KeyedService* PolicyHeaderServiceFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-#if defined(OS_CHROMEOS)
-  BrowserPolicyConnectorChromeOS* connector =
-      g_browser_process->platform_part()->browser_policy_connector_chromeos();
-#else
-  BrowserPolicyConnector* connector =
-      g_browser_process->browser_policy_connector();
-#endif
-
-  CloudPolicyStore* user_store;
-  Profile* profile = Profile::FromBrowserContext(context);
-#if defined(OS_CHROMEOS)
-  CloudPolicyManager* cloud_manager =
-      profile->GetUserCloudPolicyManagerChromeOS();
-  if (cloud_manager) {
-    user_store = cloud_manager->core()->store();
-  } else {
-    ActiveDirectoryPolicyManager* active_directory_manager =
-        profile->GetActiveDirectoryPolicyManager();
-    if (!active_directory_manager)
-      return nullptr;
-    user_store = active_directory_manager->store();
-  }
-#else
-  CloudPolicyManager* manager = profile->GetUserCloudPolicyManager();
-  if (!manager)
-    return nullptr;
-  user_store = manager->core()->store();
-#endif
-
-  std::unique_ptr<PolicyHeaderService> service =
-      std::make_unique<PolicyHeaderService>(
-          connector->device_management_service()
-              ->configuration()
-              ->GetDMServerUrl(),
-          kPolicyVerificationKeyHash, user_store);
-  return new PolicyHeaderServiceWrapper(std::move(service));
-}
-
-// static
-PolicyHeaderServiceFactory* PolicyHeaderServiceFactory::GetInstance() {
-  return base::Singleton<PolicyHeaderServiceFactory>::get();
-}
-
-}  // namespace policy
diff --git a/chrome/browser/policy/cloud/policy_header_service_factory.h b/chrome/browser/policy/cloud/policy_header_service_factory.h
deleted file mode 100644
index 22bee05..0000000
--- a/chrome/browser/policy/cloud/policy_header_service_factory.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_POLICY_CLOUD_POLICY_HEADER_SERVICE_FACTORY_H_
-#define CHROME_BROWSER_POLICY_CLOUD_POLICY_HEADER_SERVICE_FACTORY_H_
-
-#include "base/macros.h"
-#include "base/memory/singleton.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-namespace policy {
-
-class PolicyHeaderService;
-
-// Factory for PolicyHeaderService objects. PolicyHeaderService is not actually
-// a KeyedService, so this class wraps PolicyHeaderService in
-// a KeyedService internally.
-class PolicyHeaderServiceFactory : public BrowserContextKeyedServiceFactory {
- public:
-  // Returns the instance of PolicyHeaderService for the passed |context|, or
-  // NULL if there is none (for instance, for incognito windows).
-  static PolicyHeaderService* GetForBrowserContext(
-      content::BrowserContext* context);
-
-  // Returns an instance of the PolicyHeaderServiceFactory singleton.
-  static PolicyHeaderServiceFactory* GetInstance();
-
- protected:
-  // BrowserContextKeyedServiceFactory implementation.
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* profile) const override;
-
- private:
-  friend struct base::DefaultSingletonTraits<PolicyHeaderServiceFactory>;
-
-  PolicyHeaderServiceFactory();
-  ~PolicyHeaderServiceFactory() override;
-
-  DISALLOW_COPY_AND_ASSIGN(PolicyHeaderServiceFactory);
-};
-
-}  // namespace policy
-
-#endif  // CHROME_BROWSER_POLICY_CLOUD_POLICY_HEADER_SERVICE_FACTORY_H_
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 5c62926..3f345e1c 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -4510,8 +4510,8 @@
   void TearDownOnMainThread() override { prompt_factory_.reset(); }
 
   content::MediaStreamRequest CreateRequest(
-      blink::MediaStreamType audio_request_type,
-      blink::MediaStreamType video_request_type) {
+      blink::mojom::MediaStreamType audio_request_type,
+      blink::mojom::MediaStreamType video_request_type) {
     content::WebContents* web_contents =
         browser()->tab_strip_model()->GetActiveWebContents();
     EXPECT_EQ(request_url_,
@@ -4567,8 +4567,9 @@
   }
 
   void FinishAudioTest() {
-    content::MediaStreamRequest request(CreateRequest(
-        blink::MEDIA_DEVICE_AUDIO_CAPTURE, blink::MEDIA_NO_SERVICE));
+    content::MediaStreamRequest request(
+        CreateRequest(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                      blink::mojom::MediaStreamType::NO_SERVICE));
     // TODO(raymes): Test MEDIA_DEVICE_OPEN (Pepper) which grants both webcam
     // and microphone permissions at the same time.
     MediaStreamDevicesController::RequestPermissions(
@@ -4579,8 +4580,9 @@
   }
 
   void FinishVideoTest() {
-    content::MediaStreamRequest request(CreateRequest(
-        blink::MEDIA_NO_SERVICE, blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+    content::MediaStreamRequest request(
+        CreateRequest(blink::mojom::MediaStreamType::NO_SERVICE,
+                      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
     // TODO(raymes): Test MEDIA_DEVICE_OPEN (Pepper) which grants both webcam
     // and microphone permissions at the same time.
     MediaStreamDevicesController::RequestPermissions(
@@ -4600,8 +4602,9 @@
 IN_PROC_BROWSER_TEST_P(MediaStreamDevicesControllerBrowserTest,
                        AudioCaptureAllowed) {
   blink::MediaStreamDevices audio_devices;
-  blink::MediaStreamDevice fake_audio_device(blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-                                             "fake_dev", "Fake Audio Device");
+  blink::MediaStreamDevice fake_audio_device(
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, "fake_dev",
+      "Fake Audio Device");
   audio_devices.push_back(fake_audio_device);
 
   PolicyMap policies;
@@ -4623,8 +4626,9 @@
 IN_PROC_BROWSER_TEST_P(MediaStreamDevicesControllerBrowserTest,
                        AudioCaptureAllowedUrls) {
   blink::MediaStreamDevices audio_devices;
-  blink::MediaStreamDevice fake_audio_device(blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-                                             "fake_dev", "Fake Audio Device");
+  blink::MediaStreamDevice fake_audio_device(
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, "fake_dev",
+      "Fake Audio Device");
   audio_devices.push_back(fake_audio_device);
 
   const char* allow_pattern[] = {
@@ -4658,8 +4662,9 @@
 IN_PROC_BROWSER_TEST_P(MediaStreamDevicesControllerBrowserTest,
                        VideoCaptureAllowed) {
   blink::MediaStreamDevices video_devices;
-  blink::MediaStreamDevice fake_video_device(blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                                             "fake_dev", "Fake Video Device");
+  blink::MediaStreamDevice fake_video_device(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, "fake_dev",
+      "Fake Video Device");
   video_devices.push_back(fake_video_device);
 
   PolicyMap policies;
@@ -4681,8 +4686,9 @@
 IN_PROC_BROWSER_TEST_P(MediaStreamDevicesControllerBrowserTest,
                        VideoCaptureAllowedUrls) {
   blink::MediaStreamDevices video_devices;
-  blink::MediaStreamDevice fake_video_device(blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                                             "fake_dev", "Fake Video Device");
+  blink::MediaStreamDevice fake_video_device(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, "fake_dev",
+      "Fake Video Device");
   video_devices.push_back(fake_video_device);
 
   const char* allow_pattern[] = {
diff --git a/chrome/browser/previews/previews_lite_page_url_loader_interceptor_unittest.cc b/chrome/browser/previews/previews_lite_page_url_loader_interceptor_unittest.cc
index 63810137c..727062d 100644
--- a/chrome/browser/previews/previews_lite_page_url_loader_interceptor_unittest.cc
+++ b/chrome/browser/previews/previews_lite_page_url_loader_interceptor_unittest.cc
@@ -24,6 +24,14 @@
 #include "services/network/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+// TODO(crbug.com/961073): Fix memory leaks in tests and re-enable on LSAN.
+#ifdef LEAK_SANITIZER
+#define MAYBE_InterceptRequestPreviewsState \
+  DISABLED_InterceptRequestPreviewsState
+#else
+#define MAYBE_InterceptRequestPreviewsState InterceptRequestPreviewsState
+#endif
+
 namespace previews {
 
 namespace {
@@ -80,7 +88,7 @@
 };
 
 TEST_F(PreviewsLitePageURLLoaderInterceptorTest,
-       InterceptRequestPreviewsState) {
+       MAYBE_InterceptRequestPreviewsState) {
   base::HistogramTester histogram_tester;
 
   network::ResourceRequest request;
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 3bf2dc8e..bd0a4453 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -48,7 +48,6 @@
 #include "chrome/browser/ntp_snippets/content_suggestions_service_factory.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
 #include "chrome/browser/plugins/plugin_prefs_factory.h"
-#include "chrome/browser/policy/cloud/policy_header_service_factory.h"
 #include "chrome/browser/policy/cloud/user_cloud_policy_invalidator_factory.h"
 #include "chrome/browser/predictors/autocomplete_action_predictor_factory.h"
 #include "chrome/browser/predictors/loading_predictor_factory.h"
@@ -354,7 +353,6 @@
 #else  // !defined(OS_CHROMEOS)
   policy::UserPolicySigninServiceFactory::GetInstance();
 #endif
-  policy::PolicyHeaderServiceFactory::GetInstance();
   policy::UserCloudPolicyInvalidatorFactory::GetInstance();
   predictors::AutocompleteActionPredictorFactory::GetInstance();
   predictors::PredictorDatabaseFactory::GetInstance();
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index ae8b1c49..199d93ca 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -35,7 +35,6 @@
 #include "chrome/browser/net/chrome_network_delegate.h"
 #include "chrome/browser/net/profile_network_context_service.h"
 #include "chrome/browser/net/profile_network_context_service_factory.h"
-#include "chrome/browser/policy/cloud/policy_header_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
@@ -55,7 +54,6 @@
 #include "components/metrics/metrics_pref_names.h"
 #include "components/metrics/metrics_service.h"
 #include "components/net_log/chrome_net_log.h"
-#include "components/policy/core/common/cloud/policy_header_service.h"
 #include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/account_consistency_method.h"
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
index cd30ef7..8a0c272 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
@@ -399,9 +399,9 @@
   test_clock_.Advance(kBackgroundUrgentProtectionTime);
   ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 
-  blink::MediaStreamDevices video_devices{
-      blink::MediaStreamDevice(blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                               "fake_media_device", "fake_media_device")};
+  blink::MediaStreamDevices video_devices{blink::MediaStreamDevice(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, "fake_media_device",
+      "fake_media_device")};
   std::unique_ptr<content::MediaStreamUI> ui =
       MediaCaptureDevicesDispatcher::GetInstance()
           ->GetMediaStreamCaptureIndicator()
@@ -422,9 +422,9 @@
   test_clock_.Advance(kBackgroundUrgentProtectionTime);
   ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 
-  blink::MediaStreamDevices desktop_capture_devices{
-      blink::MediaStreamDevice(blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
-                               "fake_media_device", "fake_media_device")};
+  blink::MediaStreamDevices desktop_capture_devices{blink::MediaStreamDevice(
+      blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+      "fake_media_device", "fake_media_device")};
   std::unique_ptr<content::MediaStreamUI> ui =
       MediaCaptureDevicesDispatcher::GetInstance()
           ->GetMediaStreamCaptureIndicator()
diff --git a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
index dd25999..f2d721f 100644
--- a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
@@ -789,9 +789,9 @@
 
   // Simulate that a video stream is now being captured.
   blink::MediaStreamDevices video_devices(1);
-  video_devices[0] =
-      blink::MediaStreamDevice(blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                               "fake_media_device", "fake_media_device");
+  video_devices[0] = blink::MediaStreamDevice(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, "fake_media_device",
+      "fake_media_device");
   MediaCaptureDevicesDispatcher* dispatcher =
       MediaCaptureDevicesDispatcher::GetInstance();
   dispatcher->SetTestVideoCaptureDevices(video_devices);
diff --git a/chrome/browser/resource_coordinator/tab_metrics_logger_unittest.cc b/chrome/browser/resource_coordinator/tab_metrics_logger_unittest.cc
index c2395c38..6c7b791 100644
--- a/chrome/browser/resource_coordinator/tab_metrics_logger_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_metrics_logger_unittest.cc
@@ -30,6 +30,35 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ui_base_types.h"
 
+// TODO(crbug.com/961073): Fix memory leaks in tests and re-enable on LSAN.
+#ifdef LEAK_SANITIZER
+#define MAYBE_GetNavigationEntryCount DISABLED_GetNavigationEntryCount
+#define MAYBE_GetAudibleState DISABLED_GetAudibleState
+#define MAYBE_CreateWindowFeaturesTest DISABLED_CreateWindowFeaturesTest
+#define MAYBE_CreateWindowFeaturesTestMoveTabToOtherWindow \
+  DISABLED_CreateWindowFeaturesTestMoveTabToOtherWindow
+#define MAYBE_CreateWindowFeaturesTestReplaceTab \
+  DISABLED_CreateWindowFeaturesTestReplaceTab
+#define MAYBE_GetHasFormEntry DISABLED_GetHasFormEntry
+#define MAYBE_GetPinState DISABLED_GetPinState
+#define MAYBE_GetSiteEngagementScore DISABLED_GetSiteEngagementScore
+#define MAYBE_GetHost DISABLED_GetHost
+#define MAYBE_GetTabFeatures DISABLED_GetTabFeatures
+#else
+#define MAYBE_GetNavigationEntryCount GetNavigationEntryCount
+#define MAYBE_GetAudibleState GetAudibleState
+#define MAYBE_CreateWindowFeaturesTest CreateWindowFeaturesTest
+#define MAYBE_CreateWindowFeaturesTestMoveTabToOtherWindow \
+  CreateWindowFeaturesTestMoveTabToOtherWindow
+#define MAYBE_CreateWindowFeaturesTestReplaceTab \
+  CreateWindowFeaturesTestReplaceTab
+#define MAYBE_GetHasFormEntry GetHasFormEntry
+#define MAYBE_GetPinState GetPinState
+#define MAYBE_GetSiteEngagementScore GetSiteEngagementScore
+#define MAYBE_GetHost GetHost
+#define MAYBE_GetTabFeatures GetTabFeatures
+#endif
+
 using content::WebContentsTester;
 using metrics::WindowMetricsEvent;
 using tab_ranker::WindowFeatures;
@@ -162,7 +191,7 @@
 #endif
 
 // Tests has_form_entry.
-TEST_F(TabMetricsLoggerTest, MAYBE_(GetHasFormEntry)) {
+TEST_F(TabMetricsLoggerTest, MAYBE_(MAYBE_GetHasFormEntry)) {
   EXPECT_FALSE(CurrentTabFeatures().has_form_entry);
   content::PageImportanceSignals signal;
   signal.had_form_interaction = true;
@@ -171,14 +200,14 @@
 }
 
 // Tests is_pinned.
-TEST_F(TabMetricsLoggerTest, MAYBE_(GetPinState)) {
+TEST_F(TabMetricsLoggerTest, MAYBE_(MAYBE_GetPinState)) {
   EXPECT_FALSE(CurrentTabFeatures().is_pinned);
   tab_strip_model_->SetTabPinned(0, true);
   EXPECT_TRUE(CurrentTabFeatures().is_pinned);
 }
 
 // Tests navigation_entry_count.
-TEST_F(TabMetricsLoggerTest, MAYBE_(GetNavigationEntryCount)) {
+TEST_F(TabMetricsLoggerTest, MAYBE_(MAYBE_GetNavigationEntryCount)) {
   EXPECT_EQ(CurrentTabFeatures().navigation_entry_count, 1);
   tab_activity_simulator_.Navigate(web_contents_, GURL(kExampleUrl),
                                    pg_metrics_.page_transition);
@@ -189,7 +218,7 @@
 }
 
 // Tests site_engagement_score.
-TEST_F(TabMetricsLoggerTest, MAYBE_(GetSiteEngagementScore)) {
+TEST_F(TabMetricsLoggerTest, MAYBE_(MAYBE_GetSiteEngagementScore)) {
   EXPECT_EQ(CurrentTabFeatures().site_engagement_score, 0);
   SiteEngagementService::Get(profile())->ResetBaseScoreForURL(
       GURL(kChromiumUrl), 91);
@@ -197,20 +226,20 @@
 }
 
 // Tests was_recently_audible.
-TEST_F(TabMetricsLoggerTest, MAYBE_(GetAudibleState)) {
+TEST_F(TabMetricsLoggerTest, MAYBE_(MAYBE_GetAudibleState)) {
   EXPECT_FALSE(CurrentTabFeatures().was_recently_audible);
   web_contents_tester_->SetIsCurrentlyAudible(true);
   EXPECT_TRUE(CurrentTabFeatures().was_recently_audible);
 }
 
 // Tests host.
-TEST_F(TabMetricsLoggerTest, MAYBE_(GetHost)) {
+TEST_F(TabMetricsLoggerTest, MAYBE_(MAYBE_GetHost)) {
   EXPECT_EQ(CurrentTabFeatures().host, kChromiumDomain);
 }
 
 // Tests creating a flat TabFeatures structure for logging a tab and its
 // TabMetrics state.
-TEST_F(TabMetricsLoggerTest, MAYBE_(GetTabFeatures)) {
+TEST_F(TabMetricsLoggerTest, MAYBE_(MAYBE_GetTabFeatures)) {
   TabActivitySimulator tab_activity_simulator;
   Browser::CreateParams params(profile(), true);
   std::unique_ptr<Browser> browser =
@@ -411,7 +440,7 @@
 }
 
 // Tests CreateWindowFeatures of two browser windows.
-TEST_F(TabMetricsLoggerTest, CreateWindowFeaturesTest) {
+TEST_F(TabMetricsLoggerTest, MAYBE_CreateWindowFeaturesTest) {
   Browser::CreateParams params(profile(), true);
   std::unique_ptr<Browser> browser =
       FakeBrowserWindow::CreateBrowserWithFakeWindowForParams(&params);
@@ -463,7 +492,8 @@
 }
 
 // Tests moving a tab between browser windows.
-TEST_F(TabMetricsLoggerTest, CreateWindowFeaturesTestMoveTabToOtherWindow) {
+TEST_F(TabMetricsLoggerTest,
+       MAYBE_CreateWindowFeaturesTestMoveTabToOtherWindow) {
   Browser::CreateParams params(profile(), true);
   std::unique_ptr<Browser> starting_browser =
       FakeBrowserWindow::CreateBrowserWithFakeWindowForParams(&params);
@@ -511,7 +541,7 @@
 }
 
 // Tests replacing a tab.
-TEST_F(TabMetricsLoggerTest, CreateWindowFeaturesTestReplaceTab) {
+TEST_F(TabMetricsLoggerTest, MAYBE_CreateWindowFeaturesTestReplaceTab) {
   Browser::CreateParams params(profile(), true);
   std::unique_ptr<Browser> browser =
       FakeBrowserWindow::CreateBrowserWithFakeWindowForParams(&params);
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/BUILD.gn b/chrome/browser/resources/chromeos/kiosk_next_home/BUILD.gn
index 9d5a109..20e1b9dd 100644
--- a/chrome/browser/resources/chromeos/kiosk_next_home/BUILD.gn
+++ b/chrome/browser/resources/chromeos/kiosk_next_home/BUILD.gn
@@ -15,7 +15,6 @@
     "api.js",
     "api_impl.js",
   ]
-  externs_list = [ "$externs_path/arc_apps_private.js" ]
   deps = [
     "//chrome/browser/chromeos/kiosk_next_home/mojom:mojom_js_library_for_compile",
   ]
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/api.js b/chrome/browser/resources/chromeos/kiosk_next_home/api.js
index 76ee3c1..b610fb40 100644
--- a/chrome/browser/resources/chromeos/kiosk_next_home/api.js
+++ b/chrome/browser/resources/chromeos/kiosk_next_home/api.js
@@ -21,10 +21,20 @@
   /**
    * Adds listener for system events.
    * @param {!kioskNextHome.Listener} listener Listener for system events.
+   * @deprecated Use setListener instead.
+   * TODO(ltenorio): Remove usage and remove this method.
    */
   addListener(listener) {}
 
   /**
+   * Sets the current listener for system events. If null, we will no longer
+   * send events to the previous listener.
+   *
+   * @param {?kioskNextHome.Listener} listener Listener for system events.
+   */
+  setListener(listener) {}
+
+  /**
    * @return {!Promise<string>} Promise for the user's given name.
    */
   getUserGivenName() {}
@@ -46,10 +56,21 @@
    * @param {!Array<string>} scopes List of scopes to use when obtaining access
    *     token.
    * @return {!Promise<string>} Promise for the access token.
+   * @deprecated Use fetchAccessToken instead. This version doesn't return the
+   *    token expiration.
    */
   getAccessToken(scopes) {}
 
   /**
+   * Returns an access token with the requested scopes.
+   * @param {!Array<string>} scopes List of scopes to use when obtaining access
+   *     token.
+   * @return {!Promise<kioskNextHome.AccessToken>} Promise for the access
+   *     token.
+   */
+  fetchAccessToken(scopes) {}
+
+  /**
    * Returns the Android ID for the ARC++ container. This call might fail if the
    * ARC provisioning is not complete (i.e. user has not finished the ARC setup
    * flow).
@@ -99,6 +120,22 @@
 };
 
 /**
+ * A record representing an access token.
+ * @record
+ */
+kioskNextHome.AccessToken = class {
+  constructor() {
+    /** @type {string} The access token. */
+    this.token;
+    /**
+     * @type {number} Time when this token will expire in milliseconds from the
+     * Unix epoch.
+     */
+    this.expirationTime;
+  }
+};
+
+/**
  * Types of installed apps on Chrome OS.
  * @enum {string}
  */
@@ -177,6 +214,15 @@
 };
 
 /**
+ * Current status for ARC instance.
+ * @enum {string}
+ */
+kioskNextHome.ArcStatus = {
+  STOPPED: 'stopped',
+  READY: 'ready',
+};
+
+/**
  * Interface for a listener of system events, subscribed via
  * {!kioskNextHome.Bridge}.
  *
@@ -185,12 +231,18 @@
 kioskNextHome.Listener = class {
   /**
    * Called when an app state changes.
-   * TODO(brunoad): Adapt for AppService calls.
    * @param {!kioskNextHome.App} app The app whose state changed.
    */
   onAppChanged(app) {}
 
   /**
+   * Called when the ARC status changes. Called at first when the listener is
+   * set.
+   * @param {!kioskNextHome.ArcStatus} status The new ARC status.
+   */
+  onArcStatusChanged(status) {}
+
+  /**
    * Called when the network state changes.
    * @param {kioskNextHome.NetworkState} networkState Current network state of
    *     the device.
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/api_impl.js b/chrome/browser/resources/chromeos/kiosk_next_home/api_impl.js
index e98ae5b5..ce8d74d 100644
--- a/chrome/browser/resources/chromeos/kiosk_next_home/api_impl.js
+++ b/chrome/browser/resources/chromeos/kiosk_next_home/api_impl.js
@@ -43,6 +43,23 @@
 }
 
 /**
+ * Converts the given mojo time to milliseconds since the Unix epoch.
+ * @param {!mojoBase.mojom.Time} mojoTime
+ * @return {number}
+ */
+function getMillisecondsSinceUnixEpoch(mojoTime) {
+  // The mojo time representation is based on base::Time, which in turn
+  // represents the number of microseconds since the Windows epoch. The
+  // following code then mostly reimplements the base::Time::ToJSTime function.
+  const windowsEpoch = Date.UTC(1601, 0, 1, 0, 0, 0, 0);
+  const unixEpoch = Date.UTC(1970, 0, 1, 0, 0, 0, 0);
+  const epochDelta = unixEpoch - windowsEpoch;
+
+  // Converting to milliseconds and from BigInt to Number.
+  return Number(mojoTime.internalValue / BigInt(1000)) - epochDelta;
+}
+
+/**
  * Builds an app from its mojo representation coming from the AppController.
  * @param {!chromeos.kioskNextHome.mojom.App} mojoApp
  * @return {!kioskNextHome.App} A bridge representation of an app.
@@ -63,8 +80,8 @@
 /** @implements {kioskNextHome.Bridge} */
 class KioskNextHomeBridge {
   constructor() {
-    /** @private @const {!Array<!kioskNextHome.Listener>} */
-    this.listeners_ = [];
+    /** @private {?kioskNextHome.Listener} */
+    this.listener_ = null;
     /** @private @const */
     this.identityAccessorProxy_ = new identity.mojom.IdentityAccessorProxy();
     /** @private @const */
@@ -89,15 +106,20 @@
     // Attaching app listeners.
     this.appControllerClientCallbackRouter_.onAppChanged.addListener(
         mojoApp => {
-          const bridgeApp = buildApp(mojoApp);
-          for (const listener of this.listeners_) {
-            listener.onAppChanged(
-                /** @type{!kioskNextHome.App} */ (
-                    Object.assign({}, bridgeApp)));
+          if (this.listener_) {
+            this.listener_.onAppChanged(buildApp(mojoApp));
           }
         });
-    this.appControllerProxy_.setClient(
-        this.appControllerClientCallbackRouter_.createProxy());
+    this.appControllerClientCallbackRouter_.onArcStatusChanged.addListener(
+        mojoArcStatus => {
+          const status =
+              mojoArcStatus == chromeos.kioskNextHome.mojom.ArcStatus.kReady ?
+              kioskNextHome.ArcStatus.READY :
+              kioskNextHome.ArcStatus.STOPPED;
+          if (this.listener_) {
+            this.listener_.onArcStatusChanged(status);
+          }
+        });
 
     // Attaching network status listeners.
     window.addEventListener(
@@ -110,8 +132,18 @@
   }
 
   /** @override */
+  setListener(listener) {
+    this.listener_ = listener;
+    this.appControllerProxy_.setClient(
+        this.appControllerClientCallbackRouter_.createProxy());
+  }
+
+  /** @override */
   addListener(listener) {
-    this.listeners_.push(listener);
+    console.warn(
+        'Setting bridge listener by using deprecated method addListener(). ' +
+        'Use setListener() instead.');
+    this.setListener(listener);
   }
 
   /** @override */
@@ -134,6 +166,11 @@
 
   /** @override */
   getAccessToken(scopes) {
+    return this.fetchAccessToken(scopes).then(accessToken => accessToken.token);
+  }
+
+  /** @override */
+  fetchAccessToken(scopes) {
     return this.identityAccessorProxy_.getPrimaryAccountWhenAvailable()
         .then(account => {
           return this.identityAccessorProxy_.getAccessToken(
@@ -142,8 +179,13 @@
         })
         .then(tokenInfo => {
           if (tokenInfo.token) {
-            return tokenInfo.token;
+            return {
+              token: tokenInfo.token,
+              expirationTime:
+                  getMillisecondsSinceUnixEpoch(tokenInfo.expirationTime),
+            };
           }
+
           throw 'Unable to get access token.';
         });
   }
@@ -196,8 +238,8 @@
    *     state.
    */
   notifyNetworkStateChange(networkState) {
-    for (const listener of this.listeners_) {
-      listener.onNetworkStateChanged(networkState);
+    if (this.listener_) {
+      this.listener_.onNetworkStateChanged(networkState);
     }
   }
 }
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js b/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js
index 061078a..4cc49b3 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js
@@ -271,8 +271,13 @@
     var updateDailyRefreshStates = key => {
       if (!changes[key])
         return;
-      var oldDailyRefreshInfo = JSON.parse(changes[key].oldValue);
+
+      // If the user did not change Daily Refresh in this sync update,
+      // changes[key].oldValue will be empty
+      var oldDailyRefreshInfo =
+          changes[key].oldValue ? JSON.parse(changes[key].oldValue) : '';
       var newDailyRefreshInfo = JSON.parse(changes[key].newValue);
+
       // The resume token is expected to change after a new daily refresh
       // wallpaper is set. Ignore it if it's the only change.
       if (oldDailyRefreshInfo.enabled === newDailyRefreshInfo.enabled &&
diff --git a/chrome/browser/resources/local_ntp/custom_links_edit.js b/chrome/browser/resources/local_ntp/custom_links_edit.js
index fd6e4d40..6e91934 100644
--- a/chrome/browser/resources/local_ntp/custom_links_edit.js
+++ b/chrome/browser/resources/local_ntp/custom_links_edit.js
@@ -197,7 +197,7 @@
  */
 function focusBackOnCancel(event) {
   if (event.keyCode === KEYCODES.ENTER || event.keyCode === KEYCODES.SPACE) {
-    const message = {cmd: 'focusMenu', tid: prepopulatedLink.rid};
+    const message = {cmd: 'focusMenu', rid: prepopulatedLink.rid};
     window.parent.postMessage(message, DOMAIN_ORIGIN);
     event.preventDefault();
     closeDialog();
@@ -222,10 +222,10 @@
   const cmd = event.data.cmd;
   const args = event.data;
   if (cmd === 'linkData') {
-    if (args.tid) {  // We are editing a link, prepopulate the link data.
+    if (args.rid) {  // We are editing a link, prepopulate the link data.
       document.title = editLinkTitle;
       $(IDS.DIALOG_TITLE).textContent = editLinkTitle;
-      prepopulateFields(args.tid);
+      prepopulateFields(args.rid);
     } else {  // We are adding a link, disable the delete button.
       document.title = addLinkTitle;
       $(IDS.DIALOG_TITLE).textContent = addLinkTitle;
diff --git a/chrome/browser/resources/local_ntp/customize.css b/chrome/browser/resources/local_ntp/customize.css
index e673900..729a4835 100644
--- a/chrome/browser/resources/local_ntp/customize.css
+++ b/chrome/browser/resources/local_ntp/customize.css
@@ -107,6 +107,15 @@
   }
 }
 
+/* If window height is less then the customization menu height, then hide the
+ * 'customize' button.
+ */
+@media (max-height: 528px) {
+  #edit-bg {
+    display: none;
+  }
+}
+
 #edit-bg-dialog::backdrop {
   background: transparent;
 }
diff --git a/chrome/browser/resources/local_ntp/externs.js b/chrome/browser/resources/local_ntp/externs.js
index b0a701b6..6f83ae2 100644
--- a/chrome/browser/resources/local_ntp/externs.js
+++ b/chrome/browser/resources/local_ntp/externs.js
@@ -14,7 +14,7 @@
  *     InstantMostVisitedItem
  *  @typedef {{dataGenerationTime: Date,
  *             isAddButton: boolean,
- *             tid: number,
+ *             rid: number,
  *             tileSource: number,
  *             tileTitleSource: number,
  *             title: ?,
@@ -170,7 +170,7 @@
 window.chrome.embeddedSearch.newTabPage.blacklistSearchSuggestionWithHash;
 
 /**
- * @param {number} tid
+ * @param {number} rid
  */
 window.chrome.embeddedSearch.newTabPage.deleteMostVisitedItem;
 
@@ -180,7 +180,7 @@
 window.chrome.embeddedSearch.newTabPage.fixupAndValidateUrl;
 
 /**
- * @param {number} tid
+ * @param {number} rid
  */
 window.chrome.embeddedSearch.newTabPage.getMostVisitedItemData;
 
diff --git a/chrome/browser/resources/local_ntp/local_ntp.css b/chrome/browser/resources/local_ntp/local_ntp.css
index c834317..0cb8db9 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.css
+++ b/chrome/browser/resources/local_ntp/local_ntp.css
@@ -7,20 +7,14 @@
   --md-tile-margin: 16px;
   --md-tile-size: 112px;
 
-  /* This will be overridden based on the viewport width, see below.
-   * TODO(kristipark): Combine these.
-   */
-  --column-count: 2;
-  --shortcut-column-count: 3;
+  /* This will be overridden based on the viewport width, see below. */
+  --column-count: 3;
 
-  --content-width: calc(
-      (var(--column-count) * (var(--tile-width) + var(--tile-margin)))
+  --content-width: calc(var(--column-count) * var(--md-tile-size)
       /* We add an extra pixel because rounding errors on different zooms can
        * make the width shorter than it should be. */
       + 1px);
 
-  --shortcuts-width: calc(var(--shortcut-column-count) * var(--md-tile-size));
-
   --logo-height: 200px; /* Normal height of a doodle. */
   --logo-margin-top: 56px; /* Expected OGB height, so logo doesn't overlap. */
   --logo-margin-bottom: 29px; /* Between logo and fakebox. */
@@ -35,9 +29,6 @@
   --logo-iframe-height: var(--logo-height);
   --logo-iframe-resize-duration: 150ms;
   --logo-iframe-width: 500px;
-  --tile-height: 128px;
-  --tile-margin: 16px;
-  --tile-width: 154px;
 
   --mv-notice-time: 10s;
 
@@ -56,19 +47,17 @@
   --text-color-link: rgb(var(--GB400-dark-rgb));
 }
 
-/* width >= (3 cols * (16px + 154px) - 16px + 200px) */
-@media (min-width: 694px) {
+/* width >= (4 cols * 112px (tile width) + 112px (1 tile margin)) */
+@media (min-width: 560px) {
   html {
-    --column-count: 3;
-    --shortcut-column-count: 4;
+    --column-count: 4;
   }
 }
 
-/* width >= (4 cols * (16px + 154px) - 16px + 200px) */
-@media (min-width: 864px) {
+/* width >= (5 cols * 112px (tile width) + 112px (1 tile margin)) */
+@media (min-width: 672px) {
   html {
-    --column-count: 4;
-    --shortcut-column-count: 5;
+    --column-count: 5;
   }
 }
 
@@ -114,7 +103,7 @@
    * single row, but also works for a row of icons, and works well on small
    * screens (like Pixelbook), as well as high-res screens. */
   height: 155px;
-  left: calc(50% - var(--shortcuts-width)/2);
+  left: calc(50% - var(--content-width)/2);
   position: absolute;
   top: calc(50% - 155px);
 }
@@ -407,7 +396,7 @@
   text-align: -webkit-auto;
   /* Add 2*6px to account for drop shadow on the tiles. If you change this, also
    * change the corresponding padding in most_visited_single.css. */
-  width: calc(var(--shortcuts-width) + 12px);
+  width: calc(var(--content-width) + 12px);
 }
 
 #mv-notice-container {
diff --git a/chrome/browser/resources/local_ntp/local_ntp.js b/chrome/browser/resources/local_ntp/local_ntp.js
index bcfd06e..c1a8d7c 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.js
+++ b/chrome/browser/resources/local_ntp/local_ntp.js
@@ -1029,14 +1029,14 @@
       showNotification(
           configData.translatedStrings.thumbnailRemovedNotification);
     }
-    lastBlacklistedTile = args.tid;
+    lastBlacklistedTile = args.rid;
 
-    ntpApiHandle.deleteMostVisitedItem(args.tid);
+    ntpApiHandle.deleteMostVisitedItem(args.rid);
   } else if (cmd === 'resizeDoodle') {
     doodles.resizeDoodleHandler(args);
   } else if (cmd === 'startEditLink') {
     $(IDS.CUSTOM_LINKS_EDIT_IFRAME)
-        .contentWindow.postMessage({cmd: 'linkData', tid: args.tid}, '*');
+        .contentWindow.postMessage({cmd: 'linkData', rid: args.rid}, '*');
     // Small delay to allow the dialog to finish setting up before displaying.
     window.setTimeout(function() {
       $(IDS.CUSTOM_LINKS_EDIT_IFRAME_DIALOG).showModal();
@@ -1047,7 +1047,7 @@
     // Focus the edited tile's menu or the add shortcut tile after closing the
     // custom link edit dialog without saving.
     $(IDS.TILES_IFRAME)
-        .contentWindow.postMessage({cmd: 'focusMenu', tid: args.tid}, '*');
+        .contentWindow.postMessage({cmd: 'focusMenu', rid: args.rid}, '*');
   }
 }
 
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.css b/chrome/browser/resources/local_ntp/most_visited_single.css
index c2bd7de53..303b83b 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.css
+++ b/chrome/browser/resources/local_ntp/most_visited_single.css
@@ -4,7 +4,6 @@
 
 html {
   /* Material Design constants */
-  --md-add-size: 24px;
   --md-edit-menu-size: 20px;
   --md-fallback-letter-size: 16px;
   --md-favicon-size: 32px;
@@ -14,12 +13,9 @@
   --md-menu-margin-side: 2px;
   --md-menu-margin-top: 4px;
   --md-menu-size: 12px;
-  --md-tile-height: 112px;
   --md-tile-margin: 16px;
-  --md-tile-padding-bottom: 10px;
-  --md-tile-padding-horizontal: 4px;
   --md-tile-padding-top: 16px;
-  --md-tile-width: 112px;
+  --md-tile-size: 112px;
   --md-title-font-size: 12px;
   --md-title-height: 24px;
   --md-title-max-height: 28px;
@@ -51,6 +47,7 @@
 }
 
 #most-visited {
+  margin-top: 10px;
   text-align: -webkit-center;
   user-select: none;
   width: 100%;
@@ -64,9 +61,8 @@
   justify-content: center;
   /* 5 112px tiles per row. If you change this, also change the corresponding
    * values in local_ntp.css. */
-  max-width: calc(var(--md-tile-width) * var(--md-max-tiles-row));
+  max-width: calc(var(--md-tile-size) * var(--md-max-tiles-row));
   opacity: 0;
-  padding: 10px 6px 0;
   position: static;
   /* This align correctly for both LTR and RTL */
   text-align: -webkit-auto;
@@ -118,23 +114,25 @@
   transition-duration: 0s;
 }
 
-.grid-tile-container.grid-reorder {
+.grid-reorder {
   z-index: 10;  /* Ensure the held tile is visible. */
 }
 
-.md-tile-container {
+.md-tile {
   border-radius: 4px;
   box-sizing: border-box;
-  height: var(--md-tile-height);
+  cursor: pointer;
+  height: var(--md-tile-size);
   margin-bottom: var(--md-tile-margin);
   opacity: 1;
+  padding-top: var(--md-tile-padding-top);
   position: relative;
   transition-property:
       background, background-color, border-color, box-shadow, opacity, filter;
-  width: var(--md-tile-width);
+  width: var(--md-tile-size);
 }
 
-.md-tile-container.reorder .md-tile {
+.reorder {
   background-color: white;
   border-radius: 4px;
   box-shadow: 0 1px 3px 0 rgba(var(--GG800-rgb), 0.3),
@@ -142,52 +140,46 @@
   transition-duration: 200ms;
 }
 
-html[darkmode=true] .md-tile-container.reorder .md-tile {
+html[darkmode=true] .reorder {
   background-color: rgb(var(--dark-mode-bg-rgb));
   box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4),
       0 4px 8px 3px rgba(0, 0, 0, 0.25);
 }
 
-.md-tile-container.reorder .md-tile .md-tile-inner {
+.reorder .md-tile-inner {
   z-index: unset;
 }
 
-.md-tile {
-  cursor: pointer;
-  padding: var(--md-tile-padding-top) var(--md-tile-padding-horizontal)
-    var(--md-tile-padding-bottom);
-}
-
 .md-empty-tile {
   display: none;
 }
 
-body:not(.reordering) .md-tile-container:hover,
-.grid-tile-container.grid-reorder .md-tile-container {
+body:not(.reordering) .md-tile:hover,
+.grid-reorder .md-tile {
   background-color: rgba(var(--GG900-rgb), 0.06);
 }
 
-html[darkmode=true] body:not(.reordering) .md-tile-container:hover,
-html[darkmode=true] .grid-tile-container.grid-reorder .md-tile-container {
+html[darkmode=true] body:not(.reordering) .md-tile:hover,
+html[darkmode=true] .grid-reorder .md-tile {
   background-color: rgba(255, 255, 255, 0.1);
 }
 
-body.dark-theme:not(.reordering) .md-tile-container:hover,
-body.dark-theme .grid-tile-container.grid-reorder .md-tile-container {
+body.dark-theme:not(.reordering) .md-tile:hover,
+body.dark-theme .grid-reorder .md-tile {
   background-color: rgba(255, 255, 255, 0.1);
 }
 
-body:not(.reordering) .md-tile-container:hover .md-menu {
+body:not(.reordering) .md-tile:hover .md-menu {
   opacity: 1;
   transition-delay: 500ms;
 }
 
-body.dark-theme:not(.reordering) .md-tile-container:active + .md-menu::after {
+body.dark-theme:not(.reordering) .md-tile:active + .md-menu::after {
   background-color: rgb(var(--GG400-rgb));
   transition-delay: 0ms;
 }
 
-.md-tile-container.blacklisted {
+.blacklisted {
   margin: 0;
   padding: 0;
   transform: scale(0, 0);
@@ -207,34 +199,24 @@
 }
 
 .md-icon {
-  margin-bottom: var(--md-icon-margin-bottom);
-  pointer-events: none;
-}
-
-.md-icon img {
-  /* Icons returned by the NTP Icon Source are always of this size. */
-  height: var(--md-icon-size);
-  width: var(--md-icon-size);
-}
-
-.md-icon-background {
   align-items: center;
   background-color: var(--icon-background-color);
   border-radius: 50%;
   display: flex;
   height: var(--md-icon-size);
   justify-content: center;
+  margin-bottom: var(--md-icon-margin-bottom);
   width: var(--md-icon-size);
 }
 
 .md-add-icon {
-  background: url(chrome-search://most-visited/add_link.svg) no-repeat center;
-  height: var(--md-add-size);
-  width: var(--md-add-size);
+  background-image: url(chrome-search://most-visited/add_link.svg);
+  background-position: center;
+  background-repeat: no-repeat;
 }
 
 .use-white-add-icon .md-add-icon {
-  background: url(chrome-search://most-visited/add_link_white.svg) no-repeat center;
+  background-image: url(chrome-search://most-visited/add_link_white.svg);
 }
 
 .md-fallback-letter {
@@ -287,19 +269,19 @@
 }
 
 /* Apply when a custom background is set. */
-body.custom-background .md-tile-container:not(.reorder) .md-title {
+body.custom-background .md-tile:not(.reorder) .md-title {
   filter: drop-shadow(0 0 6px rgba(0, 0, 0, 0.35));
 }
 
 /* Apply only when a theme with image is installed. */
-body.use-title-container .md-title-container {
+body.use-title-container .md-title {
   background-color: white;
   /* Necessary for a "pill" shape. Using 50% creates an oval. */
   border-radius: 500px;
   padding: 0 8px;
 }
 
-body.use-title-container .md-tile-container:not(.reorder) .md-title {
+body.use-title-container .md-tile:not(.reorder) .md-title {
   color: rgb(var(--GG800-rgb));
   filter: unset;
 }
@@ -371,12 +353,12 @@
   background-color: rgb(var(--GG400-rgb));
 }
 
-body.dark-theme .md-tile-container:not(.reorder) .md-menu::after,
+body.dark-theme .md-tile:not(.reorder) .md-menu::after,
 body.dark-theme:not(.reordering) .md-menu:focus:not(.mouse-navigation)::after {
   background-color: white;
 }
 
-html[darkmode=true] body.dark-theme .md-tile-container:not(.reorder) .md-menu::after,
+html[darkmode=true] body.dark-theme .md-tile:not(.reorder) .md-menu::after,
 html[darkmode=true] body.dark-theme:not(.reordering) .md-menu:focus:not(.mouse-navigation)::after {
   background-color: rgb(var(--GG200-rgb));
 }
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.js b/chrome/browser/resources/local_ntp/most_visited_single.js
index c4a8446..23055b8 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.js
+++ b/chrome/browser/resources/local_ntp/most_visited_single.js
@@ -59,18 +59,14 @@
   MAC_CHROMEOS: 'mac-chromeos',  // Reduces font weight for MacOS and ChromeOS.
   // Material Design classes.
   MD_EMPTY_TILE: 'md-empty-tile',
-  MD_ICON_BACKGROUND: 'md-icon-background',
   MD_FALLBACK_LETTER: 'md-fallback-letter',
-  MD_FAVICON: 'md-favicon',
   MD_ICON: 'md-icon',
   MD_ADD_ICON: 'md-add-icon',
   MD_MENU: 'md-menu',
   MD_EDIT_MENU: 'md-edit-menu',
   MD_TILE: 'md-tile',
-  MD_TILE_CONTAINER: 'md-tile-container',
   MD_TILE_INNER: 'md-tile-inner',
   MD_TITLE: 'md-title',
-  MD_TITLE_CONTAINER: 'md-title-container',
   NO_INITIAL_FADE: 'no-initial-fade',
 };
 
@@ -903,8 +899,8 @@
  * @param {!Object} info Data received in the message.
  */
 function focusTileMenu(info) {
-  const tile = document.querySelector(`a.md-tile[data-tid="${info.tid}"]`);
-  if (info.tid === -1 /* Add shortcut tile */) {
+  const tile = document.querySelector(`a.md-tile[data-rid="${info.rid}"]`);
+  if (info.rid === -1 /* Add shortcut tile */) {
     tile.focus();
   } else {
     tile.parentNode.childNodes[1].focus();
@@ -937,7 +933,7 @@
   // number of tiles.
   if (isCustomLinksEnabled && cur.childNodes.length < maxNumTiles) {
     const data = {
-      'tid': -1,
+      'rid': -1,
       'title': queryArgs['addLink'],
       'url': '',
       'isAddButton': true,
@@ -975,7 +971,7 @@
     // Re-balance the tiles if there are more than |MD_MAX_TILES_PER_ROW| in
     // order to make even rows.
     if (cur.childNodes.length > MD_MAX_TILES_PER_ROW) {
-      cur.style.maxWidth = 'calc(var(--md-tile-width) * ' +
+      cur.style.maxWidth = 'calc(var(--md-tile-size) * ' +
           Math.ceil(cur.childNodes.length / 2) + ')';
     }
   }
@@ -1003,8 +999,8 @@
  * navigation.
  */
 function updateTileVisibility() {
-  const allTiles = document.querySelectorAll(
-      '#' + IDS.MV_TILES + ' .' + CLASSES.MD_TILE_CONTAINER);
+  const allTiles =
+      document.querySelectorAll('#' + IDS.MV_TILES + ' .' + CLASSES.MD_TILE);
   if (allTiles.length === 0) {
     return;
   }
@@ -1033,10 +1029,9 @@
       return;
     }
 
-    data.tid = data.rid;
     if (!data.faviconUrl) {
       data.faviconUrl = 'chrome-search://favicon/size/16@' +
-          window.devicePixelRatio + 'x/' + data.renderViewId + '/' + data.tid;
+          window.devicePixelRatio + 'x/' + data.renderViewId + '/' + data.rid;
     }
     tiles.appendChild(renderTile(data));
   } else {
@@ -1052,10 +1047,10 @@
  * @param {Element} tile DOM node of the tile we want to remove.
  */
 function blacklistTile(tile) {
-  const tid = Number(tile.firstChild.getAttribute('data-tid'));
+  const rid = Number(tile.getAttribute('data-rid'));
 
   if (isCustomLinksEnabled) {
-    chrome.embeddedSearch.newTabPage.deleteMostVisitedItem(tid);
+    chrome.embeddedSearch.newTabPage.deleteMostVisitedItem(rid);
   } else {
     tile.classList.add('blacklisted');
     tile.addEventListener('transitionend', function(ev) {
@@ -1063,7 +1058,7 @@
         return;
       }
       window.parent.postMessage(
-          {cmd: 'tileBlacklisted', tid: Number(tid)}, DOMAIN_ORIGIN);
+          {cmd: 'tileBlacklisted', rid: Number(rid)}, DOMAIN_ORIGIN);
     });
   }
 }
@@ -1072,10 +1067,10 @@
 /**
  * Starts edit custom link flow. Tells host page to show the edit custom link
  * dialog and pre-populate it with data obtained using the link's id.
- * @param {?number} tid Restricted id of the tile we want to edit.
+ * @param {?number} rid Restricted id of the tile we want to edit.
  */
-function editCustomLink(tid) {
-  window.parent.postMessage({cmd: 'startEditLink', tid: tid}, DOMAIN_ORIGIN);
+function editCustomLink(rid) {
+  window.parent.postMessage({cmd: 'startEditLink', rid: rid}, DOMAIN_ORIGIN);
 }
 
 
@@ -1117,8 +1112,8 @@
     allTiles[i].setAttribute('data-pos', i);
   }
   chrome.embeddedSearch.newTabPage.reorderCustomLink(
-      Number(tile.firstChild.getAttribute('data-tid')),
-      Number(tile.firstChild.getAttribute('data-pos')));
+      Number(tile.getAttribute('data-rid')),
+      Number(tile.getAttribute('data-pos')));
 }
 
 
@@ -1208,24 +1203,19 @@
  * @return {Element}
  */
 function renderMaterialDesignTile(data) {
-  const mdTileContainer = document.createElement('div');
-  mdTileContainer.role = 'none';
-
+  const mdTile = document.createElement('a');
   if (data == null) {
-    mdTileContainer.className = CLASSES.MD_EMPTY_TILE;
-    return mdTileContainer;
+    mdTile.className = CLASSES.MD_EMPTY_TILE;
+    return mdTile;
   }
-  mdTileContainer.className = CLASSES.MD_TILE_CONTAINER;
+  mdTile.className = CLASSES.MD_TILE;
 
-  // The tile will be appended to tiles.
+  // The tile will be appended to |tiles|.
   const position = tiles.children.length;
   // This is set in the load/error event for the favicon image.
   let tileType = TileVisualType.NONE;
 
-  const mdTile = document.createElement('a');
-  mdTile.className = CLASSES.MD_TILE;
-  mdTile.tabIndex = 0;
-  mdTile.setAttribute('data-tid', data.tid);
+  mdTile.setAttribute('data-rid', data.rid);
   mdTile.setAttribute('data-pos', position);
   if (utils.isSchemeAllowed(data.url)) {
     mdTile.href = data.url;
@@ -1249,7 +1239,7 @@
         !data.isAddButton) {
       event.preventDefault();
       event.stopPropagation();
-      blacklistTile(mdTileContainer);
+      blacklistTile(mdTile);
     } else if (
         event.keyCode === KEYCODES.ENTER || event.keyCode === KEYCODES.SPACE) {
       event.preventDefault();
@@ -1271,29 +1261,26 @@
   const mdTileInner = document.createElement('div');
   mdTileInner.className = CLASSES.MD_TILE_INNER;
 
-  const mdIcon = document.createElement('div');
-  mdIcon.classList.add(CLASSES.MD_ICON);
-  mdIcon.classList.add(CLASSES.MD_ICON_BACKGROUND);
-
   if (data.isAddButton) {
-    const mdAdd = document.createElement('div');
-    mdAdd.className = CLASSES.MD_ADD_ICON;
-    const addBackground = document.createElement('div');
-    addBackground.className = CLASSES.MD_ICON_BACKGROUND;
+    mdTile.tabIndex = 0;
 
-    addBackground.appendChild(mdAdd);
-    mdIcon.appendChild(addBackground);
+    const mdIconAdd = document.createElement('div');
+    mdIconAdd.classList.add(CLASSES.MD_ICON);
+    mdIconAdd.classList.add(CLASSES.MD_ADD_ICON);
+
+    mdTileInner.appendChild(mdIconAdd);
   } else {
-    const fi = document.createElement('img');
+    const mdIcon = document.createElement('img');
+    mdIcon.classList.add(CLASSES.MD_ICON);
     // Set title and alt to empty so screen readers won't say the image name.
-    fi.title = '';
-    fi.alt = '';
+    mdIcon.title = '';
+    mdIcon.alt = '';
     const url = new URL('chrome-search://ntpicon/');
     url.searchParams.set('size', '24@' + window.devicePixelRatio + 'x');
     url.searchParams.set('url', data.url);
-    fi.src = url.toString();
+    mdIcon.src = url.toString();
     loadedCounter += 1;
-    fi.addEventListener('load', function(ev) {
+    mdIcon.addEventListener('load', function(ev) {
       // Store the type for a potential later navigation.
       tileType = TileVisualType.ICON_REAL;
       logMostVisitedImpression(
@@ -1304,17 +1291,16 @@
       // log.
       countLoad();
     });
-    fi.addEventListener('error', function(ev) {
+    mdIcon.addEventListener('error', function(ev) {
       const fallbackBackground = document.createElement('div');
-      fallbackBackground.className = CLASSES.MD_ICON_BACKGROUND;
+      fallbackBackground.className = CLASSES.MD_ICON;
       const fallbackLetter = document.createElement('div');
       fallbackLetter.className = CLASSES.MD_FALLBACK_LETTER;
       fallbackLetter.textContent = data.title.charAt(0).toUpperCase();
-      mdIcon.classList.add(CLASSES.FAILED_FAVICON);
+      fallbackBackground.classList.add(CLASSES.FAILED_FAVICON);
 
       fallbackBackground.appendChild(fallbackLetter);
-      mdIcon.removeChild(fi);
-      mdIcon.appendChild(fallbackBackground);
+      mdTileInner.replaceChild(fallbackBackground, mdIcon);
 
       // Store the type for a potential later navigation.
       tileType = TileVisualType.ICON_DEFAULT;
@@ -1327,23 +1313,18 @@
       countLoad();
     });
 
-    mdIcon.appendChild(fi);
+    mdTileInner.appendChild(mdIcon);
   }
 
-  mdTileInner.appendChild(mdIcon);
 
-  const mdTitleContainer = document.createElement('div');
-  mdTitleContainer.className = CLASSES.MD_TITLE_CONTAINER;
   const mdTitle = document.createElement('div');
   mdTitle.className = CLASSES.MD_TITLE;
+  mdTitle.style.direction = data.direction || 'ltr';
   const mdTitleTextwrap = document.createElement('span');
   mdTitleTextwrap.innerText = data.title;
-  mdTitle.style.direction = data.direction || 'ltr';
-  mdTitleContainer.appendChild(mdTitle);
-  mdTileInner.appendChild(mdTitleContainer);
-  mdTile.appendChild(mdTileInner);
-  mdTileContainer.appendChild(mdTile);
   mdTitle.appendChild(mdTitleTextwrap);
+  mdTileInner.appendChild(mdTitle);
+  mdTile.appendChild(mdTileInner);
 
   if (!data.isAddButton) {
     const mdMenu = document.createElement('button');
@@ -1355,7 +1336,7 @@
           'aria-label',
           (queryArgs['editLinkTooltip'] || '') + ' ' + data.title);
       mdMenu.addEventListener('click', function(ev) {
-        editCustomLink(data.tid);
+        editCustomLink(data.rid);
         ev.preventDefault();
         ev.stopPropagation();
         logEvent(LOG_TYPE.NTP_CUSTOMIZE_EDIT_SHORTCUT_CLICKED);
@@ -1366,7 +1347,7 @@
           'aria-label', (queryArgs['removeTooltip'] || '') + ' ' + data.title);
       mdMenu.addEventListener('click', function(ev) {
         removeAllOldTiles();
-        blacklistTile(mdTileContainer);
+        blacklistTile(mdTile);
         ev.preventDefault();
         ev.stopPropagation();
       });
@@ -1378,19 +1359,18 @@
     });
     utils.disableOutlineOnMouseClick(mdMenu);
 
-    mdTileContainer.appendChild(mdMenu);
+    mdTile.appendChild(mdMenu);
   }
 
   if (isGridEnabled) {
-    return currGrid.createGridTile(
-        mdTileContainer, data.tid, !!data.isAddButton);
+    return currGrid.createGridTile(mdTile, data.rid, !!data.isAddButton);
   } else {
     // Enable reordering.
     if (isCustomLinksEnabled && !data.isAddButton) {
-      mdTileContainer.draggable = 'true';
-      setupReorder(mdTileContainer);
+      mdTile.draggable = 'true';
+      setupReorder(mdTile);
     }
-    return mdTileContainer;
+    return mdTile;
   }
 }
 
diff --git a/chrome/browser/resources/management/management_browser_proxy.js b/chrome/browser/resources/management/management_browser_proxy.js
index b25f115..3d73753a 100644
--- a/chrome/browser/resources/management/management_browser_proxy.js
+++ b/chrome/browser/resources/management/management_browser_proxy.js
@@ -30,18 +30,7 @@
 
 /**
  * @typedef {{
- *   overview: string,
- *   setup: string,
- *   data: string,
- * }}
- */
-management.ManagedInfo;
-
-/**
- * @typedef {{
- *   accountManagedInfo: ?management.ManagedInfo,
  *   browserManagementNotice: string,
- *   deviceManagedInfo: ?management.ManagedInfo,
  *   extensionReportingTitle: string,
  *   pageSubtitle: string,
  *   managed: boolean,
diff --git a/chrome/browser/resources/management/management_ui.html b/chrome/browser/resources/management/management_ui.html
index a3b3522..ee5a1aa 100644
--- a/chrome/browser/resources/management/management_ui.html
+++ b/chrome/browser/resources/management/management_ui.html
@@ -194,36 +194,15 @@
               [[subtitle_]]
             </h2>
           </section>
-          <section class="overview-section">
+          <section class="overview-section" hidden="[[!managementOverview_]]">
 <if expr="not chromeos">
             <div inner-h-t-m-l="[[managementNoticeHtml_]]"></div>
 </if>
 <if expr="chromeos">
             <div class="overview-container">
-              <img src="[[customerLogo_]]" hidden="[[!customerLogo_]]"
-                  alt="" aria-hidden="true">
-              <div>
-                <div>[[managementOverview_]]</div>
-                <div>[[deviceManagedInfo_.overview]]</div>
-                <ul class="overview-messages"
-                    hidden="[[!deviceManagedInfo_]]">
-                  <li>
-                    [[deviceManagedInfo_.setup]]
-                    <a href="$i18nRaw{managementDeviceLearnMoreUrl}"
-                        target="_blank">$i18n{learnMore}</a>
-                  </li>
-                  <li>[[deviceManagedInfo_.data]]</li>
-                </ul>
-                <div>[[accountManagedInfo_.overview]]</div>
-                <ul class="overview-messages" hidden="[[!accountManagedInfo_]]">
-                  <li>
-                    [[accountManagedInfo_.setup]]
-                    <a href="$i18nRaw{managementAccountLearnMoreUrl}"
-                        target="_blank">$i18n{learnMore}</a>
-                  </li>
-                  <li>[[accountManagedInfo_.data]]</li>
-                </ul>
-              </div>
+              <img src="[[customerLogo_]]" alt="" aria-hidden="true"
+                  hidden="[[!customerLogo_]]">
+              <div>[[managementOverview_]]</div>
             </div>
 </if>
           </section>
diff --git a/chrome/browser/resources/management/management_ui.js b/chrome/browser/resources/management/management_ui.js
index 12682482..afc2712 100644
--- a/chrome/browser/resources/management/management_ui.js
+++ b/chrome/browser/resources/management/management_ui.js
@@ -51,13 +51,8 @@
     /** @private */
     managementOverview_: String,
 
-    /** @private {?management.ManagedInfo} */
-    deviceManagedInfo_: Object,
     // </if>
 
-    /** @private {?management.ManagedInfo} */
-    accountManagedInfo_: Object,
-
     /** @private */
     subtitle_: String,
 
@@ -256,11 +251,9 @@
       this.managed_ = data.managed;
       this.extensionReportingSubtitle_ = data.extensionReportingTitle;
       this.subtitle_ = data.pageSubtitle;
-      this.accountManagedInfo_ = data.accountManagedInfo;
       // <if expr="chromeos">
       this.customerLogo_ = data.customerLogo;
       this.managementOverview_ = data.overview;
-      this.deviceManagedInfo_ = data.deviceManagedInfo;
       // </if>
       // <if expr="not chromeos">
       this.managementNoticeHtml_ = data.browserManagementNotice;
diff --git a/chrome/browser/resources/policy/policy.html b/chrome/browser/resources/policy/policy.html
index e043cb9a..0cbdeba 100644
--- a/chrome/browser/resources/policy/policy.html
+++ b/chrome/browser/resources/policy/policy.html
@@ -109,6 +109,10 @@
         <div class="label">$i18n{labelStatus}</div>
         <div class="status"></div>
       </div>
+      <div class="status-entry" hidden>
+        <div class="label">$i18n{labelIsAffiliated}</div>
+        <div class="is-affiliated"></div>
+      </div>
     </fieldset>
 
     <div class="policy-table" id="policy-table-template">
diff --git a/chrome/browser/resources/policy/policy_base.js b/chrome/browser/resources/policy/policy_base.js
index 31c815a..848318b9 100644
--- a/chrome/browser/resources/policy/policy_base.js
+++ b/chrome/browser/resources/policy/policy_base.js
@@ -145,6 +145,12 @@
         // Populate the user gaia id.
         this.setLabelAndShow_('.gaia-id', status.gaiaId || notSpecifiedString);
         this.setLabelAndShow_('.client-id', status.clientId);
+        if (status.isAffiliated != null) {
+          this.setLabelAndShow_(
+              '.is-affiliated',
+              loadTimeData.getString(
+                  status.isAffiliated ? 'isAffiliatedYes' : 'isAffiliatedNo'));
+        }
       }
       this.setLabelAndShow_(
           '.time-since-last-refresh', status.timeSinceLastRefresh, false);
diff --git a/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html b/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html
index 8f5a39a9..ddc49a6 100644
--- a/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html
+++ b/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html
@@ -33,7 +33,8 @@
         class="first primary-toggle"
         pref="{{prefs.settings.voice_interaction.enabled}}"
         label="[[getAssistantOnOffLabel_(
-            prefs.settings.voice_interaction.enabled.value)]]">
+            prefs.settings.voice_interaction.enabled.value)]]"
+        disabled="{{prefs.settings.assistant.disabled_by_policy.value}}">
     </settings-toggle-button>
     <template is="dom-if"
         if="[[prefs.settings.voice_interaction.enabled.value]]">
diff --git a/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.js b/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.js
index b68ac142..0fcdc63 100644
--- a/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.js
+++ b/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.js
@@ -113,6 +113,7 @@
     'onPrefsChanged_(prefs.settings.voice_interaction.hotword.always_on.value)',
     `onPrefsChanged_(
       prefs.settings.voice_interaction.activity_control.consent_status.value)`,
+    'onPrefsChanged_(prefs.settings.assistant.disabled_by_policy.value)',
   ],
 
   /** @private {?settings.GoogleAssistantBrowserProxy} */
@@ -187,6 +188,11 @@
 
   /** @private */
   onPrefsChanged_: function() {
+    if (this.getPref('settings.assistant.disabled_by_policy.value')) {
+      this.setPrefValue('settings.voice_interaction.enabled', false);
+      return;
+    }
+
     this.refreshDspHotwordState_();
 
     this.shouldShowVoiceMatchSettings_ =
diff --git a/chrome/browser/resources/settings/os_settings_manifest.json b/chrome/browser/resources/settings/os_settings_manifest.json
new file mode 100644
index 0000000..17d5330
--- /dev/null
+++ b/chrome/browser/resources/settings/os_settings_manifest.json
@@ -0,0 +1,13 @@
+{
+  "name": "Settings",
+  "display": "standalone",
+  "icons": [
+    {
+      "src": "icon-192.png",
+      "sizes": "192x192",
+      "type": "image/png"
+    }
+  ],
+  "start_url": "/",
+  "theme_color": "#F8F9FA"
+}
diff --git a/chrome/browser/resources/settings/os_settings_resources.grd b/chrome/browser/resources/settings/os_settings_resources.grd
index 9cf9b8e..2a797dad 100644
--- a/chrome/browser/resources/settings/os_settings_resources.grd
+++ b/chrome/browser/resources/settings/os_settings_resources.grd
@@ -40,7 +40,7 @@
                  file="a11y_page/tts_subpage.html"
                  type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_MANIFEST"
-                 file="manifest.json"
+                 file="os_settings_manifest.json"
                  type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_ADD_SITE_DIALOG_HTML"
                  file="site_settings/add_site_dialog.html"
diff --git a/chrome/browser/resources/settings/os_settings_resources_vulcanized.grd b/chrome/browser/resources/settings/os_settings_resources_vulcanized.grd
index 04aae4ac..24bcdcc 100644
--- a/chrome/browser/resources/settings/os_settings_resources_vulcanized.grd
+++ b/chrome/browser/resources/settings/os_settings_resources_vulcanized.grd
@@ -38,7 +38,10 @@
                flattenhtml="true"
                type="BINDATA"
                compress="gzip" />
-      <include name="IDR_OS_SETTINGS_MANIFEST" file="manifest.json" type="BINDATA" compress="gzip" />
+      <include name="IDR_OS_SETTINGS_MANIFEST"
+               file="os_settings_manifest.json"
+               type="BINDATA"
+               compress="gzip" />
     </includes>
   </release>
 </grit>
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.cc
index dcc1445..f416e92 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.cc
@@ -637,6 +637,7 @@
     base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO})
         ->PostTask(FROM_HERE, base::BindOnce(std::move(prompt_user_callback),
                                              PromptAcceptance::DENIED));
+    return;
   }
 
   controller->OnPromptUser(std::move(scanner_results),
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc
index 74917f4..e459d52 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc
@@ -476,23 +476,11 @@
 };
 
 MULTIPROCESS_TEST_MAIN(MockChromeCleanerProcessMain) {
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  MockChromeCleanerProcess::Options options;
-  EXPECT_TRUE(MockChromeCleanerProcess::Options::FromCommandLine(*command_line,
-                                                                 &options));
-
-  std::string chrome_mojo_pipe_token = command_line->GetSwitchValueASCII(
-      chrome_cleaner::kChromeMojoPipeTokenSwitch);
-  EXPECT_FALSE(chrome_mojo_pipe_token.empty());
-
-  // Since failures in any of the above calls to EXPECT_*() do not actually fail
-  // the test, we need to ensure that we return an exit code to indicate test
-  // failure in such cases.
+  MockChromeCleanerProcess mock_cleaner_process;
+  EXPECT_TRUE(mock_cleaner_process.InitWithCommandLine(
+      *base::CommandLine::ForCurrentProcess()));
   if (::testing::Test::HasFailure())
     return MockChromeCleanerProcess::kInternalTestFailureExitCode;
-
-  MockChromeCleanerProcess mock_cleaner_process(options,
-                                                chrome_mojo_pipe_token);
   return mock_cleaner_process.Run();
 }
 
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h
index a81fefd..2ba56e22 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h
@@ -13,7 +13,6 @@
 #include "base/macros.h"
 #include "chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_scanner_results_win.h"
 #include "chrome/browser/safe_browsing/chrome_cleaner/sw_reporter_invocation_win.h"
-#include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h"
 
 class Profile;
 
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_reboot_dialog_controller_impl_browsertest_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_reboot_dialog_controller_impl_browsertest_win.cc
index 3ab1e8c7..bfee018 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_reboot_dialog_controller_impl_browsertest_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_reboot_dialog_controller_impl_browsertest_win.cc
@@ -20,6 +20,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/keep_alive_registry/keep_alive_types.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -68,10 +69,9 @@
   }
 
   void OpenPage(const GURL& gurl, Browser* browser) {
-    chrome::AddSelectedTabWithURL(browser, gurl,
-                                  ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
-    content::TestNavigationObserver observer(
-        browser->tab_strip_model()->GetActiveWebContents());
+    content::WebContents* contents = chrome::AddSelectedTabWithURL(
+        browser, gurl, ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
+    content::TestNavigationObserver observer(contents);
     observer.Wait();
   }
 
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
index 9765053..e988225 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
@@ -367,23 +367,11 @@
 };
 
 MULTIPROCESS_TEST_MAIN(MockChromeCleanerProcessMain) {
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  MockChromeCleanerProcess::Options options;
-  EXPECT_TRUE(MockChromeCleanerProcess::Options::FromCommandLine(*command_line,
-                                                                 &options));
-
-  std::string chrome_mojo_pipe_token = command_line->GetSwitchValueASCII(
-      chrome_cleaner::kChromeMojoPipeTokenSwitch);
-  EXPECT_FALSE(chrome_mojo_pipe_token.empty());
-
-  // Since failures in any of the above calls to EXPECT_*() do not actually fail
-  // the test, we need to ensure that we return an exit code to indicate test
-  // failure in such cases.
+  MockChromeCleanerProcess mock_cleaner_process;
+  EXPECT_TRUE(mock_cleaner_process.InitWithCommandLine(
+      *base::CommandLine::ForCurrentProcess()));
   if (::testing::Test::HasFailure())
     return MockChromeCleanerProcess::kInternalTestFailureExitCode;
-
-  MockChromeCleanerProcess mock_cleaner_process(options,
-                                                chrome_mojo_pipe_token);
   return mock_cleaner_process.Run();
 }
 
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc
index 6e1b453..f3bfc5d6 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc
@@ -21,6 +21,7 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/chrome_cleaner/public/constants/constants.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest.h"
@@ -323,10 +324,16 @@
   return kDeclinedExitCode;
 }
 
-MockChromeCleanerProcess::MockChromeCleanerProcess(
-    const Options& options,
-    const std::string& chrome_mojo_pipe_token)
-    : options_(options), chrome_mojo_pipe_token_(chrome_mojo_pipe_token) {}
+bool MockChromeCleanerProcess::InitWithCommandLine(
+    const base::CommandLine& command_line) {
+  if (!Options::FromCommandLine(command_line, &options_))
+    return false;
+  chrome_mojo_pipe_token_ = command_line.GetSwitchValueASCII(
+      chrome_cleaner::kChromeMojoPipeTokenSwitch);
+  if (chrome_mojo_pipe_token_.empty())
+    return false;
+  return true;
+}
 
 int MockChromeCleanerProcess::Run() {
   // We use EXPECT_*() macros to get good log lines, but since this code is run
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h
index 3b82927..facf4a2 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h
@@ -22,16 +22,12 @@
 //
 // MULTIPROCESS_TEST_MAIN(MockChromeCleanerProcessMain) {
 //   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-//   MockChromeCleanerProcess::Options options;
-//   EXPECT_TRUE(MockChromeCleanerProcess::Options::FromCommandLine(
-//       *command_line, &options));
-//   std::string chrome_mojo_pipe_token = ...
-//   EXPECT_FALSE(chrome_mojo_pipe_token.empty())
 //
+//   MockChromeCleanerProcess mock_cleaner_process;
+//   EXPECT_TRUE(mock_cleaner_process.InitWithCommandLine(*command_line));
 //   if (::testing::Test::HasFailure())
 //     return MockChromeCleanerProcess::kInternalTestFailureExitCode;
-//   MockChromeCleanerProcess mock_cleaner_process(options,
-//                                                 chrome_mojo_pipe_token);
+//
 //   return mock_cleaner_process.Run();
 // }
 class MockChromeCleanerProcess {
@@ -157,8 +153,10 @@
         chrome_cleaner::mojom::PromptAcceptance::UNSPECIFIED;
   };
 
-  MockChromeCleanerProcess(const Options& options,
-                           const std::string& chrome_mojo_pipe_token);
+  MockChromeCleanerProcess() = default;
+  ~MockChromeCleanerProcess() = default;
+
+  bool InitWithCommandLine(const base::CommandLine& command_line);
 
   // Call this in the main function of the mock Chrome Cleaner process. Returns
   // the exit code that should be used when the process exits.
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/srt_chrome_prompt_impl_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/srt_chrome_prompt_impl_win.cc
index abd76968..635d53bb 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/srt_chrome_prompt_impl_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/srt_chrome_prompt_impl_win.cc
@@ -53,26 +53,25 @@
       ChromeCleanerScannerResults::RegistryKeyCollection;
   using ExtensionCollection = ChromeCleanerScannerResults::ExtensionCollection;
 
-  if (on_prompt_user_) {
-    if (base::FeatureList::IsEnabled(kChromeCleanupExtensionsFeature) &&
-        extension_ids) {
-      extension_ids_ = extension_ids.value();
-    } else {
-      extension_ids_.clear();
-    }
-
-    ChromeCleanerScannerResults scanner_results(
-        FileCollection(files_to_delete.begin(), files_to_delete.end()),
-        registry_keys ? RegistryKeyCollection(registry_keys->begin(),
-                                              registry_keys->end())
-                      : RegistryKeyCollection(),
-        extension_ids_.empty() ? ExtensionCollection()
-                               : ExtensionCollection(extension_ids_.begin(),
-                                                     extension_ids_.end()));
-
-    std::move(on_prompt_user_)
-        .Run(std::move(scanner_results), std::move(callback));
+  DCHECK(on_prompt_user_);
+  if (base::FeatureList::IsEnabled(kChromeCleanupExtensionsFeature) &&
+      extension_ids) {
+    extension_ids_ = extension_ids.value();
+  } else {
+    extension_ids_.clear();
   }
+
+  ChromeCleanerScannerResults scanner_results(
+      FileCollection(files_to_delete.begin(), files_to_delete.end()),
+      registry_keys
+          ? RegistryKeyCollection(registry_keys->begin(), registry_keys->end())
+          : RegistryKeyCollection(),
+      extension_ids_.empty()
+          ? ExtensionCollection()
+          : ExtensionCollection(extension_ids_.begin(), extension_ids_.end()));
+
+  std::move(on_prompt_user_)
+      .Run(std::move(scanner_results), std::move(callback));
 }
 
 // The |extensions_ids| passed to this function are a subset of the
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/srt_delete_extension_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/srt_delete_extension_win_unittest.cc
index b3cbde5..ee41999a 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/srt_delete_extension_win_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/srt_delete_extension_win_unittest.cc
@@ -158,6 +158,7 @@
       std::make_unique<ChromePromptImpl>(extension_service, nullptr,
                                          base::DoNothing(), base::DoNothing());
 
+  // Call DisableExtensions without prompting.
   std::vector<base::string16> extensions_to_disable{extension_ids[0]};
   chrome_prompt->DisableExtensions(
       extensions_to_disable,
@@ -186,6 +187,7 @@
                 base::UTF16ToUTF8(extension_ids[2])),
             nullptr);
 
+  // Prompt for an extension but try to disable different ones.
   chrome_prompt->PromptUser({}, {}, {{extension_ids[2]}}, base::DoNothing());
   chrome_prompt->DisableExtensions(
       {extension_ids[0], extension_ids[1]},
@@ -244,6 +246,7 @@
       std::make_unique<ChromePromptImpl>(extension_service, nullptr,
                                          base::DoNothing(), base::DoNothing());
 
+  chrome_prompt->PromptUser({}, {}, extension_ids, base::DoNothing());
   chrome_prompt->DisableExtensions(
       extension_ids, base::BindOnce([](bool result) { EXPECT_FALSE(result); }));
 }
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc
index 206e9c4..187214bf 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -48,6 +48,7 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/resource_request_info.h"
 #include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/cross_thread_shared_url_loader_factory_info.h"
 #include "services/network/public/cpp/features.h"
 #include "services/preferences/public/mojom/tracked_preference_validation_delegate.mojom.h"
 
@@ -240,21 +241,6 @@
     network_context_->FlushForTesting();
 }
 
-scoped_refptr<network::SharedURLLoaderFactory>
-SafeBrowsingService::GetURLLoaderFactoryOnIOThread() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (!shared_url_loader_factory_on_io_) {
-    base::PostTaskWithTraits(
-        FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(&SafeBrowsingService::CreateURLLoaderFactoryForIO, this,
-                       MakeRequest(&url_loader_factory_on_io_)));
-    shared_url_loader_factory_on_io_ =
-        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-            url_loader_factory_on_io_.get());
-  }
-  return shared_url_loader_factory_on_io_;
-}
-
 const scoped_refptr<SafeBrowsingUIManager>& SafeBrowsingService::ui_manager()
     const {
   return ui_manager_;
@@ -362,7 +348,8 @@
   services_delegate_->SetDatabaseManagerForTest(database_manager);
 }
 
-void SafeBrowsingService::StartOnIOThread() {
+void SafeBrowsingService::StartOnIOThread(
+    std::unique_ptr<network::SharedURLLoaderFactoryInfo> url_loader_factory) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (enabled_)
     return;
@@ -370,8 +357,9 @@
 
   V4ProtocolConfig v4_config = GetV4ProtocolConfig();
 
-  services_delegate_->StartOnIOThread(GetURLLoaderFactoryOnIOThread(),
-                                      v4_config);
+  services_delegate_->StartOnIOThread(
+      network::SharedURLLoaderFactory::Create(std::move(url_loader_factory)),
+      v4_config);
 }
 
 void SafeBrowsingService::StopOnIOThread(bool shutdown) {
@@ -382,10 +370,6 @@
   if (enabled_) {
     enabled_ = false;
   }
-
-  if (shared_url_loader_factory_on_io_)
-    shared_url_loader_factory_on_io_->Detach();
-  url_loader_factory_on_io_.reset();
 }
 
 void SafeBrowsingService::Start() {
@@ -398,7 +382,10 @@
 
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(&SafeBrowsingService::StartOnIOThread, this));
+      base::BindOnce(
+          &SafeBrowsingService::StartOnIOThread, this,
+          std::make_unique<network::CrossThreadSharedURLLoaderFactoryInfo>(
+              GetURLLoaderFactory())));
 }
 
 void SafeBrowsingService::Stop(bool shutdown) {
@@ -526,19 +513,6 @@
       g_browser_process->local_state());
 }
 
-void SafeBrowsingService::CreateURLLoaderFactoryForIO(
-    network::mojom::URLLoaderFactoryRequest request) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (shutdown_)
-    return;  // We've been shut down already.
-  network::mojom::URLLoaderFactoryParamsPtr params =
-      network::mojom::URLLoaderFactoryParams::New();
-  params->process_id = network::mojom::kBrowserProcessId;
-  params->is_corb_enabled = false;
-  GetNetworkContext()->CreateURLLoaderFactory(std::move(request),
-                                              std::move(params));
-}
-
 network::mojom::NetworkContextParamsPtr
 SafeBrowsingService::CreateNetworkContextParams() {
   auto params = g_browser_process->system_network_context_manager()
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.h b/chrome/browser/safe_browsing/safe_browsing_service.h
index 73032a5..2eb6375 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.h
+++ b/chrome/browser/safe_browsing/safe_browsing_service.h
@@ -27,7 +27,6 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/public/mojom/network_context.mojom-forward.h"
 
 #if defined(FULL_SAFE_BROWSING)
@@ -52,6 +51,7 @@
 class NetworkContext;
 }
 class SharedURLLoaderFactory;
+class SharedURLLoaderFactoryInfo;
 }  // namespace network
 
 namespace prefs {
@@ -137,10 +137,6 @@
   // Flushes above two interfaces to avoid races in tests.
   void FlushNetworkInterfaceForTesting();
 
-  // Called to get a SharedURLLoaderFactory that can be used on the IO thread.
-  scoped_refptr<network::SharedURLLoaderFactory>
-  GetURLLoaderFactoryOnIOThread();
-
   const scoped_refptr<SafeBrowsingUIManager>& ui_manager() const;
 
   virtual const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager()
@@ -228,7 +224,8 @@
 
   // Called to initialize objects that are used on the io_thread.  This may be
   // called multiple times during the life of the SafeBrowsingService.
-  void StartOnIOThread();
+  void StartOnIOThread(
+      std::unique_ptr<network::SharedURLLoaderFactoryInfo> url_loader_factory);
 
   // Called to stop or shutdown operations on the io_thread. This may be called
   // multiple times to stop during the life of the SafeBrowsingService. If
@@ -266,11 +263,6 @@
 
   void CreateTriggerManager();
 
-  // Called on the UI thread to create a URLLoaderFactory interface ptr for
-  // the IO thread.
-  void CreateURLLoaderFactoryForIO(
-      network::mojom::URLLoaderFactoryRequest request);
-
   // Creates a configured NetworkContextParams when the network service is in
   // use.
   network::mojom::NetworkContextParamsPtr CreateNetworkContextParams();
@@ -289,11 +281,6 @@
   // SimpleURLLoader for safe browsing requests.
   std::unique_ptr<safe_browsing::SafeBrowsingNetworkContext> network_context_;
 
-  // A SharedURLLoaderFactory and its interfaceptr used on the IO thread.
-  network::mojom::URLLoaderFactoryPtr url_loader_factory_on_io_;
-  scoped_refptr<network::WeakWrapperSharedURLLoaderFactory>
-      shared_url_loader_factory_on_io_;
-
   // Provides phishing and malware statistics. Accessed on UI thread.
   std::unique_ptr<PingManager> ping_manager_;
 
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc
index 6fc31f3..b858066 100644
--- a/chrome/browser/sessions/session_restore.cc
+++ b/chrome/browser/sessions/session_restore.cc
@@ -226,7 +226,7 @@
           use_new_window ? 0 : browser->tab_strip_model()->active_index() + 1;
       web_contents = chrome::AddRestoredTab(
           browser, tab.navigations, tab_index, selected_index,
-          tab.extension_app_id,
+          tab.extension_app_id, base::nullopt,
           disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB,  // selected
           tab.pinned, true, base::TimeTicks(), nullptr, tab.user_agent_override,
           true /* from_session_restore */);
@@ -531,6 +531,11 @@
         latest_last_active_time = tab.last_active_time;
     }
 
+    // TODO(crbug.com/930991): Check that tab groups are contiguous in |window|
+    // to ensure tabs will not be reordered when restoring. This is not possible
+    // yet due the ordering of TabStripModelObserver notifications in an edge
+    // case.
+
     for (int i = 0; i < static_cast<int>(window.tabs.size()); ++i) {
       const sessions::SessionTab& tab = *(window.tabs[i]);
 
@@ -585,16 +590,14 @@
 
     WebContents* web_contents = chrome::AddRestoredTab(
         browser, tab.navigations, tab_index, selected_index,
-        tab.extension_app_id, is_selected_tab, tab.pinned, true,
+        tab.extension_app_id, tab.group, is_selected_tab, tab.pinned, true,
         last_active_time, session_storage_namespace.get(),
         tab.user_agent_override, true /* from_session_restore */);
-
-    // RestoreTab can return nullptr if |tab| doesn't have valid data.
-    if (!web_contents)
-      return;
+    DCHECK(web_contents);
 
     RestoredTab restored_tab(web_contents, is_selected_tab,
-                             tab.extension_app_id.empty(), tab.pinned);
+                             tab.extension_app_id.empty(), tab.pinned,
+                             tab.group);
     created_contents->push_back(restored_tab);
 
     // If this isn't the selected tab, there's nothing else to do.
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index 9a0b0a3c..e50cb2cd0 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -9,6 +9,7 @@
 
 #include "base/base_switches.h"
 #include "base/command_line.h"
+#include "base/containers/span.h"
 #include "base/files/file_path.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
@@ -39,6 +40,7 @@
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_group_id.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/url_constants.h"
@@ -872,6 +874,134 @@
             new_browser->tab_strip_model()->GetActiveWebContents()->GetURL());
 }
 
+namespace {
+
+// Groups the tabs in |model| according to |specified_groups|.
+void CreateTabGroups(TabStripModel* model,
+                     base::span<const base::Optional<int>> specified_groups) {
+  ASSERT_EQ(model->count(), static_cast<int>(specified_groups.size()));
+
+  // Maps |specified_groups| IDs to actual group IDs in |model|.
+  base::flat_map<int, TabGroupId> group_map;
+
+  for (int i = 0; i < model->count(); ++i) {
+    if (specified_groups[i] == base::nullopt)
+      continue;
+
+    const int specified_group = specified_groups[i].value();
+    auto match = group_map.find(specified_group);
+
+    // If |group_map| doesn't contain a value for |specified_group|, we can
+    // assume we haven't created the group yet.
+    if (match == group_map.end()) {
+      const TabGroupId actual_group = model->AddToNewGroup({i});
+      group_map.insert(std::make_pair(specified_group, actual_group));
+    } else {
+      const content::WebContents* const contents = model->GetWebContentsAt(i);
+      model->AddToExistingGroup({i}, match->second);
+      // Make sure we didn't move the tab.
+      EXPECT_EQ(contents, model->GetWebContentsAt(i));
+    }
+  }
+}
+
+// Checks that the grouping of tabs in |model| is equivalent to that specified
+// in |specified_groups| up to relabeling of the group IDs.
+void CheckTabGrouping(TabStripModel* model,
+                      base::span<const base::Optional<int>> specified_groups) {
+  ASSERT_EQ(model->count(), static_cast<int>(specified_groups.size()));
+
+  // Maps |specified_groups| IDs to actual group IDs in |model|.
+  base::flat_map<int, TabGroupId> group_map;
+
+  for (int i = 0; i < model->count(); ++i) {
+    SCOPED_TRACE(i);
+
+    const base::Optional<int> specified_group = specified_groups[i];
+    const base::Optional<TabGroupId> actual_group = model->GetTabGroupForTab(i);
+
+    // The tab should be grouped iff it's grouped in |specified_groups|.
+    EXPECT_EQ(actual_group.has_value(), specified_group.has_value());
+
+    if (actual_group.has_value() && specified_group.has_value()) {
+      auto match = group_map.find(specified_group.value());
+      if (match == group_map.end()) {
+        group_map.insert(
+            std::make_pair(specified_group.value(), actual_group.value()));
+      } else {
+        EXPECT_EQ(actual_group.value(), match->second);
+      }
+    }
+  }
+}
+
+// Returns the optional group ID for each tab in a vector.
+std::vector<base::Optional<TabGroupId>> GetTabGroups(
+    const TabStripModel* model) {
+  std::vector<base::Optional<TabGroupId>> result(model->count());
+  for (int i = 0; i < model->count(); ++i)
+    result[i] = model->GetTabGroupForTab(i);
+  return result;
+}
+
+}  // namespace
+
+IN_PROC_BROWSER_TEST_F(SessionRestoreTest, TabsWithGroups) {
+  constexpr int kNumTabs = 6;
+  const std::array<base::Optional<int>, kNumTabs> group_spec = {
+      0, 0, base::nullopt, base::nullopt, 1, 1};
+
+  // Open |kNumTabs| tabs.
+  ui_test_utils::NavigateToURL(browser(), url1_);
+  for (int i = 1; i < kNumTabs; ++i) {
+    ui_test_utils::NavigateToURLWithDisposition(
+        browser(), url1_, WindowOpenDisposition::NEW_FOREGROUND_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  }
+  ASSERT_EQ(kNumTabs, browser()->tab_strip_model()->count());
+
+  CreateTabGroups(browser()->tab_strip_model(), group_spec);
+  ASSERT_NO_FATAL_FAILURE(
+      CheckTabGrouping(browser()->tab_strip_model(), group_spec));
+  const auto groups = GetTabGroups(browser()->tab_strip_model());
+
+  Browser* new_browser = QuitBrowserAndRestore(browser(), kNumTabs);
+  ASSERT_EQ(kNumTabs, new_browser->tab_strip_model()->count());
+  EXPECT_EQ(groups, GetTabGroups(new_browser->tab_strip_model()));
+}
+
+// Test that tab groups are restored correctly after the command set is rebuilt
+// from the browser state.
+IN_PROC_BROWSER_TEST_F(SessionRestoreTest, TabsWithGroupsCommandReset) {
+  constexpr int kNumTabs = 6;
+  const std::array<base::Optional<int>, kNumTabs> group_spec = {
+      0, 0, base::nullopt, base::nullopt, 1, 1};
+
+  // Open |kNumTabs| tabs.
+  ui_test_utils::NavigateToURL(browser(), url1_);
+  for (int i = 1; i < kNumTabs; ++i) {
+    ui_test_utils::NavigateToURLWithDisposition(
+        browser(), url1_, WindowOpenDisposition::NEW_FOREGROUND_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  }
+  ASSERT_EQ(kNumTabs, browser()->tab_strip_model()->count());
+
+  CreateTabGroups(browser()->tab_strip_model(), group_spec);
+  ASSERT_NO_FATAL_FAILURE(
+      CheckTabGrouping(browser()->tab_strip_model(), group_spec));
+  const auto groups = GetTabGroups(browser()->tab_strip_model());
+
+  // Rebuild commands.
+  SessionService* const session_service =
+      SessionServiceFactory::GetForProfile(browser()->profile());
+  ASSERT_TRUE(session_service);
+  session_service->ResetFromCurrentBrowsers();
+
+  Browser* new_browser = QuitBrowserAndRestore(browser(), kNumTabs);
+  ASSERT_EQ(kNumTabs, new_browser->tab_strip_model()->count());
+  EXPECT_EQ(groups, GetTabGroups(new_browser->tab_strip_model()));
+}
+
 IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoreAfterDelete) {
   ui_test_utils::NavigateToURL(browser(), url1_);
   ui_test_utils::NavigateToURL(browser(), url2_);
diff --git a/chrome/browser/sessions/session_restore_delegate.cc b/chrome/browser/sessions/session_restore_delegate.cc
index c5ca690..7a8cdaa 100644
--- a/chrome/browser/sessions/session_restore_delegate.cc
+++ b/chrome/browser/sessions/session_restore_delegate.cc
@@ -38,16 +38,21 @@
 
 }  // namespace
 
-SessionRestoreDelegate::RestoredTab::RestoredTab(content::WebContents* contents,
-                                                 bool is_active,
-                                                 bool is_app,
-                                                 bool is_pinned)
+SessionRestoreDelegate::RestoredTab::RestoredTab(
+    content::WebContents* contents,
+    bool is_active,
+    bool is_app,
+    bool is_pinned,
+    const base::Optional<base::Token>& group)
     : contents_(contents),
       is_active_(is_active),
       is_app_(is_app),
       is_internal_page_(IsInternalPage(contents->GetLastCommittedURL())),
-      is_pinned_(is_pinned) {
-}
+      is_pinned_(is_pinned),
+      group_(group) {}
+
+SessionRestoreDelegate::RestoredTab::RestoredTab(const RestoredTab& other) =
+    default;
 
 bool SessionRestoreDelegate::RestoredTab::operator<(
     const RestoredTab& right) const {
diff --git a/chrome/browser/sessions/session_restore_delegate.h b/chrome/browser/sessions/session_restore_delegate.h
index 9e5b021..4d0e69d 100644
--- a/chrome/browser/sessions/session_restore_delegate.h
+++ b/chrome/browser/sessions/session_restore_delegate.h
@@ -8,7 +8,10 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/time/time.h"
+#include "base/token.h"
+#include "components/sessions/core/session_id.h"
 
 namespace content {
 class WebContents;
@@ -23,7 +26,9 @@
     RestoredTab(content::WebContents* contents,
                 bool is_active,
                 bool is_app,
-                bool is_pinned);
+                bool is_pinned,
+                const base::Optional<base::Token>& group);
+    RestoredTab(const RestoredTab& other);
 
     bool operator<(const RestoredTab& right) const;
 
@@ -32,6 +37,7 @@
     bool is_app() const { return is_app_; }
     bool is_internal_page() const { return is_internal_page_; }
     bool is_pinned() const { return is_pinned_; }
+    const base::Optional<base::Token>& group() const { return group_; }
 
    private:
     content::WebContents* contents_;
@@ -39,6 +45,9 @@
     bool is_app_;            // Browser window is an app.
     bool is_internal_page_;  // Internal web UI page, like NTP or Settings.
     bool is_pinned_;
+    // The ID for the tab group that this tab belonged to, if any. See
+    // |TabStripModel::AddToNewGroup()| for more documentation.
+    base::Optional<base::Token> group_;
   };
 
   static void RestoreTabs(const std::vector<RestoredTab>& tabs,
diff --git a/chrome/browser/sessions/session_restore_observer_unittest.cc b/chrome/browser/sessions/session_restore_observer_unittest.cc
index 4a3fdfd..d4f7891 100644
--- a/chrome/browser/sessions/session_restore_observer_unittest.cc
+++ b/chrome/browser/sessions/session_restore_observer_unittest.cc
@@ -83,7 +83,8 @@
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
     SetContents(CreateRestoredWebContents());
-    restored_tabs_.emplace_back(web_contents(), false, false, false);
+    restored_tabs_.emplace_back(web_contents(), false, false, false,
+                                base::nullopt);
   }
 
   void TearDown() override {
@@ -174,7 +175,7 @@
     different_test_contents.emplace_back(CreateRestoredWebContents());
     content::WebContents* test_contents = different_test_contents.back().get();
     std::vector<RestoredTab> restored_tabs{
-        RestoredTab(test_contents, false, false, false)};
+        RestoredTab(test_contents, false, false, false, base::nullopt)};
 
     SessionRestore::NotifySessionRestoreStartedLoadingTabs();
     SessionRestore::OnWillRestoreTab(test_contents);
@@ -198,7 +199,8 @@
 TEST_F(SessionRestoreObserverTest, ConcurrentSessionRestores) {
   std::vector<RestoredTab> another_restored_tabs;
   auto test_contents = CreateRestoredWebContents();
-  another_restored_tabs.emplace_back(test_contents.get(), false, false, false);
+  another_restored_tabs.emplace_back(test_contents.get(), false, false, false,
+                                     base::nullopt);
 
   SessionRestore::NotifySessionRestoreStartedLoadingTabs();
   SessionRestore::OnWillRestoreTab(web_contents());
@@ -226,7 +228,7 @@
 
   std::vector<SessionRestoreDelegate::RestoredTab> restored_tabs{
       SessionRestoreDelegate::RestoredTab(test_contents.get(), false, false,
-                                          false)};
+                                          false, base::nullopt)};
 
   resource_coordinator::TabManager* tab_manager =
       g_browser_process->GetTabManager();
diff --git a/chrome/browser/sessions/session_restore_stats_collector_unittest.cc b/chrome/browser/sessions/session_restore_stats_collector_unittest.cc
index a57ec4b4..f0a7c5c 100644
--- a/chrome/browser/sessions/session_restore_stats_collector_unittest.cc
+++ b/chrome/browser/sessions/session_restore_stats_collector_unittest.cc
@@ -278,7 +278,8 @@
     // Create a last active time in the past.
     content::WebContentsTester::For(contents)->SetLastActiveTime(
         base::TimeTicks::Now() - base::TimeDelta::FromMinutes(1));
-    restored_tabs_.push_back(RestoredTab(contents, is_active, false, false));
+    restored_tabs_.push_back(
+        RestoredTab(contents, is_active, false, false, base::nullopt));
     if (is_active)
       Show(restored_tabs_.size() - 1);
   }
diff --git a/chrome/browser/sessions/session_service.cc b/chrome/browser/sessions/session_service.cc
index 3add921..8e5be04 100644
--- a/chrome/browser/sessions/session_service.cc
+++ b/chrome/browser/sessions/session_service.cc
@@ -16,6 +16,7 @@
 #include "base/command_line.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/pickle.h"
+#include "base/stl_util.h"
 #include "base/threading/thread.h"
 #include "build/build_config.h"
 #include "chrome/browser/background/background_mode_manager.h"
@@ -37,6 +38,7 @@
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/startup/startup_browser_creator.h"
+#include "chrome/browser/ui/tabs/tab_group_id.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "components/sessions/content/content_serialized_navigation_builder.h"
@@ -198,6 +200,21 @@
       sessions::CreateSetTabIndexInWindowCommand(tab_id, new_index));
 }
 
+void SessionService::SetTabGroup(const SessionID& window_id,
+                                 const SessionID& tab_id,
+                                 base::Optional<base::Token> group) {
+  if (!ShouldTrackChangesToWindow(window_id))
+    return;
+
+  // Tabs get ungrouped as they close. However, if the whole window is closing
+  // tabs should stay in their groups. So, ignore this call in that case.
+  if (base::ContainsKey(pending_window_close_ids_, window_id) ||
+      base::ContainsKey(window_closing_ids_, window_id))
+    return;
+
+  ScheduleCommand(sessions::CreateTabGroupCommand(tab_id, group));
+}
+
 void SessionService::SetPinnedState(const SessionID& window_id,
                                     const SessionID& tab_id,
                                     bool is_pinned) {
@@ -464,7 +481,9 @@
   if (!ShouldTrackChangesToWindow(session_tab_helper->window_id()))
     return;
 
-  BuildCommandsForTab(session_tab_helper->window_id(), tab, -1, pinned, NULL);
+  // TODO(crbug.com/930991): handle tab groups here.
+  BuildCommandsForTab(session_tab_helper->window_id(), tab, -1, base::nullopt,
+                      pinned, NULL);
   base_session_service_->StartSaveTimer();
 }
 
@@ -642,6 +661,7 @@
 void SessionService::BuildCommandsForTab(const SessionID& window_id,
                                          WebContents* tab,
                                          int index_in_window,
+                                         base::Optional<base::Token> group,
                                          bool is_pinned,
                                          IdToRange* tab_to_available_range) {
   DCHECK(tab);
@@ -710,6 +730,11 @@
                                                    index_in_window));
   }
 
+  if (group.has_value()) {
+    base_session_service_->AppendRebuildCommand(
+        sessions::CreateTabGroupCommand(session_id, group));
+  }
+
   // Record the association between the sessionStorage namespace and the tab.
   content::SessionStorageNamespace* session_storage_namespace =
       tab->GetController().GetDefaultSessionStorageNamespace();
@@ -750,11 +775,12 @@
   for (int i = 0; i < tab_strip->count(); ++i) {
     WebContents* tab = tab_strip->GetWebContentsAt(i);
     DCHECK(tab);
-    BuildCommandsForTab(browser->session_id(),
-                        tab,
-                        i,
-                        tab_strip->IsTabPinned(i),
-                        tab_to_available_range);
+    const base::Optional<TabGroupId> group_id = tab_strip->GetTabGroupForTab(i);
+    const base::Optional<base::Token> raw_group_id =
+        group_id.has_value() ? base::make_optional(group_id.value().token())
+                             : base::nullopt;
+    BuildCommandsForTab(browser->session_id(), tab, i, raw_group_id,
+                        tab_strip->IsTabPinned(i), tab_to_available_range);
   }
 
   base_session_service_->AppendRebuildCommand(
diff --git a/chrome/browser/sessions/session_service.h b/chrome/browser/sessions/session_service.h
index f6fb5ae..f4b2daef 100644
--- a/chrome/browser/sessions/session_service.h
+++ b/chrome/browser/sessions/session_service.h
@@ -13,8 +13,10 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "base/time/time.h"
+#include "base/token.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/sessions/session_common_utils.h"
 #include "chrome/browser/sessions/session_service_utils.h"
@@ -129,6 +131,11 @@
                            const SessionID& tab_id,
                            int new_index);
 
+  // Sets a tab's group ID, if any.
+  void SetTabGroup(const SessionID& window_id,
+                   const SessionID& tab_id,
+                   base::Optional<base::Token> group);
+
   // Sets the pinned state of the tab.
   void SetPinnedState(const SessionID& window_id,
                       const SessionID& tab_id,
@@ -268,12 +275,12 @@
   // direction from the current navigation index).
   // A pair is added to tab_to_available_range indicating the range of
   // indices that were written.
-  void BuildCommandsForTab(
-      const SessionID& window_id,
-      content::WebContents* tab,
-      int index_in_window,
-      bool is_pinned,
-      IdToRange* tab_to_available_range);
+  void BuildCommandsForTab(const SessionID& window_id,
+                           content::WebContents* tab,
+                           int index_in_window,
+                           base::Optional<base::Token> group,
+                           bool is_pinned,
+                           IdToRange* tab_to_available_range);
 
   // Adds commands to create the specified browser, and invokes
   // BuildCommandsForTab for each of the tabs in the browser. This ignores
diff --git a/chrome/browser/sessions/session_service_unittest.cc b/chrome/browser/sessions/session_service_unittest.cc
index c09e9cd6..89eef283 100644
--- a/chrome/browser/sessions/session_service_unittest.cc
+++ b/chrome/browser/sessions/session_service_unittest.cc
@@ -21,6 +21,7 @@
 #include "base/synchronization/atomic_flag.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
+#include "base/token.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/defaults.h"
@@ -39,7 +40,9 @@
 #include "components/sessions/core/session_command.h"
 #include "components/sessions/core/session_types.h"
 #include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/common/page_state.h"
+#include "content/public/test/web_contents_tester.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using content::NavigationEntry;
@@ -87,6 +90,16 @@
     }
   }
 
+  SessionID CreateTabWithTestNavigationData(SessionID window_id,
+                                            int visual_index) {
+    const SessionID tab_id = SessionID::NewUnique();
+    const SerializedNavigationEntry nav =
+        SerializedNavigationEntryTestHelper::CreateNavigationForTest();
+    helper_.PrepareTabInWindow(window_id, tab_id, visual_index, true);
+    UpdateNavigation(window_id, tab_id, nav, true);
+    return tab_id;
+  }
+
   void ReadWindows(
       std::vector<std::unique_ptr<sessions::SessionWindow>>* windows,
       SessionID* active_window_id) {
@@ -1164,6 +1177,46 @@
   helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
 }
 
+TEST_F(SessionServiceTest, TabGroupDefaultsToNone) {
+  CreateTabWithTestNavigationData(window_id, 0);
+
+  std::vector<std::unique_ptr<sessions::SessionWindow>> windows;
+  ReadWindows(&windows, nullptr);
+
+  ASSERT_EQ(1U, windows.size());
+  ASSERT_EQ(1U, windows[0]->tabs.size());
+
+  // Verify that the recorded tab has no group.
+  sessions::SessionTab* tab = windows[0]->tabs[0].get();
+  EXPECT_EQ(base::nullopt, tab->group);
+}
+
+TEST_F(SessionServiceTest, TabGroupIdsSaved) {
+  const auto group1_token = base::Token::CreateRandom();
+  const auto group2_token = base::Token::CreateRandom();
+  constexpr int kNumTabs = 5;
+  const std::array<base::Optional<base::Token>, kNumTabs> groups = {
+      base::nullopt, group1_token, group1_token, base::nullopt, group2_token};
+
+  // Create |kNumTabs| tabs with group IDs in |groups|.
+  for (int tab_ndx = 0; tab_ndx < kNumTabs; ++tab_ndx) {
+    const SessionID tab_id =
+        CreateTabWithTestNavigationData(window_id, tab_ndx);
+    service()->SetTabGroup(window_id, tab_id, groups[tab_ndx]);
+  }
+
+  std::vector<std::unique_ptr<sessions::SessionWindow>> windows;
+  ReadWindows(&windows, nullptr);
+
+  ASSERT_EQ(1U, windows.size());
+  ASSERT_EQ(kNumTabs, static_cast<int>(windows[0]->tabs.size()));
+
+  for (int tab_ndx = 0; tab_ndx < kNumTabs; ++tab_ndx) {
+    sessions::SessionTab* tab = windows[0]->tabs[tab_ndx].get();
+    EXPECT_EQ(groups[tab_ndx], tab->group);
+  }
+}
+
 // Functions used by GetSessionsAndDestroy.
 namespace {
 
diff --git a/chrome/browser/sessions/tab_loader_unittest.cc b/chrome/browser/sessions/tab_loader_unittest.cc
index 8d4c561e..4f79c90 100644
--- a/chrome/browser/sessions/tab_loader_unittest.cc
+++ b/chrome/browser/sessions/tab_loader_unittest.cc
@@ -103,9 +103,9 @@
     // TabLoadTracker needs the resource_coordinator WebContentsData to be
     // initialized.
     ResourceCoordinatorTabHelper::CreateForWebContents(test_contents);
-    restored_tabs_.push_back(
-        RestoredTab(test_contents, is_active /* is_active */,
-                    false /* is_app */, false /* is_pinned */));
+    restored_tabs_.push_back(RestoredTab(
+        test_contents, is_active /* is_active */, false /* is_app */,
+        false /* is_pinned */, base::nullopt /* group */));
 
     // If the tab is active start "loading" it right away for consistency with
     // session restore code.
diff --git a/chrome/browser/sync/chrome_sync_client.cc b/chrome/browser/sync/chrome_sync_client.cc
index 5341b4c..d7732cc 100644
--- a/chrome/browser/sync/chrome_sync_client.cc
+++ b/chrome/browser/sync/chrome_sync_client.cc
@@ -45,7 +45,6 @@
 #include "chrome/common/pref_names.h"
 #include "components/autofill/core/browser/webdata/autocomplete_sync_bridge.h"
 #include "components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h"
-#include "components/autofill/core/browser/webdata/autofill_profile_syncable_service.h"
 #include "components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.h"
 #include "components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h"
 #include "components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.h"
@@ -424,13 +423,6 @@
       return PrefServiceSyncableFromProfile(profile_)
           ->GetSyncableService(syncer::PRIORITY_PREFERENCES)
           ->AsWeakPtr();
-    case syncer::AUTOFILL_PROFILE:
-      if (profile_web_data_service_) {
-        return autofill::AutofillProfileSyncableService::FromWebDataService(
-                   profile_web_data_service_.get())
-            ->AsWeakPtr();
-      }
-      return nullptr;
     case syncer::AUTOFILL_WALLET_METADATA:
       if (profile_web_data_service_) {
         return autofill::AutofillWalletMetadataSyncableService::
diff --git a/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.cc b/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.cc
index 49b0c89e..4d6ed91 100644
--- a/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.cc
+++ b/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.cc
@@ -7,6 +7,7 @@
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/browser/sync/sessions/sync_sessions_web_contents_router.h"
+#include "components/language/core/common/language_experiments.h"
 #include "components/sync_sessions/synced_tab_delegate.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
@@ -80,8 +81,8 @@
 
 void SyncSessionsRouterTabHelper::OnLanguageDetermined(
     const translate::LanguageDetectionDetails& details) {
-  // TODO (crbug.com/957657): NotifyRouter() when language is synced on
-  // notification.
+  if (base::FeatureList::IsEnabled(language::kNotifySyncOnLanguageDetermined))
+    NotifyRouter();
 }
 
 void SyncSessionsRouterTabHelper::SetSourceTabIdForChild(
diff --git a/chrome/browser/sync/test/integration/bookmarks_helper.cc b/chrome/browser/sync/test/integration/bookmarks_helper.cc
index 672757d2..02d9c9a 100644
--- a/chrome/browser/sync/test/integration/bookmarks_helper.cc
+++ b/chrome/browser/sync/test/integration/bookmarks_helper.cc
@@ -94,15 +94,15 @@
   }
   void BookmarkNodeMoved(BookmarkModel* model,
                          const BookmarkNode* old_parent,
-                         int old_index,
+                         size_t old_index,
                          const BookmarkNode* new_parent,
-                         int new_index) override {}
+                         size_t new_index) override {}
   void BookmarkNodeAdded(BookmarkModel* model,
                          const BookmarkNode* parent,
-                         int index) override {}
+                         size_t index) override {}
   void BookmarkNodeRemoved(BookmarkModel* model,
                            const BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override {}
   void BookmarkAllUserNodesRemoved(
@@ -137,13 +137,13 @@
 
 // Returns the number of nodes of node type |node_type| in |model| whose
 // titles match the string |title|.
-int CountNodesWithTitlesMatching(BookmarkModel* model,
-                                 BookmarkNode::Type node_type,
-                                 const base::string16& title) {
+size_t CountNodesWithTitlesMatching(BookmarkModel* model,
+                                    BookmarkNode::Type node_type,
+                                    const base::string16& title) {
   ui::TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
   // Walk through the model tree looking for bookmark nodes of node type
   // |node_type| whose titles match |title|.
-  int count = 0;
+  size_t count = 0;
   while (iterator.has_next()) {
     const BookmarkNode* node = iterator.Next();
     if ((node->type() == node_type) && (node->GetTitle() == title))
@@ -153,11 +153,11 @@
 }
 
 // Returns the number of nodes of node type |node_type| in |model|.
-int CountNodes(BookmarkModel* model, BookmarkNode::Type node_type) {
+size_t CountNodes(BookmarkModel* model, BookmarkNode::Type node_type) {
   ui::TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
   // Walk through the model tree looking for bookmark nodes of node type
   // |node_type|.
-  int count = 0;
+  size_t count = 0;
   while (iterator.has_next()) {
     const BookmarkNode* node = iterator.Next();
     if (node->type() == node_type)
@@ -498,7 +498,7 @@
 }
 
 const BookmarkNode* AddURL(int profile,
-                           int index,
+                           size_t index,
                            const std::string& title,
                            const GURL& url) {
   return AddURL(profile, GetBookmarkBarNode(profile), index, title, url);
@@ -506,7 +506,7 @@
 
 const BookmarkNode* AddURL(int profile,
                            const BookmarkNode* parent,
-                           int index,
+                           size_t index,
                            const std::string& title,
                            const GURL& url) {
   BookmarkModel* model = GetBookmarkModel(profile);
@@ -542,14 +542,14 @@
 }
 
 const BookmarkNode* AddFolder(int profile,
-                              int index,
+                              size_t index,
                               const std::string& title) {
   return AddFolder(profile, GetBookmarkBarNode(profile), index, title);
 }
 
 const BookmarkNode* AddFolder(int profile,
                               const BookmarkNode* parent,
-                              int index,
+                              size_t index,
                               const std::string& title) {
   BookmarkModel* model = GetBookmarkModel(profile);
   if (bookmarks::GetBookmarkNodeByID(model, parent->id()) != parent) {
@@ -726,7 +726,7 @@
 void Move(int profile,
           const BookmarkNode* node,
           const BookmarkNode* new_parent,
-          int index) {
+          size_t index) {
   BookmarkModel* model = GetBookmarkModel(profile);
   ASSERT_EQ(bookmarks::GetBookmarkNodeByID(model, node->id()), node)
       << "Node " << node->GetTitle() << " does not belong to "
@@ -741,7 +741,7 @@
   model->Move(node, new_parent, index);
 }
 
-void Remove(int profile, const BookmarkNode* parent, int index) {
+void Remove(int profile, const BookmarkNode* parent, size_t index) {
   BookmarkModel* model = GetBookmarkModel(profile);
   ASSERT_EQ(bookmarks::GetBookmarkNodeByID(model, parent->id()), parent)
       << "Node " << parent->GetTitle() << " does not belong to "
@@ -749,10 +749,11 @@
   if (sync_datatype_helper::test()->use_verifier()) {
     const BookmarkNode* v_parent = nullptr;
     FindNodeInVerifier(model, parent, &v_parent);
-    ASSERT_TRUE(NodesMatch(parent->GetChild(index), v_parent->GetChild(index)));
-    GetVerifierBookmarkModel()->Remove(v_parent->GetChild(index));
+    ASSERT_TRUE(NodesMatch(parent->children()[index].get(),
+                           v_parent->children()[index].get()));
+    GetVerifierBookmarkModel()->Remove(v_parent->children()[index].get());
   }
-  model->Remove(parent->GetChild(index));
+  model->Remove(parent->children()[index].get());
 }
 
 void RemoveAll(int profile) {
@@ -787,11 +788,11 @@
       parent)
       << "Node " << parent->GetTitle() << " does not belong to "
       << "Profile " << profile;
-  int child_count = parent->child_count();
-  if (child_count <= 0)
+  if (parent->children().empty())
     return;
-  for (int index = 0; index < child_count; ++index) {
-    Move(profile, parent->GetChild(index), parent, child_count - index);
+  for (size_t i = 0; i < parent->children().size(); ++i) {
+    Move(profile, parent->children()[i].get(), parent,
+         parent->children().size() - i);
   }
 }
 
@@ -877,23 +878,23 @@
   return nodes[0];
 }
 
-int CountAllBookmarks(int profile) {
+size_t CountAllBookmarks(int profile) {
   return CountNodes(GetBookmarkModel(profile), BookmarkNode::URL);
 }
 
-int CountBookmarksWithTitlesMatching(int profile, const std::string& title) {
+size_t CountBookmarksWithTitlesMatching(int profile, const std::string& title) {
   return CountNodesWithTitlesMatching(GetBookmarkModel(profile),
                                       BookmarkNode::URL,
                                       base::UTF8ToUTF16(title));
 }
 
-int CountBookmarksWithUrlsMatching(int profile, const GURL& url) {
+size_t CountBookmarksWithUrlsMatching(int profile, const GURL& url) {
   std::vector<const BookmarkNode*> nodes;
   GetBookmarkModel(profile)->GetNodesByURL(url, &nodes);
   return nodes.size();
 }
 
-int CountFoldersWithTitlesMatching(int profile, const std::string& title) {
+size_t CountFoldersWithTitlesMatching(int profile, const std::string& title) {
   return CountNodesWithTitlesMatching(GetBookmarkModel(profile),
                                       BookmarkNode::FOLDER,
                                       base::UTF8ToUTF16(title));
@@ -934,24 +935,24 @@
       base::RefCountedString::TakeString(&contents));
 }
 
-std::string IndexedURL(int i) {
-  return base::StringPrintf("http://www.host.ext:1234/path/filename/%d", i);
+std::string IndexedURL(size_t i) {
+  return "http://www.host.ext:1234/path/filename/" + base::NumberToString(i);
 }
 
-std::string IndexedURLTitle(int i) {
-  return base::StringPrintf("URL Title %d", i);
+std::string IndexedURLTitle(size_t i) {
+  return "URL Title " + base::NumberToString(i);
 }
 
-std::string IndexedFolderName(int i) {
-  return base::StringPrintf("Folder Name %d", i);
+std::string IndexedFolderName(size_t i) {
+  return "Folder Name " + base::NumberToString(i);
 }
 
-std::string IndexedSubfolderName(int i) {
-  return base::StringPrintf("Subfolder Name %d", i);
+std::string IndexedSubfolderName(size_t i) {
+  return "Subfolder Name " + base::NumberToString(i);
 }
 
-std::string IndexedSubsubfolderName(int i) {
-  return base::StringPrintf("Subsubfolder Name %d", i);
+std::string IndexedSubsubfolderName(size_t i) {
+  return "Subsubfolder Name " + base::NumberToString(i);
 }
 
 std::unique_ptr<syncer::LoopbackServerEntity> CreateBookmarkServerEntity(
diff --git a/chrome/browser/sync/test/integration/bookmarks_helper.h b/chrome/browser/sync/test/integration/bookmarks_helper.h
index 5902c17..f2ddf29 100644
--- a/chrome/browser/sync/test/integration/bookmarks_helper.h
+++ b/chrome/browser/sync/test/integration/bookmarks_helper.h
@@ -61,7 +61,7 @@
 // profile |profile| at position |index|. Returns a pointer to the node that
 // was added.
 const bookmarks::BookmarkNode* AddURL(int profile,
-                                      int index,
+                                      size_t index,
                                       const std::string& title,
                                       const GURL& url) WARN_UNUSED_RESULT;
 
@@ -70,7 +70,7 @@
 // was added.
 const bookmarks::BookmarkNode* AddURL(int profile,
                                       const bookmarks::BookmarkNode* parent,
-                                      int index,
+                                      size_t index,
                                       const std::string& title,
                                       const GURL& url) WARN_UNUSED_RESULT;
 
@@ -82,7 +82,7 @@
 // Adds a folder named |title| to the bookmark bar of profile |profile| at
 // position |index|. Returns a pointer to the folder that was added.
 const bookmarks::BookmarkNode* AddFolder(int profile,
-                                         int index,
+                                         size_t index,
                                          const std::string& title)
     WARN_UNUSED_RESULT;
 
@@ -91,7 +91,7 @@
 // was added.
 const bookmarks::BookmarkNode* AddFolder(int profile,
                                          const bookmarks::BookmarkNode* parent,
-                                         int index,
+                                         size_t index,
                                          const std::string& title)
     WARN_UNUSED_RESULT;
 
@@ -140,11 +140,11 @@
 void Move(int profile,
           const bookmarks::BookmarkNode* node,
           const bookmarks::BookmarkNode* new_parent,
-          int index);
+          size_t index);
 
 // Removes the node in the bookmark model of profile |profile| under the node
 // |parent| at position |index|.
-void Remove(int profile, const bookmarks::BookmarkNode* parent, int index);
+void Remove(int profile, const bookmarks::BookmarkNode* parent, size_t index);
 
 // Removes all non-permanent nodes in the bookmark model of profile |profile|.
 void RemoveAll(int profile);
@@ -188,24 +188,22 @@
     WARN_UNUSED_RESULT;
 
 // Returns the number of bookmarks in bookmark model of profile |profile|.
-int CountAllBookmarks(int profile) WARN_UNUSED_RESULT;
+size_t CountAllBookmarks(int profile) WARN_UNUSED_RESULT;
 
 // Returns the number of bookmarks in bookmark model of profile |profile|
 // whose titles match the string |title|.
-int CountBookmarksWithTitlesMatching(
-    int profile,
-    const std::string& title) WARN_UNUSED_RESULT;
+size_t CountBookmarksWithTitlesMatching(int profile, const std::string& title)
+    WARN_UNUSED_RESULT;
 
 // Returns the number of bookmarks in bookmark model of profile |profile|
 // whose URLs match the |url|.
-int CountBookmarksWithUrlsMatching(int profile,
-                                   const GURL& url) WARN_UNUSED_RESULT;
+size_t CountBookmarksWithUrlsMatching(int profile,
+                                      const GURL& url) WARN_UNUSED_RESULT;
 
 // Returns the number of bookmark folders in the bookmark model of profile
 // |profile| whose titles contain the query string |title|.
-int CountFoldersWithTitlesMatching(
-    int profile,
-    const std::string& title) WARN_UNUSED_RESULT;
+size_t CountFoldersWithTitlesMatching(int profile, const std::string& title)
+    WARN_UNUSED_RESULT;
 
 // Creates a favicon of |color| with image reps of the platform's supported
 // scale factors (eg MacOS) in addition to 1x.
@@ -215,19 +213,19 @@
 gfx::Image Create1xFaviconFromPNGFile(const std::string& path);
 
 // Returns a URL identifiable by |i|.
-std::string IndexedURL(int i);
+std::string IndexedURL(size_t i);
 
 // Returns a URL title identifiable by |i|.
-std::string IndexedURLTitle(int i);
+std::string IndexedURLTitle(size_t i);
 
 // Returns a folder name identifiable by |i|.
-std::string IndexedFolderName(int i);
+std::string IndexedFolderName(size_t i);
 
 // Returns a subfolder name identifiable by |i|.
-std::string IndexedSubfolderName(int i);
+std::string IndexedSubfolderName(size_t i);
 
 // Returns a subsubfolder name identifiable by |i|.
-std::string IndexedSubsubfolderName(int i);
+std::string IndexedSubsubfolderName(size_t i);
 
 // Creates a server-side entity representing a bookmark with the given title and
 // URL.
diff --git a/chrome/browser/sync/test/integration/performance/autofill_sync_perf_test.cc b/chrome/browser/sync/test/integration/performance/autofill_sync_perf_test.cc
index f0b81cc..f838bfd 100644
--- a/chrome/browser/sync/test/integration/performance/autofill_sync_perf_test.cc
+++ b/chrome/browser/sync/test/integration/performance/autofill_sync_perf_test.cc
@@ -44,7 +44,7 @@
 }
 
 void ForceSync(int profile) {
-  static int id = 0;
+  static size_t id = 0;
   ++id;
   EXPECT_TRUE(bookmarks_helper::AddURL(
                   profile, 0, bookmarks_helper::IndexedURLTitle(id),
diff --git a/chrome/browser/sync/test/integration/performance/bookmarks_sync_perf_test.cc b/chrome/browser/sync/test/integration/performance/bookmarks_sync_perf_test.cc
index db033a2..c6880c3 100644
--- a/chrome/browser/sync/test/integration/performance/bookmarks_sync_perf_test.cc
+++ b/chrome/browser/sync/test/integration/performance/bookmarks_sync_perf_test.cc
@@ -23,10 +23,7 @@
 
 class BookmarksSyncPerfTest : public SyncTest {
  public:
-  BookmarksSyncPerfTest()
-      : SyncTest(TWO_CLIENT),
-        url_number_(0),
-        url_title_number_(0) {}
+  BookmarksSyncPerfTest() : SyncTest(TWO_CLIENT) {}
 
   // Adds |num_urls| new unique bookmarks to the bookmark bar for |profile|.
   void AddURLs(int profile, int num_urls);
@@ -47,8 +44,8 @@
   // Returns a new unique bookmark title.
   std::string NextIndexedURLTitle();
 
-  int url_number_;
-  int url_title_number_;
+  size_t url_number_ = 0;
+  size_t url_title_number_ = 0;
   DISALLOW_COPY_AND_ASSIGN(BookmarksSyncPerfTest);
 };
 
diff --git a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
index 613f5a9..0799ba7 100644
--- a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
@@ -168,15 +168,15 @@
   ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
 
   ASSERT_EQ(1, tier1_a_url0->parent()->GetIndexOf(tier1_a_url0));
-  Move(kSingleProfileIndex, tier1_a_url0, bar, bar->child_count());
-  const BookmarkNode* boa = AddURL(
-      kSingleProfileIndex, bar, bar->child_count(),
-      "Bank of America", GURL("https://www.bankofamerica.com"));
+  Move(kSingleProfileIndex, tier1_a_url0, bar, bar->children().size());
+  const BookmarkNode* boa =
+      AddURL(kSingleProfileIndex, bar, bar->children().size(),
+             "Bank of America", GURL("https://www.bankofamerica.com"));
   ASSERT_NE(nullptr, boa);
-  Move(kSingleProfileIndex, tier1_a_url0, top, top->child_count());
-  const BookmarkNode* bubble = AddURL(
-      kSingleProfileIndex, bar, bar->child_count(), "Seattle Bubble",
-          GURL("http://seattlebubble.com"));
+  Move(kSingleProfileIndex, tier1_a_url0, top, top->children().size());
+  const BookmarkNode* bubble =
+      AddURL(kSingleProfileIndex, bar, bar->children().size(), "Seattle Bubble",
+             GURL("http://seattlebubble.com"));
   ASSERT_NE(nullptr, bubble);
   const BookmarkNode* wired = AddURL(
       kSingleProfileIndex, bar, 2, "Wired News", GURL("http://www.wired.com"));
@@ -193,7 +193,7 @@
   ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
 
   ASSERT_EQ(tier1_a_url0->id(), top->GetChild(top->child_count() - 1)->id());
-  Remove(kSingleProfileIndex, top, top->child_count() - 1);
+  Remove(kSingleProfileIndex, top, top->children().size() - 1);
   Move(kSingleProfileIndex, wired, tier1_b, 0);
   Move(kSingleProfileIndex, porsche, bar, 3);
   const BookmarkNode* tier3_b = AddFolder(
@@ -272,7 +272,7 @@
   ASSERT_TRUE(SetupClients());
   ASSERT_TRUE(SetupSync());
 
-  EXPECT_EQ(1, CountBookmarksWithTitlesMatching(kSingleProfileIndex, title));
+  EXPECT_EQ(1u, CountBookmarksWithTitlesMatching(kSingleProfileIndex, title));
 }
 
 // Test that a client doesn't mutate the favicon data in the process
@@ -421,7 +421,7 @@
   DisableVerifier();
   ASSERT_TRUE(SetupSync());
 
-  ASSERT_EQ(1, CountBookmarksWithTitlesMatching(kSingleProfileIndex, title));
+  ASSERT_EQ(1u, CountBookmarksWithTitlesMatching(kSingleProfileIndex, title));
 
   std::vector<sync_pb::SyncEntity> server_bookmarks =
       GetFakeServer()->GetSyncEntitiesByModelType(syncer::BOOKMARKS);
@@ -454,11 +454,11 @@
   DisableVerifier();
   ASSERT_TRUE(SetupSync());
 
-  ASSERT_EQ(1, CountBookmarksWithTitlesMatching(kSingleProfileIndex, title));
-  ASSERT_EQ(1, CountBookmarksWithUrlsMatching(kSingleProfileIndex,
-                                              original_url));
-  ASSERT_EQ(0, CountBookmarksWithUrlsMatching(kSingleProfileIndex,
-                                              updated_url));
+  ASSERT_EQ(1u, CountBookmarksWithTitlesMatching(kSingleProfileIndex, title));
+  ASSERT_EQ(1u,
+            CountBookmarksWithUrlsMatching(kSingleProfileIndex, original_url));
+  ASSERT_EQ(0u,
+            CountBookmarksWithUrlsMatching(kSingleProfileIndex, updated_url));
 
   std::vector<sync_pb::SyncEntity> server_bookmarks =
       GetFakeServer()->GetSyncEntitiesByModelType(syncer::BOOKMARKS);
@@ -474,9 +474,9 @@
   TriggerSyncForModelTypes(kSingleProfileIndex, kBookmarksType);
 
   ASSERT_TRUE(BookmarksUrlChecker(kSingleProfileIndex, updated_url, 1).Wait());
-  ASSERT_EQ(0, CountBookmarksWithUrlsMatching(kSingleProfileIndex,
-                                              original_url));
-  ASSERT_EQ(1, CountBookmarksWithTitlesMatching(kSingleProfileIndex, title));
+  ASSERT_EQ(0u,
+            CountBookmarksWithUrlsMatching(kSingleProfileIndex, original_url));
+  ASSERT_EQ(1u, CountBookmarksWithTitlesMatching(kSingleProfileIndex, title));
 }
 
 IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, DownloadBookmarkFolder) {
@@ -488,11 +488,11 @@
 
   DisableVerifier();
   ASSERT_TRUE(SetupClients());
-  ASSERT_EQ(0, CountFoldersWithTitlesMatching(kSingleProfileIndex, title));
+  ASSERT_EQ(0u, CountFoldersWithTitlesMatching(kSingleProfileIndex, title));
 
   ASSERT_TRUE(SetupSync());
 
-  ASSERT_EQ(1, CountFoldersWithTitlesMatching(kSingleProfileIndex, title));
+  ASSERT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex, title));
 }
 
 IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
@@ -505,11 +505,11 @@
 
   DisableVerifier();
   ASSERT_TRUE(SetupClients());
-  ASSERT_EQ(0, CountFoldersWithTitlesMatching(kSingleProfileIndex, title));
+  ASSERT_EQ(0u, CountFoldersWithTitlesMatching(kSingleProfileIndex, title));
 
   ASSERT_TRUE(SetupSync());
 
-  ASSERT_EQ(1, CountFoldersWithTitlesMatching(kSingleProfileIndex, title));
+  ASSERT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex, title));
 }
 
 // Legacy bookmark clients append a blank space to empty titles, ".", ".." tiles
@@ -572,8 +572,8 @@
 
   // There should be bookmark with illegal title (without the appended space).
   for (const std::string& illegal_title : illegal_titles) {
-    EXPECT_EQ(1, CountBookmarksWithTitlesMatching(kSingleProfileIndex,
-                                                  illegal_title));
+    EXPECT_EQ(1u, CountBookmarksWithTitlesMatching(kSingleProfileIndex,
+                                                   illegal_title));
   }
 }
 
@@ -599,17 +599,17 @@
       AddFolder(kSingleProfileIndex, GetBookmarkBarNode(kSingleProfileIndex), 0,
                 local_empty_title);
   ASSERT_TRUE(node);
-  ASSERT_EQ(1, CountFoldersWithTitlesMatching(kSingleProfileIndex,
-                                              local_empty_title));
+  ASSERT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex,
+                                               local_empty_title));
 
   ASSERT_TRUE(SetupSync());
   // There should be only one bookmark on the client. The remote node should
   // have been merged with the local node and either the local or remote titles
   // is picked.
-  EXPECT_EQ(1, CountFoldersWithTitlesMatching(kSingleProfileIndex,
-                                              local_empty_title) +
-                   CountFoldersWithTitlesMatching(kSingleProfileIndex,
-                                                  remote_blank_title));
+  EXPECT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex,
+                                               local_empty_title) +
+                    CountFoldersWithTitlesMatching(kSingleProfileIndex,
+                                                   remote_blank_title));
 }
 
 // Legacy bookmark clients truncate long titles up to 255 bytes. This tests that
@@ -641,17 +641,17 @@
       AddFolder(kSingleProfileIndex, GetBookmarkBarNode(kSingleProfileIndex), 0,
                 local_full_title);
   ASSERT_TRUE(node);
-  ASSERT_EQ(
-      1, CountFoldersWithTitlesMatching(kSingleProfileIndex, local_full_title));
+  ASSERT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex,
+                                               local_full_title));
 
   ASSERT_TRUE(SetupSync());
   // There should be only one bookmark on the client. The remote node should
   // have been merged with the local node and either the local or remote title
   // is picked.
-  EXPECT_EQ(
-      1, CountFoldersWithTitlesMatching(kSingleProfileIndex, local_full_title) +
-             CountFoldersWithTitlesMatching(kSingleProfileIndex,
-                                            remote_truncated_title));
+  EXPECT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex,
+                                               local_full_title) +
+                    CountFoldersWithTitlesMatching(kSingleProfileIndex,
+                                                   remote_truncated_title));
 }
 
 IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
@@ -683,9 +683,9 @@
 
   ASSERT_TRUE(SetupSync());
 
-  EXPECT_EQ(1, CountFoldersWithTitlesMatching(kSingleProfileIndex, title0));
-  EXPECT_EQ(1, CountFoldersWithTitlesMatching(kSingleProfileIndex, title1));
-  EXPECT_EQ(1, CountFoldersWithTitlesMatching(kSingleProfileIndex, title2));
+  EXPECT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex, title0));
+  EXPECT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex, title1));
+  EXPECT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex, title2));
 
   const BookmarkNode* bar = GetBookmarkBarNode(kSingleProfileIndex);
   ASSERT_EQ(3, bar->child_count());
diff --git a/chrome/browser/sync/test/integration/sync_auth_test.cc b/chrome/browser/sync/test/integration/sync_auth_test.cc
index 1882517..efdcf77 100644
--- a/chrome/browser/sync/test/integration/sync_auth_test.cc
+++ b/chrome/browser/sync/test/integration/sync_auth_test.cc
@@ -409,8 +409,8 @@
   // Create a bookmark...
   const bookmarks::BookmarkNode* bar = bookmarks_helper::GetBookmarkBarNode(0);
   ASSERT_FALSE(bookmarks_helper::HasNodeWithURL(0, kTestURL));
-  const bookmarks::BookmarkNode* bookmark =
-      bookmarks_helper::AddURL(0, bar, bar->child_count(), "Title", kTestURL);
+  const bookmarks::BookmarkNode* bookmark = bookmarks_helper::AddURL(
+      0, bar, bar->children().size(), "Title", kTestURL);
 
   // ...set a pref...
   ASSERT_FALSE(HasUserPrefValue(pref_service, prefs::kHomePageIsNewTabPage));
@@ -444,7 +444,7 @@
   // Note that AttemptToTriggerAuthError() also creates bookmarks, so the index
   // of our test bookmark might have changed.
   ASSERT_EQ(bar->GetChild(bar->child_count() - 1), bookmark);
-  bookmarks_helper::Remove(0, bar, bar->child_count() - 1);
+  bookmarks_helper::Remove(0, bar, bar->children().size() - 1);
   ASSERT_FALSE(bookmarks_helper::HasNodeWithURL(0, kTestURL));
   pref_service->ClearPref(prefs::kHomePageIsNewTabPage);
 
diff --git a/chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc b/chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc
index 3a491785..f206f0d 100644
--- a/chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc
@@ -300,8 +300,9 @@
 
 // Tests that adding the same profile on the two clients before sync is started
 // results in each client only having one profile after sync is started
+// Flaky on all platform, crbug.com/971644.
 IN_PROC_BROWSER_TEST_F(TwoClientAutofillProfileSyncTest,
-                       ClientsAddSameProfile) {
+                       DISABLED_ClientsAddSameProfile) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed";
   base::HistogramTester histograms;
 
diff --git a/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
index f45a1f4..7732357 100644
--- a/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
@@ -387,7 +387,7 @@
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
-  for (int i = 0; i < 20; ++i) {
+  for (size_t i = 0; i < 20; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, i, title, url));
@@ -401,7 +401,7 @@
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
-  for (int i = 0; i < 15; ++i) {
+  for (size_t i = 0; i < 15; ++i) {
     if (base::RandDouble() > 0.6) {
       std::string title = IndexedURLTitle(i);
       GURL url = GURL(IndexedURL(i));
@@ -411,15 +411,15 @@
       const BookmarkNode* folder = AddFolder(0, i, title);
       ASSERT_NE(nullptr, folder);
       if (base::RandDouble() > 0.4) {
-        for (int i = 0; i < 20; ++i) {
-          std::string title = IndexedURLTitle(i);
-          GURL url = GURL(IndexedURL(i));
-          ASSERT_NE(nullptr, AddURL(0, folder, i, title, url));
+        for (size_t j = 0; j < 20; ++j) {
+          std::string title = IndexedURLTitle(j);
+          GURL url = GURL(IndexedURL(j));
+          ASSERT_NE(nullptr, AddURL(0, folder, j, title, url));
         }
       }
     }
   }
-  for (int i = 0; i < 10; i++) {
+  for (size_t i = 0; i < 10; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, GetOtherNode(0), i, title, url));
@@ -557,7 +557,7 @@
   std::string title = IndexedFolderName(1);
   const BookmarkNode* folder = AddFolder(0, title);
   ASSERT_NE(nullptr, folder);
-  for (int i = 0; i < 120; ++i) {
+  for (size_t i = 0; i < 120; ++i) {
     if (base::RandDouble() > 0.15) {
       std::string title = IndexedURLTitle(i);
       GURL url = GURL(IndexedURL(i));
@@ -581,14 +581,14 @@
 
   const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
   ASSERT_NE(nullptr, folder);
-  for (int i = 1; i < 15; ++i) {
+  for (size_t i = 1; i < 15; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, i, title, url));
   }
   std::string title = IndexedSubfolderName(1);
   const BookmarkNode* subfolder = AddFolder(0, folder, 0, title);
-  for (int i = 0; i < 120; ++i) {
+  for (size_t i = 0; i < 120; ++i) {
     if (base::RandDouble() > 0.15) {
       std::string title = IndexedURLTitle(i);
       GURL url = GURL(IndexedURL(i));
@@ -641,7 +641,7 @@
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
-  for (int i = 0; i < 20; ++i) {
+  for (size_t i = 0; i < 20; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, i, title, url));
@@ -659,7 +659,7 @@
 
   const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
   ASSERT_NE(nullptr, folder);
-  for (int i = 0; i < 10; ++i) {
+  for (size_t i = 0; i < 10; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, folder, i, title, url));
@@ -677,14 +677,14 @@
 
   const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
   ASSERT_NE(nullptr, folder);
-  for (int i = 0; i < 10; ++i) {
+  for (size_t i = 0; i < 10; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, folder, i, title, url));
   }
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 
-  Remove(0, folder, folder->child_count() - 1);
+  Remove(0, folder, folder->children().size() - 1);
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
@@ -695,7 +695,7 @@
 
   const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
   ASSERT_NE(nullptr, folder);
-  for (int i = 0; i < 10; ++i) {
+  for (size_t i = 0; i < 10; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, folder, i, title, url));
@@ -713,17 +713,15 @@
 
   const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
   ASSERT_NE(nullptr, folder);
-  for (int i = 0; i < 10; ++i) {
+  for (size_t i = 0; i < 10; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, folder, i, title, url));
   }
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 
-  int child_count = folder->child_count();
-  for (int i = 0; i < child_count; ++i) {
+  for (size_t i = 0; i < folder->children().size(); ++i)
     Remove(0, folder, 0);
-  }
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
@@ -745,7 +743,7 @@
   ASSERT_TRUE(AllModelsMatchVerifier());
 
   ASSERT_NE(nullptr, AddFolder(0, kGenericFolderName));
-  for (int i = 1; i < 15; ++i) {
+  for (size_t i = 1; i < 15; ++i) {
     if (base::RandDouble() > 0.6) {
       std::string title = IndexedURLTitle(i);
       GURL url = GURL(IndexedURL(i));
@@ -769,7 +767,7 @@
   ASSERT_NE(nullptr, AddURL(0, kGenericURLTitle, GURL(kGenericURL)));
   const BookmarkNode* folder = AddFolder(0, 1, kGenericFolderName);
   ASSERT_NE(nullptr, folder);
-  for (int i = 2; i < 10; ++i) {
+  for (size_t i = 2; i < 10; ++i) {
     if (base::RandDouble() > 0.6) {
       std::string title = IndexedURLTitle(i);
       GURL url = GURL(IndexedURL(i));
@@ -779,7 +777,7 @@
       ASSERT_NE(nullptr, AddFolder(0, i, title));
     }
   }
-  for (int i = 0; i < 15; ++i) {
+  for (size_t i = 0; i < 15; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, folder, i, title, url));
@@ -798,7 +796,7 @@
   ASSERT_NE(nullptr, AddURL(0, kGenericURLTitle, GURL(kGenericURL)));
   const BookmarkNode* folder = AddFolder(0, 1, kGenericFolderName);
   ASSERT_NE(nullptr, folder);
-  for (int i = 2; i < 10; ++i) {
+  for (size_t i = 2; i < 10; ++i) {
     if (base::RandDouble() > 0.6) {
       std::string title = IndexedURLTitle(i);
       GURL url = GURL(IndexedURL(i));
@@ -808,7 +806,7 @@
       ASSERT_NE(nullptr, AddFolder(0, i, title));
     }
   }
-  for (int i = 0; i < 10; ++i) {
+  for (size_t i = 0; i < 10; ++i) {
     if (base::RandDouble() > 0.6) {
       std::string title = IndexedURLTitle(i);
       GURL url = GURL(IndexedURL(i));
@@ -819,7 +817,7 @@
           AddFolder(0, folder, i, title);
       ASSERT_NE(nullptr, subfolder);
       if (base::RandDouble() > 0.3) {
-        for (int j = 0; j < 10; ++j) {
+        for (size_t j = 0; j < 10; ++j) {
           if (base::RandDouble() > 0.6) {
             std::string title = IndexedURLTitle(j);
             GURL url = GURL(IndexedURL(j));
@@ -845,7 +843,7 @@
 
   const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
   ASSERT_NE(nullptr, folder);
-  for (int i = 1; i < 11; ++i) {
+  for (size_t i = 1; i < 11; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, i, title, url));
@@ -853,7 +851,7 @@
   const BookmarkNode* subfolder =
       AddFolder(0, folder, 0, kGenericSubfolderName);
   ASSERT_NE(nullptr, subfolder);
-  for (int i = 0; i < 30; ++i) {
+  for (size_t i = 0; i < 30; ++i) {
     if (base::RandDouble() > 0.2) {
       std::string title = IndexedURLTitle(i);
       GURL url = GURL(IndexedURL(i));
@@ -893,7 +891,7 @@
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
-  for (int i = 0; i < 10; ++i) {
+  for (size_t i = 0; i < 10; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, i, title, url));
@@ -912,18 +910,16 @@
   ASSERT_NE(nullptr, AddURL(0, kGenericURLTitle, GURL(kGenericURL)));
   const BookmarkNode* folder = AddFolder(0, 1, kGenericFolderName);
   ASSERT_NE(nullptr, folder);
-  for (int i = 2; i < 10; ++i) {
+  for (size_t i = 2; i < 10; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, i, title, url));
   }
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 
-  int num_bookmarks_to_move =
-      GetBookmarkBarNode(0)->child_count() - 2;
-  for (int i = 0; i < num_bookmarks_to_move; ++i) {
-    Move(
-        0, GetBookmarkBarNode(0)->GetChild(2), folder, i);
+  size_t num_bookmarks_to_move = GetBookmarkBarNode(0)->children().size() - 2;
+  for (size_t i = 0; i < num_bookmarks_to_move; ++i) {
+    Move(0, GetBookmarkBarNode(0)->GetChild(2), folder, i);
     ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
     ASSERT_TRUE(AllModelsMatchVerifier());
   }
@@ -937,15 +933,15 @@
   ASSERT_NE(nullptr, AddURL(0, kGenericURLTitle, GURL(kGenericURL)));
   const BookmarkNode* folder = AddFolder(0, 1, kGenericFolderName);
   ASSERT_NE(nullptr, folder);
-  for (int i = 0; i < 10; ++i) {
+  for (size_t i = 0; i < 10; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, folder, i, title, url));
   }
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 
-  int num_bookmarks_to_move = folder->child_count() - 2;
-  for (int i = 0; i < num_bookmarks_to_move; ++i) {
+  size_t num_bookmarks_to_move = folder->children().size() - 2;
+  for (size_t i = 0; i < num_bookmarks_to_move; ++i) {
     Move(0, folder->GetChild(0), GetBookmarkBarNode(0), i);
     ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
     ASSERT_TRUE(AllModelsMatchVerifier());
@@ -959,7 +955,7 @@
 
   const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
   ASSERT_NE(nullptr, folder);
-  for (int i = 0; i < 3; ++i) {
+  for (size_t i = 0; i < 3; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, folder, i, title, url));
@@ -967,14 +963,14 @@
   const BookmarkNode* subfolder =
       AddFolder(0, folder, 3, kGenericSubfolderName);
   ASSERT_NE(nullptr, subfolder);
-  for (int i = 0; i < 10; ++i) {
+  for (size_t i = 0; i < 10; ++i) {
     std::string title = IndexedURLTitle(i + 3);
     GURL url = GURL(IndexedURL(i + 3));
     ASSERT_NE(nullptr, AddURL(0, subfolder, i, title, url));
   }
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 
-  for (int i = 0; i < 3; ++i) {
+  for (size_t i = 0; i < 3; ++i) {
     GURL url = GURL(IndexedURL(i));
     Move(0, GetUniqueNodeByURL(0, url), subfolder, i + 10);
   }
@@ -988,7 +984,7 @@
 
   const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
   ASSERT_NE(nullptr, folder);
-  for (int i = 0; i < 3; ++i) {
+  for (size_t i = 0; i < 3; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, folder, i, title, url));
@@ -996,14 +992,14 @@
   const BookmarkNode* subfolder =
       AddFolder(0, folder, 3, kGenericSubfolderName);
   ASSERT_NE(nullptr, subfolder);
-  for (int i = 0; i < 5; ++i) {
+  for (size_t i = 0; i < 5; ++i) {
     std::string title = IndexedURLTitle(i + 3);
     GURL url = GURL(IndexedURL(i + 3));
     ASSERT_NE(nullptr, AddURL(0, subfolder, i, title, url));
   }
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 
-  for (int i = 0; i < 3; ++i) {
+  for (size_t i = 0; i < 3; ++i) {
     GURL url = GURL(IndexedURL(i + 3));
     Move(0, GetUniqueNodeByURL(0, url), folder, i + 4);
   }
@@ -1018,20 +1014,20 @@
   const BookmarkNode* folder = GetBookmarkBarNode(0);
   const BookmarkNode* folder_L0 = nullptr;
   const BookmarkNode* folder_L10 = nullptr;
-  for (int level = 0; level < 15; ++level) {
-    int num_bookmarks = base::RandInt(0, 9);
-    for (int i = 0; i < num_bookmarks; ++i) {
+  for (size_t level = 0; level < 15; ++level) {
+    size_t num_bookmarks = base::RandInt(0, 9);
+    for (size_t i = 0; i < num_bookmarks; ++i) {
       std::string title = IndexedURLTitle(i);
       GURL url = GURL(IndexedURL(i));
       ASSERT_NE(nullptr, AddURL(0, folder, i, title, url));
     }
     std::string title = IndexedFolderName(level);
-    folder = AddFolder(0, folder, folder->child_count(), title);
+    folder = AddFolder(0, folder, folder->children().size(), title);
     ASSERT_NE(nullptr, folder);
     if (level == 0) folder_L0 = folder;
     if (level == 10) folder_L10 = folder;
   }
-  for (int i = 0; i < 3; ++i) {
+  for (size_t i = 0; i < 3; ++i) {
     std::string title = IndexedURLTitle(i + 10);
     GURL url = GURL(IndexedURL(i + 10));
     ASSERT_NE(nullptr, AddURL(0, folder_L10, i, title, url));
@@ -1039,8 +1035,8 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 
   GURL url10 = GURL(IndexedURL(10));
-  Move(0, GetUniqueNodeByURL(
-      0, url10), folder_L0, folder_L0->child_count());
+  Move(0, GetUniqueNodeByURL(0, url10), folder_L0,
+       folder_L0->children().size());
   GURL url11 = GURL(IndexedURL(11));
   Move(0, GetUniqueNodeByURL(0, url11), folder_L0, 0);
   GURL url12 = GURL(IndexedURL(12));
@@ -1057,20 +1053,20 @@
   const BookmarkNode* folder = GetBookmarkBarNode(0);
   const BookmarkNode* folder_L0 = nullptr;
   const BookmarkNode* folder_L10 = nullptr;
-  for (int level = 0; level < 15; ++level) {
-    int num_bookmarks = base::RandInt(0, 9);
-    for (int i = 0; i < num_bookmarks; ++i) {
+  for (size_t level = 0; level < 15; ++level) {
+    size_t num_bookmarks = base::RandInt(0, 9);
+    for (size_t i = 0; i < num_bookmarks; ++i) {
       std::string title = IndexedURLTitle(i);
       GURL url = GURL(IndexedURL(i));
       ASSERT_NE(nullptr, AddURL(0, folder, i, title, url));
     }
     std::string title = IndexedFolderName(level);
-    folder = AddFolder(0, folder, folder->child_count(), title);
+    folder = AddFolder(0, folder, folder->children().size(), title);
     ASSERT_NE(nullptr, folder);
     if (level == 0) folder_L0 = folder;
     if (level == 10) folder_L10 = folder;
   }
-  for (int i = 0; i < 3; ++i) {
+  for (size_t i = 0; i < 3; ++i) {
     std::string title = IndexedURLTitle(i + 10);
     GURL url = GURL(IndexedURL(i + 10));
     ASSERT_NE(nullptr, AddURL(0, folder_L0, 0, title, url));
@@ -1078,7 +1074,8 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 
   GURL url10 = GURL(IndexedURL(10));
-  Move(0, GetUniqueNodeByURL(0, url10), folder_L10, folder_L10->child_count());
+  Move(0, GetUniqueNodeByURL(0, url10), folder_L10,
+       folder_L10->children().size());
   GURL url11 = GURL(IndexedURL(11));
   Move(0, GetUniqueNodeByURL(0, url11), folder_L10, 0);
   GURL url12 = GURL(IndexedURL(12));
@@ -1093,25 +1090,24 @@
 
   const BookmarkNode* folder = GetBookmarkBarNode(0);
   const BookmarkNode* folder_L5 = nullptr;
-  for (int level = 0; level < 15; ++level) {
-    int num_bookmarks = base::RandInt(0, 9);
-    for (int i = 0; i < num_bookmarks; ++i) {
+  for (size_t level = 0; level < 15; ++level) {
+    size_t num_bookmarks = base::RandInt(0, 9);
+    for (size_t i = 0; i < num_bookmarks; ++i) {
       std::string title = IndexedURLTitle(i);
       GURL url = GURL(IndexedURL(i));
       ASSERT_NE(nullptr, AddURL(0, folder, i, title, url));
     }
     std::string title = IndexedFolderName(level);
-    folder = AddFolder(
-        0, folder, folder->child_count(), title);
+    folder = AddFolder(0, folder, folder->children().size(), title);
     ASSERT_NE(nullptr, folder);
     if (level == 5) folder_L5 = folder;
   }
-  folder = AddFolder(
-      0, GetBookmarkBarNode(0)->child_count(), kGenericFolderName);
+  folder = AddFolder(0, GetBookmarkBarNode(0)->children().size(),
+                     kGenericFolderName);
   ASSERT_NE(nullptr, folder);
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 
-  Move(0, folder, folder_L5, folder_L5->child_count());
+  Move(0, folder, folder_L5, folder_L5->children().size());
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
@@ -1122,29 +1118,29 @@
 
   const BookmarkNode* folder = GetBookmarkBarNode(0);
   const BookmarkNode* folder_L5 = nullptr;
-  for (int level = 0; level < 6; ++level) {
-    int num_bookmarks = base::RandInt(0, 9);
-    for (int i = 0; i < num_bookmarks; ++i) {
+  for (size_t level = 0; level < 6; ++level) {
+    size_t num_bookmarks = base::RandInt(0, 9);
+    for (size_t i = 0; i < num_bookmarks; ++i) {
       std::string title = IndexedURLTitle(i);
       GURL url = GURL(IndexedURL(i));
       ASSERT_NE(nullptr, AddURL(0, folder, i, title, url));
     }
     std::string title = IndexedFolderName(level);
-    folder = AddFolder(0, folder, folder->child_count(), title);
+    folder = AddFolder(0, folder, folder->children().size(), title);
     ASSERT_NE(nullptr, folder);
     if (level == 5) folder_L5 = folder;
   }
-  folder = AddFolder(
-      0, GetBookmarkBarNode(0)->child_count(), kGenericFolderName);
+  folder = AddFolder(0, GetBookmarkBarNode(0)->children().size(),
+                     kGenericFolderName);
   ASSERT_NE(nullptr, folder);
-  for (int i = 0; i < 10; ++i) {
+  for (size_t i = 0; i < 10; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, folder, i, title, url));
   }
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 
-  Move(0, folder, folder_L5, folder_L5->child_count());
+  Move(0, folder, folder_L5, folder_L5->children().size());
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
@@ -1155,29 +1151,30 @@
 
   const BookmarkNode* folder = GetBookmarkBarNode(0);
   const BookmarkNode* folder_L5 = nullptr;
-  for (int level = 0; level < 6; ++level) {
-    int num_bookmarks = base::RandInt(0, 9);
-    for (int i = 0; i < num_bookmarks; ++i) {
+  for (size_t level = 0; level < 6; ++level) {
+    size_t num_bookmarks = base::RandInt(0, 9);
+    for (size_t i = 0; i < num_bookmarks; ++i) {
       std::string title = IndexedURLTitle(i);
       GURL url = GURL(IndexedURL(i));
       ASSERT_NE(nullptr, AddURL(0, folder, i, title, url));
     }
     std::string title = IndexedFolderName(level);
-    folder = AddFolder(0, folder, folder->child_count(), title);
+    folder = AddFolder(0, folder, folder->children().size(), title);
     ASSERT_NE(nullptr, folder);
     if (level == 5) folder_L5 = folder;
   }
-  folder = AddFolder(
-      0, folder_L5, folder_L5->child_count(), kGenericFolderName);
+  folder =
+      AddFolder(0, folder_L5, folder_L5->children().size(), kGenericFolderName);
   ASSERT_NE(nullptr, folder);
-  for (int i = 0; i < 10; ++i) {
+  for (size_t i = 0; i < 10; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, folder, i, title, url));
   }
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 
-  Move(0, folder, GetBookmarkBarNode(0), GetBookmarkBarNode(0)->child_count());
+  Move(0, folder, GetBookmarkBarNode(0),
+       GetBookmarkBarNode(0)->children().size());
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
@@ -1186,11 +1183,11 @@
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
-  for (int i = 0; i < 2; ++i) {
+  for (size_t i = 0; i < 2; ++i) {
     std::string title = IndexedFolderName(i);
     const BookmarkNode* folder = AddFolder(0, i, title);
     ASSERT_NE(nullptr, folder);
-    for (int j = 0; j < 10; ++j) {
+    for (size_t j = 0; j < 10; ++j) {
       std::string title = IndexedURLTitle(j);
       GURL url = GURL(IndexedURL(j));
       ASSERT_NE(nullptr, AddURL(0, folder, j, title, url));
@@ -1207,11 +1204,11 @@
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
-  for (int i = 0; i < 10; ++i) {
+  for (size_t i = 0; i < 10; ++i) {
     std::string title = IndexedFolderName(i);
     const BookmarkNode* folder = AddFolder(0, i, title);
     ASSERT_NE(nullptr, folder);
-    for (int j = 0; j < 10; ++j) {
+    for (size_t j = 0; j < 10; ++j) {
       std::string title = IndexedURLTitle(1000 * i + j);
       GURL url = GURL(IndexedURL(j));
       ASSERT_NE(nullptr, AddURL(0, folder, j, title, url));
@@ -1229,12 +1226,12 @@
   ASSERT_TRUE(AllModelsMatchVerifier());
 
   DisableVerifier();
-  for (int i = 0; i < 2; ++i) {
-    std::string title0 = IndexedURLTitle(2*i);
-    GURL url0 = GURL(IndexedURL(2*i));
+  for (size_t i = 0; i < 2; ++i) {
+    std::string title0 = IndexedURLTitle(2 * i);
+    GURL url0 = GURL(IndexedURL(2 * i));
     ASSERT_NE(nullptr, AddURL(0, title0, url0));
-    std::string title1 = IndexedURLTitle(2*i+1);
-    GURL url1 = GURL(IndexedURL(2*i+1));
+    std::string title1 = IndexedURLTitle(2 * i + 1);
+    GURL url1 = GURL(IndexedURL(2 * i + 1));
     ASSERT_NE(nullptr, AddURL(1, title1, url1));
   }
   ASSERT_TRUE(BookmarksMatchChecker().Wait());
@@ -1249,7 +1246,7 @@
   // Note: When a racy commit is done with identical bookmarks, it is possible
   // for duplicates to exist after sync completes. See http://crbug.com/19769.
   DisableVerifier();
-  for (int i = 0; i < 2; ++i) {
+  for (size_t i = 0; i < 2; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, title, url));
@@ -1293,19 +1290,19 @@
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
 
-  for (int i = 0; i < 3; ++i) {
+  for (size_t i = 0; i < 3; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, i, title, url));
     ASSERT_NE(nullptr, AddURL(1, i, title, url));
   }
 
-  for (int i = 3; i < 10; ++i) {
+  for (size_t i = 3; i < 10; ++i) {
     std::string title0 = IndexedURLTitle(i);
     GURL url0 = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, i, title0, url0));
-    std::string title1 = IndexedURLTitle(i+7);
-    GURL url1 = GURL(IndexedURL(i+7));
+    std::string title1 = IndexedURLTitle(i + 7);
+    GURL url1 = GURL(IndexedURL(i + 7));
     ASSERT_NE(nullptr, AddURL(1, i, title1, url1));
   }
 
@@ -1320,14 +1317,14 @@
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
 
-  for (int i = 0; i < 3; ++i) {
+  for (size_t i = 0; i < 3; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, i, title, url));
     ASSERT_NE(nullptr, AddURL(1, i, title, url));
   }
 
-  for (int i = 3; i < 10; ++i) {
+  for (size_t i = 3; i < 10; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(1, i, title, url));
@@ -1344,7 +1341,7 @@
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
 
-  for (int i = 0; i < 3; ++i) {
+  for (size_t i = 0; i < 3; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, i, title, url));
@@ -1368,12 +1365,12 @@
   ASSERT_NE(nullptr, folder0);
   const BookmarkNode* folder1 = AddFolder(1, kGenericFolderName);
   ASSERT_NE(nullptr, folder1);
-  for (int i = 0; i < 2; ++i) {
-    std::string title0 = IndexedURLTitle(2*i);
-    GURL url0 = GURL(IndexedURL(2*i));
+  for (size_t i = 0; i < 2; ++i) {
+    std::string title0 = IndexedURLTitle(2 * i);
+    GURL url0 = GURL(IndexedURL(2 * i));
     ASSERT_NE(nullptr, AddURL(0, folder0, i, title0, url0));
-    std::string title1 = IndexedURLTitle(2*i+1);
-    GURL url1 = GURL(IndexedURL(2*i+1));
+    std::string title1 = IndexedURLTitle(2 * i + 1);
+    GURL url1 = GURL(IndexedURL(2 * i + 1));
     ASSERT_NE(nullptr, AddURL(1, folder1, i, title1, url1));
   }
   // Commit sequentially to make sure there is no race condition.
@@ -1388,31 +1385,31 @@
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
 
-  for (int i = 0; i < 25; ++i) {
+  for (size_t i = 0; i < 25; ++i) {
     std::string title0 = IndexedURLTitle(i);
     GURL url0 = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, i, title0, url0));
-    std::string title1 = IndexedURLTitle(i+50);
-    GURL url1 = GURL(IndexedURL(i+50));
+    std::string title1 = IndexedURLTitle(i + 50);
+    GURL url1 = GURL(IndexedURL(i + 50));
     ASSERT_NE(nullptr, AddURL(1, i, title1, url1));
   }
-  for (int i = 25; i < 30; ++i) {
+  for (size_t i = 25; i < 30; ++i) {
     std::string title0 = IndexedFolderName(i);
     const BookmarkNode* folder0 = AddFolder(0, i, title0);
     ASSERT_NE(nullptr, folder0);
-    std::string title1 = IndexedFolderName(i+50);
+    std::string title1 = IndexedFolderName(i + 50);
     const BookmarkNode* folder1 = AddFolder(1, i, title1);
     ASSERT_NE(nullptr, folder1);
-    for (int j = 0; j < 5; ++j) {
-      std::string title0 = IndexedURLTitle(i+5*j);
-      GURL url0 = GURL(IndexedURL(i+5*j));
+    for (size_t j = 0; j < 5; ++j) {
+      std::string title0 = IndexedURLTitle(i + 5 * j);
+      GURL url0 = GURL(IndexedURL(i + 5 * j));
       ASSERT_NE(nullptr, AddURL(0, folder0, j, title0, url0));
-      std::string title1 = IndexedURLTitle(i+5*j+50);
-      GURL url1 = GURL(IndexedURL(i+5*j+50));
+      std::string title1 = IndexedURLTitle(i + 5 * j + 50);
+      GURL url1 = GURL(IndexedURL(i + 5 * j + 50));
       ASSERT_NE(nullptr, AddURL(1, folder1, j, title1, url1));
     }
   }
-  for (int i = 100; i < 125; ++i) {
+  for (size_t i = 100; i < 125; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, title, url));
@@ -1434,7 +1431,7 @@
   for (int i = 0; i < 2; ++i) {
     const BookmarkNode* folder = AddFolder(i, kGenericFolderName);
     ASSERT_NE(nullptr, folder);
-    for (int j = 0; j < 4; ++j) {
+    for (size_t j = 0; j < 4; ++j) {
       if (base::RandDouble() < 0.5) {
         std::string title = IndexedURLTitle(j);
         GURL url = GURL(IndexedURL(j));
@@ -1457,13 +1454,13 @@
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
 
-  for (int i = 0; i < 4; ++i) {
+  for (size_t i = 0; i < 4; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, i, title, url));
   }
 
-  for (int j = 0; j < 2; ++j) {
+  for (size_t j = 0; j < 2; ++j) {
     std::string title = IndexedURLTitle(j);
     GURL url = GURL(IndexedURL(j));
     ASSERT_NE(nullptr, AddURL(1, j, title, url));
@@ -1545,15 +1542,15 @@
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
 
-  for (int i = 0; i < 3; ++i) {
-    std::string title = IndexedURLTitle(i+1);
-    GURL url = GURL(IndexedURL(i+1));
+  for (size_t i = 0; i < 3; ++i) {
+    std::string title = IndexedURLTitle(i + 1);
+    GURL url = GURL(IndexedURL(i + 1));
     ASSERT_NE(nullptr, AddURL(0, i, title, url));
   }
 
-  for (int j = 0; j < 3; ++j) {
-    std::string title = IndexedURLTitle(j+4);
-    GURL url = GURL(IndexedURL(j+4));
+  for (size_t j = 0; j < 3; ++j) {
+    std::string title = IndexedURLTitle(j + 4);
+    GURL url = GURL(IndexedURL(j + 4));
     ASSERT_NE(nullptr, AddURL(0, j, title, url));
   }
 
@@ -1570,8 +1567,8 @@
 
   // Let's add duplicate set of bookmark {1,2,2,3,3,3,4,4,4,4} to client0.
   int node_index = 0;
-  for (int i = 1; i < 5 ; ++i) {
-    for (int j = 0; j < i; ++j) {
+  for (size_t i = 1; i < 5; ++i) {
+    for (size_t j = 0; j < i; ++j) {
       std::string title = IndexedURLTitle(i);
       GURL url = GURL(IndexedURL(i));
       ASSERT_NE(nullptr, AddURL(0, node_index, title, url));
@@ -1579,9 +1576,9 @@
     }
   }
   // Let's add a set of bookmarks {1,2,3,4} to client1.
-  for (int i = 0; i < 4; ++i) {
-    std::string title = IndexedURLTitle(i+1);
-    GURL url = GURL(IndexedURL(i+1));
+  for (size_t i = 0; i < 4; ++i) {
+    std::string title = IndexedURLTitle(i + 1);
+    GURL url = GURL(IndexedURL(i + 1));
     ASSERT_NE(nullptr, AddURL(1, i, title, url));
   }
 
@@ -1589,7 +1586,7 @@
   SetupSyncOneClientAfterAnother();
   ASSERT_TRUE(BookmarksMatchChecker().Wait());
 
-  for (int i = 1; i < 5 ; ++i) {
+  for (size_t i = 1; i < 5; ++i) {
     ASSERT_EQ(i, CountBookmarksWithTitlesMatching(1, IndexedURLTitle(i)));
   }
 }
@@ -1637,12 +1634,12 @@
   ASSERT_NE(nullptr, folder0);
   const BookmarkNode* folder1 = AddFolder(1, kGenericFolderName);
   ASSERT_NE(nullptr, folder1);
-  for (int i = 0; i < 5; ++i) {
+  for (size_t i = 0; i < 5; ++i) {
     std::string title0 = IndexedURLTitle(i);
     GURL url0 = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, folder0, i, title0, url0));
-    std::string title1 = IndexedURLTitle(i+5);
-    GURL url1 = GURL(IndexedURL(i+5));
+    std::string title1 = IndexedURLTitle(i + 5);
+    GURL url1 = GURL(IndexedURL(i + 5));
     ASSERT_NE(nullptr, AddURL(1, folder1, i, title1, url1));
   }
 
@@ -1694,7 +1691,7 @@
 
   const BookmarkNode* folder0 = AddFolder(0, kGenericFolderName);
   ASSERT_NE(nullptr, folder0);
-  for (int i = 0; i < 3; ++i) {
+  for (size_t i = 0; i < 3; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, folder0, i, title, url));
@@ -1719,7 +1716,7 @@
 
   const BookmarkNode* folder0 = AddFolder(0, kGenericFolderName);
   ASSERT_NE(nullptr, folder0);
-  for (int i = 0; i < 3; ++i) {
+  for (size_t i = 0; i < 3; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, folder0, i, title, url));
@@ -1760,7 +1757,7 @@
   ASSERT_NE(nullptr, folderB[0]);
   folderB[1] = AddFolder(1, IndexedFolderName(1));
   ASSERT_NE(nullptr, folderB[1]);
-  for (int i = 0; i < 3; ++i) {
+  for (size_t i = 0; i < 3; ++i) {
     std::string title = IndexedURLTitle(i);
     GURL url = GURL(IndexedURL(i));
     ASSERT_NE(nullptr, AddURL(0, folderB[0], i, title, url));
@@ -1771,11 +1768,11 @@
   ASSERT_NE(nullptr, folderC[0]);
   folderC[1] = AddFolder(1, IndexedFolderName(2));
   ASSERT_NE(nullptr, folderC[1]);
-  for (int i = 0; i < 3; ++i) {
+  for (size_t i = 0; i < 3; ++i) {
     std::string folder_name = IndexedSubfolderName(i);
     const BookmarkNode* subfolder = AddFolder(0, folderC[0], i, folder_name);
     ASSERT_NE(nullptr, subfolder);
-    for (int j = 0; j < 3; ++j) {
+    for (size_t j = 0; j < 3; ++j) {
       std::string title = IndexedURLTitle(j);
       GURL url = GURL(IndexedURL(j));
       ASSERT_NE(nullptr, AddURL(0, subfolder, j, title, url));
@@ -1941,7 +1938,7 @@
   // Make changes on client 0.
   for (size_t i = 0; i < num_bookmarks; ++i) {
     const BookmarkNode* node = GetUniqueNodeByURL(0, GURL(IndexedURL(i)));
-    int rand_pos = base::RandInt(0, num_bookmarks-1);
+    size_t rand_pos = size_t{base::RandInt(0, int{num_bookmarks} - 1)};
     DVLOG(1) << "Moving client 0's bookmark " << i << " to position "
              << rand_pos;
     Move(0, node, node->parent(), rand_pos);
@@ -1950,7 +1947,7 @@
   // Make changes on client 1.
   for (size_t i = 0; i < num_bookmarks; ++i) {
     const BookmarkNode* node = GetUniqueNodeByURL(1, GURL(IndexedURL(i)));
-    int rand_pos = base::RandInt(0, num_bookmarks-1);
+    size_t rand_pos = size_t{base::RandInt(0, int{num_bookmarks} - 1)};
     DVLOG(1) << "Moving client 1's bookmark " << i << " to position "
              << rand_pos;
     Move(1, node, node->parent(), rand_pos);
@@ -1961,7 +1958,7 @@
   // Now make changes to client 1 first.
   for (size_t i = 0; i < num_bookmarks; ++i) {
     const BookmarkNode* node = GetUniqueNodeByURL(1, GURL(IndexedURL(i)));
-    int rand_pos = base::RandInt(0, num_bookmarks-1);
+    size_t rand_pos = size_t{base::RandInt(0, int{num_bookmarks} - 1)};
     DVLOG(1) << "Moving client 1's bookmark " << i << " to position "
              << rand_pos;
     Move(1, node, node->parent(), rand_pos);
@@ -1970,7 +1967,7 @@
   // Make changes on client 0.
   for (size_t i = 0; i < num_bookmarks; ++i) {
     const BookmarkNode* node = GetUniqueNodeByURL(0, GURL(IndexedURL(i)));
-    int rand_pos = base::RandInt(0, num_bookmarks-1);
+    size_t rand_pos = size_t{base::RandInt(0, int{num_bookmarks} - 1)};
     DVLOG(1) << "Moving client 0's bookmark " << i << " to position "
              << rand_pos;
     Move(0, node, node->parent(), rand_pos);
@@ -2172,7 +2169,7 @@
   // For clean profiles, the bookmarks count should be zero. We are not
   // enforcing this, we only check that the final count is equal to initial
   // count plus new bookmarks count.
-  int init_bookmarks_count = CountAllBookmarks(0);
+  size_t init_bookmarks_count = CountAllBookmarks(0);
 
   // Add one new bookmark to the first profile.
   ASSERT_NE(nullptr,
@@ -2205,10 +2202,10 @@
   ASSERT_TRUE(BookmarksMatchChecker().Wait());
   // Check that both profiles have the folder and the bookmark created above.
   for (int i = 0; i < num_clients(); ++i) {
-    ASSERT_EQ(1, CountFoldersWithTitlesMatching(i, "Folder 0"))
+    ASSERT_EQ(1u, CountFoldersWithTitlesMatching(i, "Folder 0"))
         << "Failed to match the folder";
     ASSERT_EQ(
-        1, CountBookmarksWithUrlsMatching(i, GURL("http://www.google.com/0")))
+        1u, CountBookmarksWithUrlsMatching(i, GURL("http://www.google.com/0")))
         << "Failed to match the bookmark";
   }
 }
@@ -2222,7 +2219,7 @@
   // For clean profiles, the bookmarks count should be zero. We are not
   // enforcing this, we only check that the final count is equal to initial
   // count plus new bookmarks count.
-  int init_bookmarks_count = CountAllBookmarks(0);
+  size_t init_bookmarks_count = CountAllBookmarks(0);
 
   // Add one new bookmark per profile.
   for (int i = 0; i < num_clients(); ++i) {
@@ -2248,7 +2245,7 @@
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(BookmarksMatchChecker().Wait())
       << "Initial bookmark models did not match for all profiles";
-  const int initial_count = CountAllBookmarks(0);
+  const size_t initial_count = CountAllBookmarks(0);
 
   // Verify that we can sync. Add a bookmark on the first client and verify it's
   // synced to the second client.
diff --git a/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc
index eb9f3417..c79a2b4d 100644
--- a/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc
@@ -467,7 +467,7 @@
 
 // Flaky. http://crbug.com/917498
 IN_PROC_BROWSER_TEST_P(TwoClientWalletSyncTest,
-                       DISABLED_ServerAddressConvertsToSameLocalAddress) {
+                       ServerAddressConvertsToSameLocalAddress) {
   GetFakeServer()->SetWalletData(
       {CreateSyncWalletAddress(/*name=*/"address-1", /*company=*/"Company-1"),
        CreateDefaultSyncPaymentsCustomerData()});
diff --git a/chrome/browser/task_manager/sampling/task_group_sampler.cc b/chrome/browser/task_manager/sampling/task_group_sampler.cc
index c643c48..8430af1 100644
--- a/chrome/browser/task_manager/sampling/task_group_sampler.cc
+++ b/chrome/browser/task_manager/sampling/task_group_sampler.cc
@@ -137,9 +137,7 @@
   DCHECK(worker_pool_sequenced_checker_.CalledOnValidSequence());
 
 #if defined(OS_CHROMEOS)
-  base::ProcessMetrics::TotalsSummary summary =
-      process_metrics_->GetTotalsSummary();
-  return summary.swap_kb * 1024;
+  return process_metrics_->GetVmSwapBytes();
 #endif  // defined(OS_CHROMEOS)
 
   return 0;
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index a9ee1f3..6c7069a 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1466,8 +1466,8 @@
       "ash/launcher/settings_window_observer.h",
       "ash/login_screen_client.cc",
       "ash/login_screen_client.h",
-      "ash/media_client.cc",
-      "ash/media_client.h",
+      "ash/media_client_impl.cc",
+      "ash/media_client_impl.h",
       "ash/multi_user/multi_profile_support.cc",
       "ash/multi_user/multi_profile_support.h",
       "ash/multi_user/multi_user_context_menu.h",
@@ -3184,7 +3184,6 @@
         "views/sync/dice_signin_button_view.h",
         "views/sync/one_click_signin_dialog_view.cc",
         "views/sync/one_click_signin_dialog_view.h",
-        "views/tabs/window_finder.cc",
       ]
       deps += [ "//ui/views/window/vector_icons" ]
     }
@@ -3900,8 +3899,6 @@
 
   if (is_chromeos) {
     sources += [
-      "ash/ash_test_util.cc",
-      "ash/ash_test_util.h",
       "ash/fake_tablet_mode_controller.cc",
       "ash/fake_tablet_mode_controller.h",
       "ash/test_login_screen.cc",
diff --git a/chrome/browser/ui/app_list/extension_app_utils.cc b/chrome/browser/ui/app_list/extension_app_utils.cc
index 9affea2..002ddce 100644
--- a/chrome/browser/ui/app_list/extension_app_utils.cc
+++ b/chrome/browser/ui/app_list/extension_app_utils.cc
@@ -16,6 +16,11 @@
 #include "ui/views/controls/menu/menu_config.h"
 #include "ui/views/vector_icons.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/web_applications/system_web_app_ui_utils_chromeos.h"
+#endif
+
 namespace app_list {
 
 namespace {
@@ -27,6 +32,26 @@
 
 bool ShouldShowInLauncher(const extensions::Extension* extension,
                           content::BrowserContext* context) {
+  // TODO(crbug.com/971029): Make this a per System Web App property that is
+  // accessible by querying the SystemWebAppManager.
+#if defined(OS_CHROMEOS)
+  Profile* profile = Profile::FromBrowserContext(context);
+  // These System Web Apps should not show in the launcher as they are added
+  // as internal apps.
+  web_app::SystemAppType hidden_system_web_apps[] = {
+      // TODO(crbug.com/836128): Remove this once OS Settings is launched, and
+      // permanently migrate OS Settings from an internal app to a full System
+      // Web App.
+      web_app::SystemAppType::SETTINGS,
+  };
+  for (auto app_type : hidden_system_web_apps) {
+    if (extension->id() ==
+        web_app::GetAppIdForSystemWebApp(profile, app_type)) {
+      return false;
+    }
+  }
+#endif
+
   return !HideInLauncherById(extension->id()) &&
          chromeos::DemoSession::ShouldDisplayInAppLauncher(extension->id()) &&
          extensions::ui_util::ShouldDisplayInAppLauncher(extension, context);
diff --git a/chrome/browser/ui/app_list/search/chrome_search_result.cc b/chrome/browser/ui/app_list/search/chrome_search_result.cc
index 8ed4ecaa2..1c83a129 100644
--- a/chrome/browser/ui/app_list/search/chrome_search_result.cc
+++ b/chrome/browser/ui/app_list/search/chrome_search_result.cc
@@ -217,10 +217,6 @@
   return result;
 }
 
-int ChromeSearchResult::GetSubType() const {
-  return -1;
-}
-
 app_list::AppContextMenu* ChromeSearchResult::GetAppContextMenu() {
   return nullptr;
 }
diff --git a/chrome/browser/ui/app_list/search/chrome_search_result.h b/chrome/browser/ui/app_list/search/chrome_search_result.h
index ca081d1..96930a7 100644
--- a/chrome/browser/ui/app_list/search/chrome_search_result.h
+++ b/chrome/browser/ui/app_list/search/chrome_search_result.h
@@ -127,10 +127,11 @@
   static std::string TagsDebugStringForTest(const std::string& text,
                                             const Tags& tags);
 
-  // Subtype of a search result. -1 means no sub type. Derived class
-  // can use this to return useful values for rankers etc. Currently,
-  // OmniboxResult overrides it to return AutocompleteMatch::Type.
-  virtual int GetSubType() const;
+  // Subtype of a search result. -1 means no sub type. Derived classes
+  // can set this in their metadata to return useful values for rankers etc.
+  // Note set_result_subtype() does not call into ModelUpdater so changing the
+  // subtype after construction is not reflected in ash.
+  int result_subtype() const { return metadata_->result_subtype; }
 
   // Get the type of the result, used in metrics.
   virtual app_list::SearchResultType GetSearchResultType() const = 0;
@@ -138,6 +139,9 @@
  protected:
   // These id setters should be called in derived class constructors only.
   void set_id(const std::string& id) { metadata_->id = id; }
+  void set_result_subtype(int result_subtype) {
+    metadata_->result_subtype = result_subtype;
+  }
 
   // Get the context menu of a certain search result. This could be different
   // for different kinds of items.
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.cc b/chrome/browser/ui/app_list/search/omnibox_result.cc
index 50cb858..018dcaa5 100644
--- a/chrome/browser/ui/app_list/search/omnibox_result.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_result.cc
@@ -141,6 +141,7 @@
   }
   set_id(match_.stripped_destination_url.spec());
   SetResultType(ash::SearchResultType::kOmnibox);
+  set_result_subtype(static_cast<int>(match_.type));
 
   // Derive relevance from omnibox relevance and normalize it to [0, 1].
   // The magic number 1500 is the highest score of an omnibox result.
@@ -180,10 +181,6 @@
   }
 }
 
-int OmniboxResult::GetSubType() const {
-  return static_cast<int>(match_.type);
-}
-
 SearchResultType OmniboxResult::GetSearchResultType() const {
   switch (match_.type) {
     case AutocompleteMatchType::URL_WHAT_YOU_TYPED:
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.h b/chrome/browser/ui/app_list/search/omnibox_result.h
index 392e6db..33e821c 100644
--- a/chrome/browser/ui/app_list/search/omnibox_result.h
+++ b/chrome/browser/ui/app_list/search/omnibox_result.h
@@ -39,7 +39,6 @@
   // ChromeSearchResult overrides:
   void Open(int event_flags) override;
   void InvokeAction(int action_index, int event_flags) override;
-  int GetSubType() const override;
   SearchResultType GetSearchResultType() const override;
 
  private:
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.cc b/chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.cc
index 4acebe0..8cb964e 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.cc
@@ -14,16 +14,16 @@
 
 namespace app_list {
 
-// Given a |ChromeSearchResult| representing an omnibox result, convert it based
-// on the subtype specified by |ChromeSearchResult::GetSubType|. Any
-// unanticipated subtypes are converted into |RankingItemType::kOmniboxGeneric|.
+// Given a ChromeSearchResult representing an omnibox result, convert it based
+// on the subtype specified by ChromeSearchResult::result_subtype. Any
+// unanticipated subtypes are converted into RankingItemType::kOmniboxGeneric.
 RankingItemType ExpandOmniboxType(const ChromeSearchResult& result) {
   if (result.result_type() != ash::SearchResultType::kOmnibox) {
     NOTREACHED();
     return RankingItemType::kUnknown;
   }
 
-  switch (static_cast<AutocompleteMatchType::Type>(result.GetSubType())) {
+  switch (static_cast<AutocompleteMatchType::Type>(result.result_subtype())) {
     case AutocompleteMatchType::Type::HISTORY_URL:
     case AutocompleteMatchType::Type::HISTORY_TITLE:
     case AutocompleteMatchType::Type::HISTORY_BODY:
diff --git a/chrome/browser/ui/apps/chrome_app_delegate.cc b/chrome/browser/ui/apps/chrome_app_delegate.cc
index f33312d..6eaaed8 100644
--- a/chrome/browser/ui/apps/chrome_app_delegate.cc
+++ b/chrome/browser/ui/apps/chrome_app_delegate.cc
@@ -304,7 +304,7 @@
 bool ChromeAppDelegate::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const extensions::Extension* extension) {
   return MediaCaptureDevicesDispatcher::GetInstance()
       ->CheckMediaAccessPermission(render_frame_host, security_origin, type,
diff --git a/chrome/browser/ui/apps/chrome_app_delegate.h b/chrome/browser/ui/apps/chrome_app_delegate.h
index 729b0d39..5109b38 100644
--- a/chrome/browser/ui/apps/chrome_app_delegate.h
+++ b/chrome/browser/ui/apps/chrome_app_delegate.h
@@ -65,7 +65,7 @@
   bool CheckMediaAccessPermission(
       content::RenderFrameHost* render_frame_host,
       const GURL& security_origin,
-      blink::MediaStreamType type,
+      blink::mojom::MediaStreamType type,
       const extensions::Extension* extension) override;
   int PreferredIconSize() const override;
   void SetWebContentsBlocked(content::WebContents* web_contents,
diff --git a/chrome/browser/ui/ash/ash_test_util.cc b/chrome/browser/ui/ash/ash_test_util.cc
deleted file mode 100644
index 2be6d2b..0000000
--- a/chrome/browser/ui/ash/ash_test_util.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/ash/ash_test_util.h"
-
-#include "ash/public/cpp/window_properties.h"
-#include "ash/public/cpp/window_state_type.h"
-#include "base/run_loop.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/aura/window.h"
-#include "ui/aura/window_observer.h"
-#include "ui/base/test/ui_controls.h"
-#include "ui/wm/core/window_util.h"
-
-namespace test {
-namespace {
-// Wait until the window's state changes to given the snapped state.
-// The window should stay alive, so no need to observer destroying.
-class SnapWaiter : public aura::WindowObserver {
- public:
-  SnapWaiter(aura::Window* window, ash::WindowStateType type)
-      : window_(window), type_(type) {
-    window->AddObserver(this);
-  }
-  ~SnapWaiter() override { window_->RemoveObserver(this); }
-
-  // aura::WindowObserver:
-  void OnWindowPropertyChanged(aura::Window* window,
-                               const void* key,
-                               intptr_t old) override {
-    if (key == ash::kWindowStateTypeKey && IsSnapped())
-      run_loop_.Quit();
-  }
-
-  void Wait() { run_loop_.Run(); }
-
-  bool IsSnapped() const {
-    return window_->GetProperty(ash::kWindowStateTypeKey) == type_;
-  }
-
- private:
-  aura::Window* window_;
-  ash::WindowStateType type_;
-  base::RunLoop run_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(SnapWaiter);
-};
-
-}  // namespace
-
-void ActivateAndSnapWindow(aura::Window* window, ash::WindowStateType type) {
-  DCHECK(window);
-  if (!wm::IsActiveWindow(window))
-    wm::ActivateWindow(window);
-
-  ASSERT_TRUE(wm::IsActiveWindow(window));
-
-  SnapWaiter snap_waiter(window, type);
-  ASSERT_TRUE(type == ash::WindowStateType::kRightSnapped ||
-              type == ash::WindowStateType::kLeftSnapped);
-
-  // Early return if it's already snapped.
-  if (snap_waiter.IsSnapped())
-    return;
-
-  ui_controls::SendKeyPress(window,
-                            type == ash::WindowStateType::kLeftSnapped
-                                ? ui::VKEY_OEM_4
-                                : ui::VKEY_OEM_6,
-                            /*control=*/false,
-                            /*shift=*/false,
-                            /*alt=*/true,
-                            /*command=*/false);
-  snap_waiter.Wait();
-}
-
-}  // namespace test
diff --git a/chrome/browser/ui/ash/ash_test_util.h b/chrome/browser/ui/ash/ash_test_util.h
deleted file mode 100644
index 7fd082b6..0000000
--- a/chrome/browser/ui/ash/ash_test_util.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_ASH_ASH_TEST_UTIL_H_
-#define CHROME_BROWSER_UI_ASH_ASH_TEST_UTIL_H_
-
-namespace aura {
-class Window;
-}
-
-namespace ash {
-enum class WindowStateType;
-}
-
-namespace test {
-
-// The snap window. This will activate the |window|.
-void ActivateAndSnapWindow(aura::Window* window, ash::WindowStateType type);
-
-}  // namespace test
-
-#endif  // CHROME_BROWSER_UI_ASH_ASH_TEST_UTIL_H_
diff --git a/chrome/browser/ui/ash/assistant/assistant_pref_util.cc b/chrome/browser/ui/ash/assistant/assistant_pref_util.cc
index 127a63a6c..38a64a2 100644
--- a/chrome/browser/ui/ash/assistant/assistant_pref_util.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_pref_util.cc
@@ -12,13 +12,19 @@
 namespace assistant {
 namespace prefs {
 
+// A preference that indicates the user has accepted activity control.
 const char kAssistantConsentStatus[] =
     "settings.voice_interaction.activity_control.consent_status";
+// A preference that indicates the Assistant has been disabled by domain policy.
+// If true, the Assistant will always been disabled and user cannot enable it.
+const char kAssistantDisabledByPolicy[] =
+    "settings.assistant.disabled_by_policy";
 
 void RegisterProfilePrefs(PrefRegistrySimple* registry) {
   registry->RegisterIntegerPref(
       kAssistantConsentStatus,
       static_cast<int>(ash::mojom::ConsentStatus::kUnknown));
+  registry->RegisterBooleanPref(kAssistantDisabledByPolicy, false);
 }
 
 ash::mojom::ConsentStatus GetConsentStatus(PrefService* pref_service) {
diff --git a/chrome/browser/ui/ash/assistant/assistant_pref_util.h b/chrome/browser/ui/ash/assistant/assistant_pref_util.h
index 77ef30d..a95c67a 100644
--- a/chrome/browser/ui/ash/assistant/assistant_pref_util.h
+++ b/chrome/browser/ui/ash/assistant/assistant_pref_util.h
@@ -14,6 +14,7 @@
 namespace prefs {
 
 extern const char kAssistantConsentStatus[];
+extern const char kAssistantDisabledByPolicy[];
 
 // Registers Assistant specific profile preferences.
 void RegisterProfilePrefs(PrefRegistrySimple* registry);
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index 84f67fd8..d85fcac 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -32,7 +32,7 @@
 #include "chrome/browser/ui/ash/kiosk_next_shell_client.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/ash/login_screen_client.h"
-#include "chrome/browser/ui/ash/media_client.h"
+#include "chrome/browser/ui/ash/media_client_impl.h"
 #include "chrome/browser/ui/ash/network/mobile_data_notifications.h"
 #include "chrome/browser/ui/ash/network/network_connect_delegate_chromeos.h"
 #include "chrome/browser/ui/ash/network/network_portal_notification_controller.h"
@@ -168,10 +168,11 @@
 
 void ChromeBrowserMainExtraPartsAsh::PostProfileInit() {
   login_screen_client_ = std::make_unique<LoginScreenClient>();
-  // https://crbug.com/884127 ensuring that LoginScreenClient is initialized before using it InitializeDeviceDisablingManager.
+  // https://crbug.com/884127 ensuring that LoginScreenClient is initialized
+  // before using it InitializeDeviceDisablingManager.
   g_browser_process->platform_part()->InitializeDeviceDisablingManager();
 
-  media_client_ = std::make_unique<MediaClient>();
+  media_client_ = std::make_unique<MediaClientImpl>();
   media_client_->Init();
 
   // Instantiate DisplaySettingsHandler after CrosSettings has been
@@ -203,8 +204,10 @@
     TabScrubber::GetInstance();
   }
 
-  if (base::FeatureList::IsEnabled(ash::features::kKioskNextShell))
+  if (base::FeatureList::IsEnabled(ash::features::kKioskNextShell)) {
     kiosk_next_shell_client_ = std::make_unique<KioskNextShellClient>();
+    kiosk_next_shell_client_->Init();
+  }
 }
 
 void ChromeBrowserMainExtraPartsAsh::PostBrowserStart() {
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
index 790c321..5dd38371 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
@@ -27,7 +27,7 @@
 class ImeControllerClient;
 class KioskNextShellClient;
 class LoginScreenClient;
-class MediaClient;
+class MediaClientImpl;
 class MobileDataNotifications;
 class NetworkConnectDelegateChromeOS;
 class NightLightClient;
@@ -100,7 +100,7 @@
   // Initialized in PostProfileInit in all configs:
   std::unique_ptr<KioskNextShellClient> kiosk_next_shell_client_;
   std::unique_ptr<LoginScreenClient> login_screen_client_;
-  std::unique_ptr<MediaClient> media_client_;
+  std::unique_ptr<MediaClientImpl> media_client_;
   std::unique_ptr<policy::DisplaySettingsHandler> display_settings_handler_;
 
   // Initialized in PostBrowserStart in all configs:
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.cc b/chrome/browser/ui/ash/chrome_new_window_client.cc
index d8ad149..cadbc00 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client.cc
@@ -47,6 +47,9 @@
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "components/version_info/version_info.h"
+#include "components/web_modal/web_contents_modal_dialog_host.h"
+#include "components/web_modal/web_contents_modal_dialog_manager.h"
+#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/service_manager_connection.h"
 #include "content/public/common/user_agent.h"
@@ -58,6 +61,9 @@
 #include "ui/aura/window.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/base/window_open_disposition.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/native_widget_types.h"
 #include "url/url_constants.h"
 
 namespace {
@@ -100,7 +106,10 @@
 }
 
 // Implementation of CustomTabSession interface.
-class CustomTabSessionImpl : public arc::mojom::CustomTabSession {
+class CustomTabSessionImpl
+    : public arc::mojom::CustomTabSession,
+      public web_modal::WebContentsModalDialogHost,
+      public web_modal::WebContentsModalDialogManagerDelegate {
  public:
   static arc::mojom::CustomTabSessionPtr Create(
       Profile* profile,
@@ -116,7 +125,32 @@
     return ptr;
   }
 
-  // arc::mojom::CustomTabSession override:
+  // web_modal::WebContentsModalDialogManagerDelegate:
+  web_modal::WebContentsModalDialogHost* GetWebContentsModalDialogHost()
+      override {
+    return this;
+  }
+
+  // web_modal::WebContentsModalDialogHost:
+  gfx::NativeView GetHostView() const override {
+    return custom_tab_->GetHostView();
+  }
+
+  gfx::Point GetDialogPosition(const gfx::Size& size) override {
+    return web_contents_->GetViewBounds().origin();
+  }
+
+  gfx::Size GetMaximumDialogSize() override {
+    return web_contents_->GetViewBounds().size();
+  }
+
+  void AddObserver(web_modal::ModalDialogHostObserver* observer) override {
+  }
+
+  void RemoveObserver(web_modal::ModalDialogHostObserver* observer) override {
+  }
+
+  // arc::mojom::CustomTabSession:
   void OnOpenInChromeClicked() override { forwarded_to_normal_tab_ = true; }
 
  private:
@@ -187,6 +221,15 @@
     // Add a flag to remember this tab originated in the ARC context.
     web_contents->SetUserData(&arc::ArcWebContentsData::kArcTransitionFlag,
                               std::make_unique<arc::ArcWebContentsData>());
+
+    // Attach any required WebContents helpers. Browser tabs automatically get
+    // them attached in TabHelpers::AttachTabHelpers.
+    web_modal::WebContentsModalDialogManager::CreateForWebContents(
+        web_contents.get());
+    web_modal::WebContentsModalDialogManager::FromWebContents(
+        web_contents.get())
+        ->SetDelegate(this);
+
     return web_contents;
   }
 
diff --git a/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc b/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
index d94fc04..6db89e1 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
@@ -15,6 +15,8 @@
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/browser/ui/settings_window_manager_observer_chromeos.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/web_applications/system_web_app_manager.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/account_id/account_id.h"
 #include "components/session_manager/core/session_manager.h"
@@ -172,6 +174,11 @@
     int new_settings_count_ = 0;
   } observer;
 
+  // Install the Settings App.
+  web_app::WebAppProvider::Get(browser()->profile())
+      ->system_web_app_manager()
+      .InstallSystemAppsForTesting();
+
   auto* settings = chrome::SettingsWindowManager::GetInstance();
   settings->AddObserver(&observer);
 
diff --git a/chrome/browser/ui/ash/kiosk_next_shell_client.cc b/chrome/browser/ui/ash/kiosk_next_shell_client.cc
index 028991cb..866454a 100644
--- a/chrome/browser/ui/ash/kiosk_next_shell_client.cc
+++ b/chrome/browser/ui/ash/kiosk_next_shell_client.cc
@@ -7,15 +7,12 @@
 #include <utility>
 
 #include "apps/launcher.h"
-#include "ash/public/interfaces/constants.mojom.h"
 #include "base/logging.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/common/extensions/extension_constants.h"
-#include "content/public/common/service_manager_connection.h"
+#include "components/account_id/account_id.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/constants.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 namespace {
 
@@ -26,20 +23,16 @@
 KioskNextShellClient::KioskNextShellClient() {
   DCHECK(!g_kiosk_next_shell_client_instance);
   g_kiosk_next_shell_client_instance = this;
-
-  ash::mojom::KioskNextShellControllerPtr kiosk_next_shell_controller;
-  content::ServiceManagerConnection::GetForProcess()
-      ->GetConnector()
-      ->BindInterface(ash::mojom::kServiceName, &kiosk_next_shell_controller);
-
-  ash::mojom::KioskNextShellClientPtr client;
-  binding_.Bind(mojo::MakeRequest(&client));
-  kiosk_next_shell_controller->SetClient(std::move(client));
 }
 
 KioskNextShellClient::~KioskNextShellClient() {
   DCHECK_EQ(this, g_kiosk_next_shell_client_instance);
   g_kiosk_next_shell_client_instance = nullptr;
+  ash::KioskNextShellController::Get()->SetClientAndLaunchSession(nullptr);
+}
+
+void KioskNextShellClient::Init() {
+  ash::KioskNextShellController::Get()->SetClientAndLaunchSession(this);
 }
 
 // static
diff --git a/chrome/browser/ui/ash/kiosk_next_shell_client.h b/chrome/browser/ui/ash/kiosk_next_shell_client.h
index 3a087171..081fc87 100644
--- a/chrome/browser/ui/ash/kiosk_next_shell_client.h
+++ b/chrome/browser/ui/ash/kiosk_next_shell_client.h
@@ -5,27 +5,25 @@
 #ifndef CHROME_BROWSER_UI_ASH_KIOSK_NEXT_SHELL_CLIENT_H_
 #define CHROME_BROWSER_UI_ASH_KIOSK_NEXT_SHELL_CLIENT_H_
 
-#include "ash/public/interfaces/kiosk_next_shell.mojom.h"
+#include "ash/public/cpp/kiosk_next_shell.h"
 #include "base/macros.h"
-#include "components/account_id/account_id.h"
-#include "mojo/public/cpp/bindings/binding.h"
 
-class KioskNextShellClient : public ash::mojom::KioskNextShellClient {
+class KioskNextShellClient : public ash::KioskNextShellClient {
  public:
   KioskNextShellClient();
   ~KioskNextShellClient() override;
 
+  void Init();
+
   // Returns the singleton KioskNextShellClient instance, if it exists.
   static KioskNextShellClient* Get();
 
-  // mojom::KioskNextShellClient:
+  // ash::KioskNextShellClient:
   void LaunchKioskNextShell(const AccountId& account_id) override;
 
   bool has_launched() const { return has_launched_; }
 
  private:
-  mojo::Binding<ash::mojom::KioskNextShellClient> binding_{this};
-
   // True once the KioskNextShell has been launched.
   bool has_launched_ = false;
 
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
index 22caccc..97122a8 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -53,6 +53,8 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/test/test_app_window_icon_observer.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
+#include "chrome/browser/web_applications/system_web_app_manager.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -1730,6 +1732,10 @@
 
 // Ensure opening settings and task manager windows create new shelf items.
 IN_PROC_BROWSER_TEST_F(ShelfAppBrowserTest, SettingsAndTaskManagerWindows) {
+  // Install the Settings App.
+  web_app::WebAppProvider::Get(browser()->profile())
+      ->system_web_app_manager()
+      .InstallSystemAppsForTesting();
   chrome::SettingsWindowManager* settings_manager =
       chrome::SettingsWindowManager::GetInstance();
 
diff --git a/chrome/browser/ui/ash/launcher_page_switches_interactive_uitest.cc b/chrome/browser/ui/ash/launcher_page_switches_interactive_uitest.cc
new file mode 100644
index 0000000..a07d83e
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher_page_switches_interactive_uitest.cc
@@ -0,0 +1,118 @@
+// 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 "ash/public/cpp/app_list/app_list_types.h"
+#include "ash/public/cpp/pagination/pagination_model.h"
+#include "ash/public/cpp/pagination/pagination_model_observer.h"
+#include "ash/public/cpp/test/shell_test_api.h"
+#include "base/run_loop.h"
+#include "base/task/post_task.h"
+#include "chrome/browser/ui/app_list/test/chrome_app_list_test_support.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/test/base/perf/performance_test.h"
+#include "ui/base/test/ui_controls.h"
+
+namespace {
+
+class PageSwitchWaiter : public ash::PaginationModelObserver {
+ public:
+  explicit PageSwitchWaiter(ash::PaginationModel* model) : model_(model) {
+    model_->AddObserver(this);
+  }
+  ~PageSwitchWaiter() override { model_->RemoveObserver(this); }
+
+  void Wait() { run_loop_.Run(); }
+
+ private:
+  // ash::PaginationModelObserver:
+  void TransitionEnded() override { run_loop_.Quit(); }
+
+  ash::PaginationModel* model_;
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(PageSwitchWaiter);
+};
+
+}  // namespace
+
+class LauncherPageSwitchesTest : public UIPerformanceTest,
+                                 public ::testing::WithParamInterface<bool> {
+ public:
+  LauncherPageSwitchesTest() = default;
+  ~LauncherPageSwitchesTest() override = default;
+
+  // UIPerformanceTest:
+  void SetUpOnMainThread() override {
+    UIPerformanceTest::SetUpOnMainThread();
+    is_tablet_mode_ = GetParam();
+
+    test::PopulateDummyAppListItems(100);
+    if (base::SysInfo::IsRunningOnChromeOS()) {
+      base::RunLoop run_loop;
+      base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(),
+                            base::TimeDelta::FromSeconds(5));
+      run_loop.Run();
+    }
+
+    ash::ShellTestApi shell_test_api;
+
+    // switch to tablet-mode if necessary.
+    if (is_tablet_mode_)
+      shell_test_api.EnableTabletModeWindowManager(true);
+
+    // Open the fullscreen app; required for page switching.
+    BrowserView* browser_view =
+        BrowserView::GetBrowserViewForBrowser(browser());
+    aura::Window* browser_window = browser_view->GetWidget()->GetNativeWindow();
+    ui_controls::SendKeyPress(browser_window, ui::VKEY_BROWSER_SEARCH,
+                              /*control=*/false,
+                              /*shift=*/true,
+                              /*alt=*/false,
+                              /* command = */ false);
+    shell_test_api.WaitForLauncherAnimationState(
+        ash::AppListViewState::kFullscreenAllApps);
+  }
+
+  // UIPerformanceTest:
+  std::vector<std::string> GetUMAHistogramNames() const override {
+    return {
+        is_tablet_mode_
+            ? "Apps.PaginationTransition.AnimationSmoothness.TabletMode"
+            : "Apps.PaginationTransition.AnimationSmoothness.ClamshellMode",
+    };
+  }
+
+ private:
+  bool is_tablet_mode_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(LauncherPageSwitchesTest);
+};
+
+IN_PROC_BROWSER_TEST_P(LauncherPageSwitchesTest, SwitchToNextPage) {
+  ash::PaginationModel* model = ash::ShellTestApi().GetAppListPaginationModel();
+  ASSERT_TRUE(model);
+  EXPECT_LT(1, model->total_pages());
+  EXPECT_EQ(0, model->selected_page());
+
+  PageSwitchWaiter waiter(model);
+  model->SelectPageRelative(1, /*animate=*/true);
+  waiter.Wait();
+}
+
+IN_PROC_BROWSER_TEST_P(LauncherPageSwitchesTest, SwitchToFarPage) {
+  ash::PaginationModel* model = ash::ShellTestApi().GetAppListPaginationModel();
+  ASSERT_TRUE(model);
+  EXPECT_LT(2, model->total_pages());
+  EXPECT_EQ(0, model->selected_page());
+
+  PageSwitchWaiter waiter(model);
+  model->SelectPageRelative(2, /*animate=*/true);
+  waiter.Wait();
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+                         LauncherPageSwitchesTest,
+                         /*tablet_mode=*/::testing::Bool());
diff --git a/chrome/browser/ui/ash/media_client.cc b/chrome/browser/ui/ash/media_client_impl.cc
similarity index 74%
rename from chrome/browser/ui/ash/media_client.cc
rename to chrome/browser/ui/ash/media_client_impl.cc
index b8629c7..5c0c0b51 100644
--- a/chrome/browser/ui/ash/media_client.cc
+++ b/chrome/browser/ui/ash/media_client_impl.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/media_client.h"
+#include "chrome/browser/ui/ash/media_client_impl.h"
 
 #include <utility>
 
-#include "ash/public/interfaces/constants.mojom.h"
+#include "ash/public/cpp/media_controller.h"
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/logging.h"
@@ -29,18 +29,17 @@
 #include "content/public/browser/media_session.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/service_manager_connection.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/app_window_registry.h"
 #include "extensions/browser/process_manager.h"
 #include "services/media_session/public/mojom/media_session.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 
-using ash::mojom::MediaCaptureState;
+using ash::MediaCaptureState;
 
 namespace {
 
-MediaClient* g_media_client_ = nullptr;
+MediaClientImpl* g_media_client = nullptr;
 
 MediaCaptureState& operator|=(MediaCaptureState& lhs, MediaCaptureState rhs) {
   lhs = static_cast<MediaCaptureState>(static_cast<int>(lhs) |
@@ -55,9 +54,9 @@
   bool audio = indicator->IsCapturingAudio(web_contents);
 
   if (video)
-    *media_state_out |= MediaCaptureState::VIDEO;
+    *media_state_out |= MediaCaptureState::kVideo;
   if (audio)
-    *media_state_out |= MediaCaptureState::AUDIO;
+    *media_state_out |= MediaCaptureState::kAudio;
 }
 
 void GetBrowserMediaCaptureState(const MediaStreamCaptureIndicator* indicator,
@@ -73,7 +72,7 @@
       if (web_contents->GetBrowserContext() != context)
         continue;
       GetMediaCaptureState(indicator, web_contents, media_state_out);
-      if (*media_state_out == MediaCaptureState::AUDIO_VIDEO)
+      if (*media_state_out == MediaCaptureState::kAudioVideo)
         return;
     }
   }
@@ -88,7 +87,7 @@
            apps.begin();
        iter != apps.end(); ++iter) {
     GetMediaCaptureState(indicator, (*iter)->web_contents(), media_state_out);
-    if (*media_state_out == MediaCaptureState::AUDIO_VIDEO)
+    if (*media_state_out == MediaCaptureState::kAudioVideo)
       return;
   }
 }
@@ -104,7 +103,7 @@
     if (!web_contents)
       continue;
     GetMediaCaptureState(indicator, web_contents, media_state_out);
-    if (*media_state_out == MediaCaptureState::AUDIO_VIDEO)
+    if (*media_state_out == MediaCaptureState::kAudioVideo)
       return;
   }
 }
@@ -112,22 +111,22 @@
 MediaCaptureState GetMediaCaptureStateOfAllWebContents(
     content::BrowserContext* context) {
   if (!context)
-    return MediaCaptureState::NONE;
+    return MediaCaptureState::kNone;
 
   scoped_refptr<MediaStreamCaptureIndicator> indicator =
       MediaCaptureDevicesDispatcher::GetInstance()
           ->GetMediaStreamCaptureIndicator();
 
-  MediaCaptureState media_state = MediaCaptureState::NONE;
+  MediaCaptureState media_state = MediaCaptureState::kNone;
   // Browser windows
   GetBrowserMediaCaptureState(indicator.get(), context, &media_state);
-  if (media_state == MediaCaptureState::AUDIO_VIDEO)
-    return MediaCaptureState::AUDIO_VIDEO;
+  if (media_state == MediaCaptureState::kAudioVideo)
+    return MediaCaptureState::kAudioVideo;
 
   // App windows
   GetAppMediaCaptureState(indicator.get(), context, &media_state);
-  if (media_state == MediaCaptureState::AUDIO_VIDEO)
-    return MediaCaptureState::AUDIO_VIDEO;
+  if (media_state == MediaCaptureState::kAudioVideo)
+    return MediaCaptureState::kAudioVideo;
 
   // Extensions
   GetExtensionMediaCaptureState(indicator.get(), context, &media_state);
@@ -137,55 +136,56 @@
 
 }  // namespace
 
-MediaClient::MediaClient() {
+MediaClientImpl::MediaClientImpl() {
   MediaCaptureDevicesDispatcher::GetInstance()->AddObserver(this);
   BrowserList::GetInstance()->AddObserver(this);
 
-  DCHECK(!g_media_client_);
-  g_media_client_ = this;
+  DCHECK(!g_media_client);
+  g_media_client = this;
 }
 
-MediaClient::~MediaClient() {
-  g_media_client_ = nullptr;
+MediaClientImpl::~MediaClientImpl() {
+  g_media_client = nullptr;
+
+  if (media_controller_ && ash::MediaController::Get() == media_controller_)
+    media_controller_->SetClient(nullptr);
 
   MediaCaptureDevicesDispatcher::GetInstance()->RemoveObserver(this);
   BrowserList::GetInstance()->RemoveObserver(this);
 }
 
 // static
-MediaClient* MediaClient::Get() {
-  return g_media_client_;
+MediaClientImpl* MediaClientImpl::Get() {
+  return g_media_client;
 }
 
-void MediaClient::Init() {
-  DCHECK(!media_controller_.is_bound());
+void MediaClientImpl::Init() {
+  DCHECK(!media_controller_);
 
-  service_manager::Connector* connector =
-      content::ServiceManagerConnection::GetForProcess()->GetConnector();
-  connector->BindInterface(ash::mojom::kServiceName, &media_controller_);
-  BindAndSetClient();
+  media_controller_ = ash::MediaController::Get();
+  media_controller_->SetClient(this);
 }
 
-void MediaClient::InitForTesting(ash::mojom::MediaControllerPtr controller) {
-  DCHECK(!media_controller_.is_bound());
+void MediaClientImpl::InitForTesting(ash::MediaController* controller) {
+  DCHECK(!media_controller_);
 
-  media_controller_ = std::move(controller);
-  BindAndSetClient();
+  media_controller_ = controller;
+  media_controller_->SetClient(this);
 }
 
-void MediaClient::HandleMediaNextTrack() {
+void MediaClientImpl::HandleMediaNextTrack() {
   HandleMediaAction(ui::VKEY_MEDIA_NEXT_TRACK);
 }
 
-void MediaClient::HandleMediaPlayPause() {
+void MediaClientImpl::HandleMediaPlayPause() {
   HandleMediaAction(ui::VKEY_MEDIA_PLAY_PAUSE);
 }
 
-void MediaClient::HandleMediaPrevTrack() {
+void MediaClientImpl::HandleMediaPrevTrack() {
   HandleMediaAction(ui::VKEY_MEDIA_PREV_TRACK);
 }
 
-void MediaClient::RequestCaptureState() {
+void MediaClientImpl::RequestCaptureState() {
   base::flat_map<AccountId, MediaCaptureState> capture_states;
   for (user_manager::User* user :
        user_manager::UserManager::Get()->GetLRULoggedInUsers()) {
@@ -196,32 +196,32 @@
   media_controller_->NotifyCaptureState(std::move(capture_states));
 }
 
-void MediaClient::SuspendMediaSessions() {
+void MediaClientImpl::SuspendMediaSessions() {
   for (auto* web_contents : AllTabContentses()) {
     content::MediaSession::Get(web_contents)
         ->Suspend(content::MediaSession::SuspendType::kSystem);
   }
 }
 
-void MediaClient::OnRequestUpdate(int render_process_id,
-                                  int render_frame_id,
-                                  blink::MediaStreamType stream_type,
-                                  const content::MediaRequestState state) {
+void MediaClientImpl::OnRequestUpdate(int render_process_id,
+                                      int render_frame_id,
+                                      blink::mojom::MediaStreamType stream_type,
+                                      const content::MediaRequestState state) {
   DCHECK(base::MessageLoopCurrentForUI::IsSet());
   // The PostTask is necessary because the state of MediaStreamCaptureIndicator
   // gets updated after this.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&MediaClient::RequestCaptureState,
+      FROM_HERE, base::BindOnce(&MediaClientImpl::RequestCaptureState,
                                 weak_ptr_factory_.GetWeakPtr()));
 }
 
-void MediaClient::OnBrowserSetLastActive(Browser* browser) {
+void MediaClientImpl::OnBrowserSetLastActive(Browser* browser) {
   active_context_ = browser ? browser->profile() : nullptr;
 
   UpdateForceMediaClientKeyHandling();
 }
 
-void MediaClient::EnableCustomMediaKeyHandler(
+void MediaClientImpl::EnableCustomMediaKeyHandler(
     content::BrowserContext* context,
     ui::MediaKeysListener::Delegate* delegate) {
   auto it = media_key_delegates_.find(context);
@@ -234,7 +234,7 @@
   UpdateForceMediaClientKeyHandling();
 }
 
-void MediaClient::DisableCustomMediaKeyHandler(
+void MediaClientImpl::DisableCustomMediaKeyHandler(
     content::BrowserContext* context,
     ui::MediaKeysListener::Delegate* delegate) {
   if (!base::ContainsKey(media_key_delegates_, context))
@@ -248,17 +248,7 @@
   UpdateForceMediaClientKeyHandling();
 }
 
-void MediaClient::FlushForTesting() {
-  media_controller_.FlushForTesting();
-}
-
-void MediaClient::BindAndSetClient() {
-  ash::mojom::MediaClientAssociatedPtrInfo ptr_info;
-  binding_.Bind(mojo::MakeRequest(&ptr_info));
-  media_controller_->SetClient(std::move(ptr_info));
-}
-
-void MediaClient::UpdateForceMediaClientKeyHandling() {
+void MediaClientImpl::UpdateForceMediaClientKeyHandling() {
   bool enabled = GetCurrentMediaKeyDelegate() != nullptr;
 
   if (enabled == is_forcing_media_client_key_handling_)
@@ -269,7 +259,7 @@
   media_controller_->SetForceMediaClientKeyHandling(enabled);
 }
 
-ui::MediaKeysListener::Delegate* MediaClient::GetCurrentMediaKeyDelegate()
+ui::MediaKeysListener::Delegate* MediaClientImpl::GetCurrentMediaKeyDelegate()
     const {
   auto it = media_key_delegates_.find(active_context_);
   if (it != media_key_delegates_.end())
@@ -278,7 +268,7 @@
   return nullptr;
 }
 
-void MediaClient::HandleMediaAction(ui::KeyboardCode keycode) {
+void MediaClientImpl::HandleMediaAction(ui::KeyboardCode keycode) {
   if (ui::MediaKeysListener::Delegate* custom = GetCurrentMediaKeyDelegate()) {
     custom->OnMediaKeysAccelerator(ui::Accelerator(keycode, ui::EF_NONE));
     return;
diff --git a/chrome/browser/ui/ash/media_client.h b/chrome/browser/ui/ash/media_client_impl.h
similarity index 72%
rename from chrome/browser/ui/ash/media_client.h
rename to chrome/browser/ui/ash/media_client_impl.h
index 397d9018..6f73ac5 100644
--- a/chrome/browser/ui/ash/media_client.h
+++ b/chrome/browser/ui/ash/media_client_impl.h
@@ -2,35 +2,39 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_MEDIA_CLIENT_H_
-#define CHROME_BROWSER_UI_ASH_MEDIA_CLIENT_H_
+#ifndef CHROME_BROWSER_UI_ASH_MEDIA_CLIENT_IMPL_H_
+#define CHROME_BROWSER_UI_ASH_MEDIA_CLIENT_IMPL_H_
 
-#include "ash/public/interfaces/media.mojom.h"
+#include "ash/public/cpp/media_client.h"
 #include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/ui/browser_list_observer.h"
-#include "mojo/public/cpp/bindings/associated_binding.h"
 #include "ui/base/accelerators/media_keys_listener.h"
 
-class MediaClient : public ash::mojom::MediaClient,
-                    public BrowserListObserver,
-                    MediaCaptureDevicesDispatcher::Observer {
- public:
-  MediaClient();
-  ~MediaClient() override;
+namespace ash {
+enum class MediaCaptureState;
+class MediaController;
+}  // namespace ash
 
-  // Initializes and connects to ash.
+class MediaClientImpl : public ash::MediaClient,
+                        public BrowserListObserver,
+                        public MediaCaptureDevicesDispatcher::Observer {
+ public:
+  MediaClientImpl();
+  ~MediaClientImpl() override;
+
+  // Initializes and set as client for ash.
   void Init();
 
   // Tests can provide a mock mojo interface for the ash controller.
-  void InitForTesting(ash::mojom::MediaControllerPtr controller);
+  void InitForTesting(ash::MediaController* controller);
 
   // Returns a pointer to the singleton MediaClient, or nullptr if none exists.
-  static MediaClient* Get();
+  static MediaClientImpl* Get();
 
-  // ash::mojom::MediaClient:
+  // ash::MediaClient:
   void HandleMediaNextTrack() override;
   void HandleMediaPlayPause() override;
   void HandleMediaPrevTrack() override;
@@ -40,7 +44,7 @@
   // MediaCaptureDevicesDispatcher::Observer:
   void OnRequestUpdate(int render_process_id,
                        int render_frame_id,
-                       blink::MediaStreamType stream_type,
+                       blink::mojom::MediaStreamType stream_type,
                        const content::MediaRequestState state) override;
 
   // BrowserListObserver:
@@ -53,12 +57,7 @@
   void DisableCustomMediaKeyHandler(content::BrowserContext* context,
                                     ui::MediaKeysListener::Delegate* delegate);
 
-  void FlushForTesting();
-
  private:
-  // Binds this object to its mojo interface and sets it as the ash client.
-  void BindAndSetClient();
-
   // Sets |is_forcing_media_client_key_handling_| to true if
   // |GetCurrentMediaKeyDelegate| returns a delegate. This will also mirror the
   // value of |is_forcing_media_client_key_handling_| to Ash.
@@ -73,14 +72,14 @@
   // Returns the media capture state for the current user at
   // |user_index|. (Note that this isn't stable, see implementation comment on
   // RequestCaptureState()).
-  ash::mojom::MediaCaptureState GetMediaCaptureStateByIndex(int user_index);
+  ash::MediaCaptureState GetMediaCaptureStateByIndex(int user_index);
 
   // Handles the media key action for the key with |code|. If there is a
   // |GetCurrentMediaKeyDelegate| then the action will be forwarded to the
   // delegate. Otherwise, we will forward the action to the extensions API.
   void HandleMediaAction(ui::KeyboardCode code);
 
-  ash::mojom::MediaControllerPtr media_controller_;
+  ash::MediaController* media_controller_ = nullptr;
 
   base::flat_map<content::BrowserContext*, ui::MediaKeysListener::Delegate*>
       media_key_delegates_;
@@ -91,11 +90,9 @@
 
   content::BrowserContext* active_context_ = nullptr;
 
-  mojo::AssociatedBinding<ash::mojom::MediaClient> binding_{this};
+  base::WeakPtrFactory<MediaClientImpl> weak_ptr_factory_{this};
 
-  base::WeakPtrFactory<MediaClient> weak_ptr_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(MediaClient);
+  DISALLOW_COPY_AND_ASSIGN(MediaClientImpl);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_MEDIA_CLIENT_H_
+#endif  // CHROME_BROWSER_UI_ASH_MEDIA_CLIENT_IMPL_H_
diff --git a/chrome/browser/ui/ash/media_client_unittest.cc b/chrome/browser/ui/ash/media_client_impl_unittest.cc
similarity index 85%
rename from chrome/browser/ui/ash/media_client_unittest.cc
rename to chrome/browser/ui/ash/media_client_impl_unittest.cc
index c0dac17..2186605 100644
--- a/chrome/browser/ui/ash/media_client_unittest.cc
+++ b/chrome/browser/ui/ash/media_client_impl_unittest.cc
@@ -2,40 +2,33 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/media_client.h"
+#include "chrome/browser/ui/ash/media_client_impl.h"
 
 #include <memory>
 
-#include "ash/public/interfaces/media.mojom.h"
+#include "ash/public/cpp/media_controller.h"
 #include "base/optional.h"
 #include "chrome/browser/chromeos/extensions/media_player_api.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "components/account_id/account_id.h"
-#include "mojo/public/cpp/bindings/binding.h"
 #include "ui/base/accelerators/media_keys_listener.h"
 
 namespace {
 
-class TestMediaController : public ash::mojom::MediaController {
+class TestMediaController : public ash::MediaController {
  public:
   TestMediaController() = default;
   ~TestMediaController() override = default;
 
-  ash::mojom::MediaControllerPtr CreateMediaControllerPtr() {
-    ash::mojom::MediaControllerPtr ptr;
-    binding_.Bind(mojo::MakeRequest(&ptr));
-    return ptr;
-  }
-
-  // mojom::MediaController:
-  void SetClient(ash::mojom::MediaClientAssociatedPtrInfo client) override {}
+  // ash::MediaController:
+  void SetClient(ash::MediaClient* client) override {}
   void SetForceMediaClientKeyHandling(bool enabled) override {
     force_media_client_key_handling_ = enabled;
   }
   void NotifyCaptureState(
-      const base::flat_map<AccountId, ash::mojom::MediaCaptureState>&
-          capture_states) override {}
+      const base::flat_map<AccountId, ash::MediaCaptureState>& capture_states)
+      override {}
 
   bool force_media_client_key_handling() const {
     return force_media_client_key_handling_;
@@ -44,8 +37,6 @@
  private:
   bool force_media_client_key_handling_ = false;
 
-  mojo::Binding<ash::mojom::MediaController> binding_{this};
-
   DISALLOW_COPY_AND_ASSIGN(TestMediaController);
 };
 
@@ -74,7 +65,7 @@
 
 class MediaClientTest : public BrowserWithTestWindowTest {
  public:
-  MediaClientTest() {}
+  MediaClientTest() = default;
   ~MediaClientTest() override = default;
 
   void SetUp() override {
@@ -87,14 +78,15 @@
     extensions::MediaPlayerAPI::Get(profile());
 
     test_delegate_ = std::make_unique<TestMediaKeysDelegate>();
+
+    media_controller_resetter_ =
+        std::make_unique<ash::MediaController::ScopedResetterForTest>();
     test_media_controller_ = std::make_unique<TestMediaController>();
 
-    media_client_ = std::make_unique<MediaClient>();
-    media_client_->InitForTesting(
-        test_media_controller_->CreateMediaControllerPtr());
+    media_client_ = std::make_unique<MediaClientImpl>();
+    media_client_->InitForTesting(test_media_controller_.get());
 
     BrowserList::SetLastActive(browser());
-    client()->FlushForTesting();
 
     ASSERT_FALSE(test_media_controller_->force_media_client_key_handling());
     ASSERT_EQ(base::nullopt, delegate()->ConsumeLastMediaKey());
@@ -103,6 +95,7 @@
   void TearDown() override {
     media_client_.reset();
     test_media_controller_.reset();
+    media_controller_resetter_.reset();
     test_delegate_.reset();
 
     alt_browser_->tab_strip_model()->CloseAllTabs();
@@ -112,7 +105,7 @@
     BrowserWithTestWindowTest::TearDown();
   }
 
-  MediaClient* client() { return media_client_.get(); }
+  MediaClientImpl* client() { return media_client_.get(); }
 
   TestMediaController* controller() { return test_media_controller_.get(); }
 
@@ -124,8 +117,10 @@
 
  private:
   std::unique_ptr<TestMediaKeysDelegate> test_delegate_;
+  std::unique_ptr<ash::MediaController::ScopedResetterForTest>
+      media_controller_resetter_;
   std::unique_ptr<TestMediaController> test_media_controller_;
-  std::unique_ptr<MediaClient> media_client_;
+  std::unique_ptr<MediaClientImpl> media_client_;
 
   std::unique_ptr<Browser> alt_browser_;
   std::unique_ptr<BrowserWindow> alt_window_;
@@ -140,7 +135,6 @@
   // Enable custom media key handling for the current browser. Ensure that the
   // client set the override on the controller.
   client()->EnableCustomMediaKeyHandler(profile(), delegate());
-  client()->FlushForTesting();
   EXPECT_TRUE(controller()->force_media_client_key_handling());
 
   // Simulate the media key and check that the delegate received it.
@@ -149,7 +143,6 @@
 
   // Change the active browser and ensure the override was disabled.
   BrowserList::SetLastActive(alt_browser());
-  client()->FlushForTesting();
   EXPECT_FALSE(controller()->force_media_client_key_handling());
 
   // Simulate the media key and check that the delegate did not receive it.
@@ -158,7 +151,6 @@
 
   // Change the active browser back and ensure the override was enabled.
   BrowserList::SetLastActive(browser());
-  client()->FlushForTesting();
   EXPECT_TRUE(controller()->force_media_client_key_handling());
 
   // Simulate the media key and check the delegate received it.
@@ -168,7 +160,6 @@
   // Disable custom media key handling for the current browser and ensure the
   // override was disabled.
   client()->DisableCustomMediaKeyHandler(profile(), delegate());
-  client()->FlushForTesting();
   EXPECT_FALSE(controller()->force_media_client_key_handling());
 
   // Simulate the media key and check the delegate did not receive it.
@@ -183,7 +174,6 @@
   // Enable custom media key handling for the current browser. Ensure that the
   // client set the override on the controller.
   client()->EnableCustomMediaKeyHandler(profile(), delegate());
-  client()->FlushForTesting();
   EXPECT_TRUE(controller()->force_media_client_key_handling());
 
   // Simulate the media key and check that the delegate received it.
@@ -192,7 +182,6 @@
 
   // Change the active browser and ensure the override was disabled.
   BrowserList::SetLastActive(alt_browser());
-  client()->FlushForTesting();
   EXPECT_FALSE(controller()->force_media_client_key_handling());
 
   // Simulate the media key and check that the delegate did not receive it.
@@ -201,7 +190,6 @@
 
   // Change the active browser back and ensure the override was enabled.
   BrowserList::SetLastActive(browser());
-  client()->FlushForTesting();
   EXPECT_TRUE(controller()->force_media_client_key_handling());
 
   // Simulate the media key and check the delegate received it.
@@ -211,7 +199,6 @@
   // Disable custom media key handling for the current browser and ensure the
   // override was disabled.
   client()->DisableCustomMediaKeyHandler(profile(), delegate());
-  client()->FlushForTesting();
   EXPECT_FALSE(controller()->force_media_client_key_handling());
 
   // Simulate the media key and check the delegate did not receive it.
@@ -226,7 +213,6 @@
   // Enable custom media key handling for the current browser. Ensure that the
   // client set the override on the controller.
   client()->EnableCustomMediaKeyHandler(profile(), delegate());
-  client()->FlushForTesting();
   EXPECT_TRUE(controller()->force_media_client_key_handling());
 
   // Simulate the media key and check that the delegate received it.
@@ -235,7 +221,6 @@
 
   // Change the active browser and ensure the override was disabled.
   BrowserList::SetLastActive(alt_browser());
-  client()->FlushForTesting();
   EXPECT_FALSE(controller()->force_media_client_key_handling());
 
   // Simulate the media key and check that the delegate did not receive it.
@@ -244,7 +229,6 @@
 
   // Change the active browser back and ensure the override was enabled.
   BrowserList::SetLastActive(browser());
-  client()->FlushForTesting();
   EXPECT_TRUE(controller()->force_media_client_key_handling());
 
   // Simulate the media key and check the delegate received it.
@@ -254,7 +238,6 @@
   // Disable custom media key handling for the current browser and ensure the
   // override was disabled.
   client()->DisableCustomMediaKeyHandler(profile(), delegate());
-  client()->FlushForTesting();
   EXPECT_FALSE(controller()->force_media_client_key_handling());
 
   // Simulate the media key and check the delegate did not receive it.
diff --git a/chrome/browser/ui/ash/network/network_state_notifier.cc b/chrome/browser/ui/ash/network/network_state_notifier.cc
index bfb74cf..2e3a661 100644
--- a/chrome/browser/ui/ash/network/network_state_notifier.cc
+++ b/chrome/browser/ui/ash/network/network_state_notifier.cc
@@ -496,7 +496,7 @@
   const NetworkState* network = GetNetworkStateForGuid(network_id);
   if (!network)
     return;
-  std::string error = network->GetErrorState();
+  std::string error = network->error();
   if (!error.empty()) {
     NET_LOG(ERROR) << "Notify ShowNetworkSettings: " << network_id
                    << ": Error: " << error;
diff --git a/chrome/browser/ui/ash/split_view_interactive_uitest.cc b/chrome/browser/ui/ash/split_view_interactive_uitest.cc
index 1f42475..f52e9c40 100644
--- a/chrome/browser/ui/ash/split_view_interactive_uitest.cc
+++ b/chrome/browser/ui/ash/split_view_interactive_uitest.cc
@@ -2,164 +2,157 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/test/shell_test_api.h"
-#include "ash/public/cpp/window_state_type.h"
-#include "base/command_line.h"
+#include "ash/shell.h"
+#include "ash/wm/splitview/split_view_controller.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/system/sys_info.h"
 #include "base/task/post_task.h"
 #include "base/test/bind_test_util.h"
-#include "chrome/browser/ui/ash/ash_test_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
-#include "chrome/common/webui_url_constants.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/test/base/interactive_test_utils.h"
-#include "chrome/test/base/perf/drag_event_generator.h"
 #include "chrome/test/base/perf/performance_test.h"
 #include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
 #include "ui/compositor/compositor.h"
+#include "ui/compositor/compositor_switches.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
-#include "ui/wm/core/wm_core_switches.h"
+#include "ui/gl/gl_switches.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_observer.h"
 
 namespace {
 
-class SplitViewTest
-    : public UIPerformanceTest,
-      public testing::WithParamInterface<::testing::tuple<bool, bool>> {
+class SplitViewTest : public UIPerformanceTest {
  public:
   SplitViewTest() = default;
   ~SplitViewTest() override = default;
 
   // UIPerformanceTest:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    // We're not interested in window transition animation in this test,
-    // so just disable it.
-    command_line->AppendSwitch(wm::switches::kWindowAnimationsDisabled);
-  }
-  void SetUpOnMainThread() override {
-    UIPerformanceTest::SetUpOnMainThread();
-
-    use_ntp_ = std::get<0>(GetParam());
-    use_touch_ = std::get<1>(GetParam());
-
-    if (use_ntp_)
-      ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
-  }
-
-  bool use_touch() const { return use_touch_; }
-
-  void SetUMAToObserve(std::string name) { uma_name_ = std::move(name); }
-
-  Browser* CreateBrowserMaybeWithNtp() {
-    Browser* new_browser = CreateBrowser(browser()->profile());
-    if (use_ntp_)
-      ui_test_utils::NavigateToURL(new_browser,
-                                   GURL(chrome::kChromeUINewTabURL));
-    return new_browser;
-  }
-
-  void WaitForWarmup() {
-    // If running on device, wait a bit so that the display is actually powered
-    // on.
-    // TODO: this should be better factored.
-    base::TimeDelta warmup = base::TimeDelta::FromSeconds(
-        base::SysInfo::IsRunningOnChromeOS() ? 5 : 0);
-    // Give ntp at least 1 seconds to be fully resized.
-    if (use_ntp_ && warmup.is_zero())
-      warmup += base::TimeDelta::FromSeconds(1);
-
-    if (!warmup.is_zero()) {
-      base::RunLoop run_loop;
-      base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(), warmup);
-      run_loop.Run();
-    }
+    command_line->AppendSwitch(ash::switches::kAshEnableTabletMode);
   }
 
  private:
   std::vector<std::string> GetUMAHistogramNames() const override {
-    return {uma_name_};
+    return {"Ash.SplitViewResize.PresentationTime.TabletMode.MultiWindow"};
   }
 
-  bool use_ntp_;
-  bool use_touch_;
-  std::string uma_name_;
-
   DISALLOW_COPY_AND_ASSIGN(SplitViewTest);
 };
 
-IN_PROC_BROWSER_TEST_P(SplitViewTest, ResizeTwoWindows) {
-  SetUMAToObserve(
-      "Ash.SplitViewResize.PresentationTime.TabletMode.MultiWindow");
+// Used to wait for a window resize to show up on screen.
+class WidgetResizeWaiter : public views::WidgetObserver {
+ public:
+  explicit WidgetResizeWaiter(views::Widget* widget) : widget_(widget) {
+    widget_->AddObserver(this);
+  }
 
+  ~WidgetResizeWaiter() override {
+    widget_->RemoveObserver(this);
+    EXPECT_FALSE(waiting_for_frame_);
+  }
+
+  void WaitForDisplay() {
+    do {
+      if (waiting_for_frame_) {
+        run_loop_ = std::make_unique<base::RunLoop>();
+        run_loop_->Run();
+        EXPECT_FALSE(waiting_for_frame_);
+      }
+      ash::ShellTestApi().WaitForNoPointerHoldLock();
+    } while (waiting_for_frame_);
+  }
+
+ private:
+  void OnFramePresented(const gfx::PresentationFeedback& feedback) {
+    waiting_for_frame_ = false;
+    if (run_loop_)
+      run_loop_->Quit();
+  }
+
+  // views::WidgetObserver:
+  void OnWidgetBoundsChanged(views::Widget* widget,
+                             const gfx::Rect& new_bounds) override {
+    ui::Compositor* compositor =
+        widget->GetNativeWindow()->GetHost()->compositor();
+    ASSERT_TRUE(compositor);
+    waiting_for_frame_ = true;
+    compositor->RequestPresentationTimeForNextFrame(base::BindOnce(
+        &WidgetResizeWaiter::OnFramePresented, base::Unretained(this)));
+  }
+
+  views::Widget* widget_;
+  bool waiting_for_frame_ = false;
+  std::unique_ptr<base::RunLoop> run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(WidgetResizeWaiter);
+};
+
+IN_PROC_BROWSER_TEST_F(SplitViewTest, SplitViewResize) {
   // This test is intended to gauge performance of resizing windows while in
   // tablet mode split view. It does the following:
   // . creates two browser windows.
-  // . snaps one to the left, one to the right.
   // . enters tablet mode.
+  // . snaps one to the left, one to the right.
   // . drags the resizer, which triggers resizing both browsers.
+  browser()->window()->Maximize();
 
-  aura::Window* browser_window = browser()->window()->GetNativeWindow();
-  test::ActivateAndSnapWindow(browser_window,
-                              ash::WindowStateType::kLeftSnapped);
+  Browser* browser2 = CreateBrowser(browser()->profile());
+  browser2->window()->Show();
+  browser2->window()->Maximize();
 
-  Browser* browser2 = CreateBrowserMaybeWithNtp();
-  aura::Window* browser2_window = browser2->window()->GetNativeWindow();
-  test::ActivateAndSnapWindow(browser2_window,
-                              ash::WindowStateType::kRightSnapped);
+  // If running on device, wait a bit so that the display is actually powered
+  // on.
+  // TODO: this should be better factored.
+  if (base::SysInfo::IsRunningOnChromeOS()) {
+    base::RunLoop run_loop;
+    base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(),
+                          base::TimeDelta::FromSeconds(5));
+    run_loop.Run();
+  }
+
   ash::ShellTestApi().EnableTabletModeWindowManager(true);
 
-  WaitForWarmup();
+  views::Widget* browser_widget =
+      BrowserView::GetBrowserViewForBrowser(browser())->GetWidget();
+  views::Widget* browser2_widget =
+      BrowserView::GetBrowserViewForBrowser(browser2)->GetWidget();
+  ash::Shell* shell = ash::Shell::Get();
+  shell->split_view_controller()->SnapWindow(browser2_widget->GetNativeWindow(),
+                                             ash::SplitViewController::LEFT);
+  shell->split_view_controller()->SnapWindow(browser_widget->GetNativeWindow(),
+                                             ash::SplitViewController::RIGHT);
+
+  ash::ShellTestApi().WaitForNoPointerHoldLock();
 
   const gfx::Size display_size =
       display::Screen::GetScreen()->GetPrimaryDisplay().bounds().size();
-  const gfx::Point start_position(gfx::Rect(display_size).CenterPoint());
+  const gfx::Point start_position(display_size.width() / 2,
+                                  display_size.height() / 2);
   TRACE_EVENT_ASYNC_BEGIN0("ui", "Interaction.ui_WindowResize", this);
-  gfx::Point end_position(start_position);
-  end_position.set_x(end_position.x() - 60);
-  ui_test_utils::DragEventGenerator generator(
-      std::make_unique<ui_test_utils::InterpolatedProducer>(
-          start_position, end_position,
-          base::TimeDelta::FromMilliseconds(1000)),
-      use_touch());
-  generator.Wait();
+  ASSERT_TRUE(
+      ui_test_utils::SendMouseMoveSync(
+          gfx::Point(start_position.x(), start_position.y())) &&
+      ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, ui_controls::DOWN));
+
+  constexpr int kNumIncrements = 20;
+  int width = browser_widget->GetWindowBoundsInScreen().width();
+  for (int i = 0; i < kNumIncrements; ++i) {
+    const gfx::Point resize_point(start_position.x() - i - 1,
+                                  start_position.y());
+    WidgetResizeWaiter observer(browser_widget);
+    ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(resize_point));
+    observer.WaitForDisplay();
+    ++width;
+    EXPECT_EQ(width, browser_widget->GetWindowBoundsInScreen().width());
+  }
   TRACE_EVENT_ASYNC_END0("ui", "Interaction.ui_WindowResize", this);
 }
 
-IN_PROC_BROWSER_TEST_P(SplitViewTest, ResizeWithOverview) {
-  SetUMAToObserve(
-      "Ash.SplitViewResize.PresentationTime.TabletMode.WithOverview");
-
-  Browser* browser2 = CreateBrowserMaybeWithNtp();
-
-  aura::Window* browser2_window = browser2->window()->GetNativeWindow();
-  test::ActivateAndSnapWindow(browser2_window,
-                              ash::WindowStateType::kRightSnapped);
-
-  ash::ShellTestApi().EnableTabletModeWindowManager(true);
-  ash::ShellTestApi().WaitForOverviewAnimationState(
-      ash::OverviewAnimationState::kEnterAnimationComplete);
-
-  WaitForWarmup();
-
-  const gfx::Point start_position =
-      display::Screen::GetScreen()->GetPrimaryDisplay().bounds().CenterPoint();
-  TRACE_EVENT_ASYNC_BEGIN0("ui", "Interaction.ui_WindowResize", this);
-  gfx::Point end_position(start_position);
-  end_position.set_x(end_position.x() - 60);
-  ui_test_utils::DragEventGenerator generator(
-      std::make_unique<ui_test_utils::InterpolatedProducer>(
-          start_position, end_position,
-          base::TimeDelta::FromMilliseconds(1000)),
-      use_touch());
-  generator.Wait();
-  TRACE_EVENT_ASYNC_END0("ui", "Interaction.ui_WindowResize", this);
-}
-
-INSTANTIATE_TEST_SUITE_P(,
-                         SplitViewTest,
-                         ::testing::Combine(/*ntp=*/testing::Bool(),
-                                            /*touch=*/testing::Bool()));
-
 }  // namespace
diff --git a/chrome/browser/ui/ash/system_tray_client.cc b/chrome/browser/ui/ash/system_tray_client.cc
index 8ee6505..3fba3f8 100644
--- a/chrome/browser/ui/ash/system_tray_client.cc
+++ b/chrome/browser/ui/ash/system_tray_client.cc
@@ -5,8 +5,8 @@
 #include "chrome/browser/ui/ash/system_tray_client.h"
 
 #include "ash/public/cpp/system_tray.h"
+#include "ash/public/cpp/update_types.h"
 #include "ash/public/interfaces/locale.mojom.h"
-#include "ash/public/interfaces/update.mojom.h"
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
@@ -71,24 +71,24 @@
 }
 
 // Returns the severity of a pending Chrome / Chrome OS update.
-ash::mojom::UpdateSeverity GetUpdateSeverity(UpgradeDetector* detector) {
+ash::UpdateSeverity GetUpdateSeverity(UpgradeDetector* detector) {
   switch (detector->upgrade_notification_stage()) {
     case UpgradeDetector::UPGRADE_ANNOYANCE_NONE:
-      return ash::mojom::UpdateSeverity::NONE;
+      return ash::UpdateSeverity::kNone;
     case UpgradeDetector::UPGRADE_ANNOYANCE_VERY_LOW:
-      return ash::mojom::UpdateSeverity::VERY_LOW;
+      return ash::UpdateSeverity::kVeryLow;
     case UpgradeDetector::UPGRADE_ANNOYANCE_LOW:
-      return ash::mojom::UpdateSeverity::LOW;
+      return ash::UpdateSeverity::kLow;
     case UpgradeDetector::UPGRADE_ANNOYANCE_ELEVATED:
-      return ash::mojom::UpdateSeverity::ELEVATED;
+      return ash::UpdateSeverity::kElevated;
     case UpgradeDetector::UPGRADE_ANNOYANCE_HIGH:
-      return ash::mojom::UpdateSeverity::HIGH;
+      return ash::UpdateSeverity::kHigh;
     case UpgradeDetector::UPGRADE_ANNOYANCE_CRITICAL:
       break;
   }
   DCHECK_EQ(detector->upgrade_notification_stage(),
             UpgradeDetector::UPGRADE_ANNOYANCE_CRITICAL);
-  return ash::mojom::UpdateSeverity::CRITICAL;
+  return ash::UpdateSeverity::kCritical;
 }
 
 const chromeos::NetworkState* GetNetworkState(const std::string& network_id) {
@@ -109,7 +109,7 @@
 
 SystemTrayClient::SystemTrayClient()
     : system_tray_(ash::SystemTray::Get()),
-      update_notification_style_(ash::mojom::NotificationStyle::DEFAULT) {
+      update_notification_style_(ash::NotificationStyle::kDefault) {
   // If this observes clock setting changes before ash comes up the IPCs will
   // be queued on |system_tray_|.
   g_browser_process->platform_part()->GetSystemClock()->AddObserver(this);
@@ -162,7 +162,7 @@
 }
 
 void SystemTrayClient::SetUpdateNotificationState(
-    ash::mojom::NotificationStyle style,
+    ash::NotificationStyle style,
     const base::string16& notification_title,
     const base::string16& notification_body) {
   update_notification_style_ = style;
@@ -428,23 +428,23 @@
     return;
 
   // Get the Chrome update severity.
-  ash::mojom::UpdateSeverity severity = GetUpdateSeverity(detector);
+  ash::UpdateSeverity severity = GetUpdateSeverity(detector);
 
   // Flash updates are low severity unless the Chrome severity is higher.
   if (flash_update_available_)
-    severity = std::max(severity, ash::mojom::UpdateSeverity::LOW);
+    severity = std::max(severity, ash::UpdateSeverity::kLow);
 
   // Show a string specific to updating flash player if there is no system
   // update.
-  ash::mojom::UpdateType update_type = detector->notify_upgrade()
-                                           ? ash::mojom::UpdateType::SYSTEM
-                                           : ash::mojom::UpdateType::FLASH;
+  ash::UpdateType update_type = detector->notify_upgrade()
+                                    ? ash::UpdateType::kSystem
+                                    : ash::UpdateType::kFlash;
 
   system_tray_->ShowUpdateIcon(severity, detector->is_factory_reset_required(),
                                detector->is_rollback(), update_type);
 
   // Only overwrite title and body for system updates, not for flash updates.
-  if (update_type == ash::mojom::UpdateType::SYSTEM) {
+  if (update_type == ash::UpdateType::kSystem) {
     system_tray_->SetUpdateNotificationState(update_notification_style_,
                                              update_notification_title_,
                                              update_notification_body_);
diff --git a/chrome/browser/ui/ash/system_tray_client.h b/chrome/browser/ui/ash/system_tray_client.h
index 39bd19a..fb681ff 100644
--- a/chrome/browser/ui/ash/system_tray_client.h
+++ b/chrome/browser/ui/ash/system_tray_client.h
@@ -7,7 +7,6 @@
 
 #include "ash/public/cpp/system_tray_client.h"
 #include "ash/public/interfaces/locale.mojom-forward.h"
-#include "ash/public/interfaces/update.mojom-forward.h"
 #include "base/macros.h"
 #include "chrome/browser/chromeos/system/system_clock_observer.h"
 #include "chrome/browser/upgrade_detector/upgrade_observer.h"
@@ -16,6 +15,7 @@
 namespace ash {
 class SystemTray;
 enum class LoginStatus;
+enum class NotificationStyle;
 }
 
 // Handles method calls delegated back to chrome from ash. Also notifies ash of
@@ -37,7 +37,7 @@
 
   // Specifies if notification is recommended or required by administrator and
   // triggers the notification to be shown with the given body and title.
-  void SetUpdateNotificationState(ash::mojom::NotificationStyle style,
+  void SetUpdateNotificationState(ash::NotificationStyle style,
                                   const base::string16& notification_title,
                                   const base::string16& notification_body);
 
@@ -108,7 +108,7 @@
   bool flash_update_available_ = false;
 
   // Tells update notification style, for example required by administrator.
-  ash::mojom::NotificationStyle update_notification_style_;
+  ash::NotificationStyle update_notification_style_;
 
   // Update notification title to be overwritten.
   base::string16 update_notification_title_;
diff --git a/chrome/browser/ui/ash/window_resize_interactive_uitest.cc b/chrome/browser/ui/ash/window_resize_interactive_uitest.cc
index 9696aff..46cebb5 100644
--- a/chrome/browser/ui/ash/window_resize_interactive_uitest.cc
+++ b/chrome/browser/ui/ash/window_resize_interactive_uitest.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ash/public/cpp/window_properties.h"
 #include "ash/public/cpp/window_state_type.h"
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/task/post_task.h"
-#include "chrome/browser/ui/ash/ash_test_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/test/base/interactive_test_utils.h"
@@ -107,6 +107,41 @@
   DISALLOW_COPY_AND_ASSIGN(MultiPointProducer);
 };
 
+// Wait until the window's state changed to given snapped state.
+// The window should stay alive, so no need to observer destroying.
+class SnapWaiter : public aura::WindowObserver {
+ public:
+  SnapWaiter(aura::Window* window, ash::WindowStateType type)
+      : window_(window), type_(type) {
+    window->AddObserver(this);
+  }
+  ~SnapWaiter() override { window_->RemoveObserver(this); }
+
+  // aura::WindowObserver:
+  void OnWindowPropertyChanged(aura::Window* window,
+                               const void* key,
+                               intptr_t old) override {
+    if (key == ash::kWindowStateTypeKey && IsSnapped())
+      run_loop_.Quit();
+  }
+
+  void Wait() {
+    if (!IsSnapped())
+      run_loop_.Run();
+  }
+
+ private:
+  bool IsSnapped() const {
+    return window_->GetProperty(ash::kWindowStateTypeKey) == type_;
+  }
+
+  aura::Window* window_;
+  ash::WindowStateType type_;
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(SnapWaiter);
+};
+
 }  // namespace
 
 // Test window resize performance in clamshell mode.
@@ -156,8 +191,13 @@
 
 IN_PROC_BROWSER_TEST_P(WindowResizeTest, Single) {
   aura::Window* browser_window = browser()->window()->GetNativeWindow();
-  test::ActivateAndSnapWindow(browser_window,
-                              ash::WindowStateType::kLeftSnapped);
+  SnapWaiter snap_waiter(browser_window, ash::WindowStateType::kLeftSnapped);
+  ui_controls::SendKeyPress(browser_window, ui::VKEY_OEM_4,
+                            /*control=*/false,
+                            /*shift=*/false,
+                            /*alt=*/true,
+                            /*command=*/false);
+  snap_waiter.Wait();
 
   gfx::Rect bounds = browser_window->GetBoundsInScreen();
   gfx::Point start_point = gfx::Point(bounds.right_center());
@@ -181,8 +221,15 @@
 
 IN_PROC_BROWSER_TEST_P(WindowResizeTest, Multi) {
   aura::Window* browser_window = browser()->window()->GetNativeWindow();
-  test::ActivateAndSnapWindow(browser_window,
-                              ash::WindowStateType::kLeftSnapped);
+  {
+    SnapWaiter snap_waiter(browser_window, ash::WindowStateType::kLeftSnapped);
+    ui_controls::SendKeyPress(browser_window, ui::VKEY_OEM_4,
+                              /*control=*/false,
+                              /*shift=*/false,
+                              /*alt=*/true,
+                              /*command=*/false);
+    snap_waiter.Wait();
+  }
 
   Browser* browser2 = CreateBrowser(browser()->profile());
   if (use_ntp()) {
@@ -190,10 +237,18 @@
     ui_test_utils::NavigateToURL(browser2, ntp_url);
   }
 
-  aura::Window* browser_window2 = browser2->window()->GetNativeWindow();
   // Snap Right
-  test::ActivateAndSnapWindow(browser_window2,
-                              ash::WindowStateType::kRightSnapped);
+  {
+    aura::Window* browser_window2 = browser2->window()->GetNativeWindow();
+    SnapWaiter snap_waiter(browser_window2,
+                           ash::WindowStateType::kRightSnapped);
+    ui_controls::SendKeyPress(browser_window2, ui::VKEY_OEM_6,
+                              /*control=*/false,
+                              /*shift=*/false,
+                              /*alt=*/true,
+                              /*command=*/false);
+    snap_waiter.Wait();
+  }
 
   gfx::Rect bounds = browser_window->GetBoundsInScreen();
   gfx::Point start_point = gfx::Point(bounds.right_center());
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
index 586ccb6..e0d6cadb 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
@@ -28,6 +28,7 @@
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/autofill/manual_filling_controller_impl.h"
+#include "chrome/browser/password_manager/touch_to_fill_controller.h"
 
 using FillingSource = ManualFillingController::FillingSource;
 #endif
@@ -118,9 +119,9 @@
 
   if (just_created) {
 #if defined(OS_ANDROID)
-    if (popup_type == PopupType::kPasswords) {
-      ManualFillingController::GetOrCreate(web_contents_)
-          ->ShowTouchToFillSheet();
+    if (popup_type == PopupType::kPasswords &&
+        TouchToFillController::AllowedForWebContents(web_contents_)) {
+      TouchToFillController::GetOrCreate(web_contents_)->Show(suggestions);
     }
 
     ManualFillingController::GetOrCreate(web_contents_)
diff --git a/chrome/browser/ui/bookmarks/bookmark_browsertest.cc b/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
index 4995b84..d1cc57e8 100644
--- a/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
@@ -333,17 +333,17 @@
   const BookmarkNode* parent = bookmarks::GetParentForNewNodes(bookmark_model);
   // Add one bookmark with a unique URL, two other bookmarks with a shared URL,
   // and three more with another shared URL.
-  bookmark_model->AddURL(parent, parent->child_count(),
+  bookmark_model->AddURL(parent, parent->children().size(),
                          base::ASCIIToUTF16("title1"), GURL("http://a.com"));
-  bookmark_model->AddURL(parent, parent->child_count(),
+  bookmark_model->AddURL(parent, parent->children().size(),
                          base::ASCIIToUTF16("title2"), GURL("http://b.com"));
-  bookmark_model->AddURL(parent, parent->child_count(),
+  bookmark_model->AddURL(parent, parent->children().size(),
                          base::ASCIIToUTF16("title3"), GURL("http://b.com"));
-  bookmark_model->AddURL(parent, parent->child_count(),
+  bookmark_model->AddURL(parent, parent->children().size(),
                          base::ASCIIToUTF16("title4"), GURL("http://c.com"));
-  bookmark_model->AddURL(parent, parent->child_count(),
+  bookmark_model->AddURL(parent, parent->children().size(),
                          base::ASCIIToUTF16("title5"), GURL("http://c.com"));
-  bookmark_model->AddURL(parent, parent->child_count(),
+  bookmark_model->AddURL(parent, parent->children().size(),
                          base::ASCIIToUTF16("title6"), GURL("http://c.com"));
 }
 
@@ -362,15 +362,15 @@
   BookmarkModel* bookmark_model = WaitForBookmarkModel(browser()->profile());
   const BookmarkNode* parent = bookmarks::GetParentForNewNodes(bookmark_model);
   // Add two bookmarks with a non-empty title and three with an empty one.
-  bookmark_model->AddURL(parent, parent->child_count(),
+  bookmark_model->AddURL(parent, parent->children().size(),
                          base::ASCIIToUTF16("title1"), GURL("http://a.com"));
-  bookmark_model->AddURL(parent, parent->child_count(),
+  bookmark_model->AddURL(parent, parent->children().size(),
                          base::ASCIIToUTF16("title2"), GURL("http://b.com"));
-  bookmark_model->AddURL(parent, parent->child_count(), base::string16(),
+  bookmark_model->AddURL(parent, parent->children().size(), base::string16(),
                          GURL("http://c.com"));
-  bookmark_model->AddURL(parent, parent->child_count(), base::string16(),
+  bookmark_model->AddURL(parent, parent->children().size(), base::string16(),
                          GURL("http://d.com"));
-  bookmark_model->AddURL(parent, parent->child_count(), base::string16(),
+  bookmark_model->AddURL(parent, parent->children().size(), base::string16(),
                          GURL("http://e.com"));
 }
 
diff --git a/chrome/browser/ui/bookmarks/bookmark_context_menu_controller.cc b/chrome/browser/ui/bookmarks/bookmark_context_menu_controller.cc
index 1461452..1005956 100644
--- a/chrome/browser/ui/bookmarks/bookmark_context_menu_controller.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_context_menu_controller.cc
@@ -270,7 +270,7 @@
     case IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK: {
       base::RecordAction(UserMetricsAction("BookmarkBar_ContextMenu_Add"));
 
-      int index;
+      size_t index;
       const BookmarkNode* parent =
           bookmarks::GetParentForNewNodes(parent_, selection_, &index);
       GURL url;
@@ -290,7 +290,7 @@
       base::RecordAction(
           UserMetricsAction("BookmarkBar_ContextMenu_NewFolder"));
 
-      int index;
+      size_t index;
       const BookmarkNode* parent =
           bookmarks::GetParentForNewNodes(parent_, selection_, &index);
       BookmarkEditor::Show(
@@ -343,7 +343,7 @@
       break;
 
     case IDC_PASTE: {
-      int index;
+      size_t index;
       const BookmarkNode* paste_target =
           bookmarks::GetParentForNewNodes(parent_, selection_, &index);
       if (!paste_target)
diff --git a/chrome/browser/ui/bookmarks/bookmark_drag_drop.cc b/chrome/browser/ui/bookmarks/bookmark_drag_drop.cc
index 1718324..41ff1fe 100644
--- a/chrome/browser/ui/bookmarks/bookmark_drag_drop.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_drag_drop.cc
@@ -38,7 +38,7 @@
 int DropBookmarks(Profile* profile,
                   const BookmarkNodeData& data,
                   const BookmarkNode* parent_node,
-                  int index,
+                  size_t index,
                   bool copy) {
   BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile);
 #if !defined(OS_ANDROID)
@@ -58,7 +58,7 @@
         } else {
           model->Move(dragged_nodes[i], parent_node, index);
         }
-        index = parent_node->GetIndexOf(dragged_nodes[i]) + 1;
+        index = size_t{parent_node->GetIndexOf(dragged_nodes[i]) + 1};
       }
       return copy ? ui::DragDropTypes::DRAG_COPY : ui::DragDropTypes::DRAG_MOVE;
     }
diff --git a/chrome/browser/ui/bookmarks/bookmark_drag_drop.h b/chrome/browser/ui/bookmarks/bookmark_drag_drop.h
index 47b11c2..48b9b6c 100644
--- a/chrome/browser/ui/bookmarks/bookmark_drag_drop.h
+++ b/chrome/browser/ui/bookmarks/bookmark_drag_drop.h
@@ -66,7 +66,7 @@
 int DropBookmarks(Profile* profile,
                   const bookmarks::BookmarkNodeData& data,
                   const bookmarks::BookmarkNode* parent_node,
-                  int index,
+                  size_t index,
                   bool copy);
 
 }  // namespace chrome
diff --git a/chrome/browser/ui/bookmarks/bookmark_editor.cc b/chrome/browser/ui/bookmarks/bookmark_editor.cc
index 4ff4fcedf..9efb52c 100644
--- a/chrome/browser/ui/bookmarks/bookmark_editor.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_editor.cc
@@ -27,16 +27,18 @@
   // it was right-clicked, it might cause out of range exception when another
   // bookmark manager edits contents of the folder.
   // So we must check the range.
-  int child_count = parent->child_count();
-  int insert_index = (parent == details.parent_node && details.index >= 0 &&
-                      details.index <= child_count) ?
-                      details.index : child_count;
+  size_t child_count = parent->children().size();
+  size_t insert_index =
+      (parent == details.parent_node && details.index.has_value() &&
+       details.index.value() <= child_count)
+          ? details.index.value()
+          : child_count;
   if (details.type == BookmarkEditor::EditDetails::NEW_URL) {
     node = model->AddURL(parent, insert_index, new_title, new_url);
   } else if (details.type == BookmarkEditor::EditDetails::NEW_FOLDER) {
     node = model->AddFolder(parent, insert_index, new_title);
     for (size_t i = 0; i < details.urls.size(); ++i) {
-      model->AddURL(node, node->child_count(), details.urls[i].second,
+      model->AddURL(node, node->children().size(), details.urls[i].second,
                     details.urls[i].first);
     }
     model->SetDateFolderModified(parent, base::Time::Now());
@@ -50,9 +52,7 @@
 
 }  // namespace
 
-BookmarkEditor::EditDetails::EditDetails(Type node_type)
-    : type(node_type), existing_node(NULL), parent_node(NULL), index(-1) {
-}
+BookmarkEditor::EditDetails::EditDetails(Type node_type) : type(node_type) {}
 
 BookmarkNode::Type BookmarkEditor::EditDetails::GetNodeType() const {
   BookmarkNode::Type node_type = BookmarkNode::URL;
@@ -104,7 +104,7 @@
 
 BookmarkEditor::EditDetails BookmarkEditor::EditDetails::AddNodeInFolder(
     const BookmarkNode* parent_node,
-    int index,
+    size_t index,
     const GURL& url,
     const base::string16& title) {
   EditDetails details(NEW_URL);
@@ -117,7 +117,7 @@
 
 BookmarkEditor::EditDetails BookmarkEditor::EditDetails::AddFolder(
     const BookmarkNode* parent_node,
-    int index) {
+    size_t index) {
   EditDetails details(NEW_FOLDER);
   details.parent_node = parent_node;
   details.index = index;
@@ -166,7 +166,7 @@
   DCHECK(node);
 
   if (new_parent != node->parent())
-    model->Move(node, new_parent, new_parent->child_count());
+    model->Move(node, new_parent, new_parent->children().size());
   if (node->is_url())
     model->SetURL(node, new_url);
   model->SetTitle(node, new_title);
diff --git a/chrome/browser/ui/bookmarks/bookmark_editor.h b/chrome/browser/ui/bookmarks/bookmark_editor.h
index ef662ab..3d0ad90 100644
--- a/chrome/browser/ui/bookmarks/bookmark_editor.h
+++ b/chrome/browser/ui/bookmarks/bookmark_editor.h
@@ -48,14 +48,14 @@
     // a given parent node with a specified index.
     static EditDetails AddNodeInFolder(
         const bookmarks::BookmarkNode* parent_node,
-        int index,
+        size_t index,
         const GURL& url,
         const base::string16& title);
 
     // Returns an EditDetails instance for the user adding a folder within a
     // given parent node with a specified index.
     static EditDetails AddFolder(const bookmarks::BookmarkNode* parent_node,
-                                 int index);
+                                 size_t index);
 
     enum Type {
       // The user is editing an existing node in the model. The node the user
@@ -79,15 +79,15 @@
     const Type type;
 
     // If type == EXISTING_NODE this gives the existing node.
-    const bookmarks::BookmarkNode* existing_node;
+    const bookmarks::BookmarkNode* existing_node = nullptr;
 
     // If type == NEW_URL or type == NEW_FOLDER this gives the initial parent
     // node to place the new node in.
-    const bookmarks::BookmarkNode* parent_node;
+    const bookmarks::BookmarkNode* parent_node = nullptr;
 
     // If type == NEW_URL or type == NEW_FOLDER this gives the index to insert
     // the new node at.
-    int index;
+    base::Optional<size_t> index;
 
     // If type == NEW_URL this gives the URL/title.
     GURL url;
diff --git a/chrome/browser/ui/bookmarks/bookmark_editor_unittest.cc b/chrome/browser/ui/bookmarks/bookmark_editor_unittest.cc
index 8eef491..c513ac55 100644
--- a/chrome/browser/ui/bookmarks/bookmark_editor_unittest.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_editor_unittest.cc
@@ -34,7 +34,7 @@
   }
   {
     BookmarkEditor::EditDetails detail(
-        BookmarkEditor::EditDetails::AddFolder(bookmarkbar, -1));
+        BookmarkEditor::EditDetails::AddFolder(bookmarkbar, size_t{-1}));
     BookmarkEditor::ApplyEditsWithNoFolderChange(model.get(),
                                                  bookmarkbar,
                                                  detail,
diff --git a/chrome/browser/ui/bookmarks/bookmark_tab_helper.cc b/chrome/browser/ui/bookmarks/bookmark_tab_helper.cc
index f6ef61a..d73183d 100644
--- a/chrome/browser/ui/bookmarks/bookmark_tab_helper.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_tab_helper.cc
@@ -118,14 +118,14 @@
 
 void BookmarkTabHelper::BookmarkNodeAdded(BookmarkModel* model,
                                           const BookmarkNode* parent,
-                                          int index) {
+                                          size_t index) {
   UpdateStarredStateForCurrentURL();
 }
 
 void BookmarkTabHelper::BookmarkNodeRemoved(
     BookmarkModel* model,
     const BookmarkNode* parent,
-    int old_index,
+    size_t old_index,
     const BookmarkNode* node,
     const std::set<GURL>& removed_urls) {
   UpdateStarredStateForCurrentURL();
diff --git a/chrome/browser/ui/bookmarks/bookmark_tab_helper.h b/chrome/browser/ui/bookmarks/bookmark_tab_helper.h
index 1dca47d99..2a671e7 100644
--- a/chrome/browser/ui/bookmarks/bookmark_tab_helper.h
+++ b/chrome/browser/ui/bookmarks/bookmark_tab_helper.h
@@ -72,10 +72,10 @@
                            bool ids_reassigned) override;
   void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* parent,
-                         int index) override;
+                         size_t index) override;
   void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
                            const bookmarks::BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const bookmarks::BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override;
   void BookmarkAllUserNodesRemoved(bookmarks::BookmarkModel* model,
diff --git a/chrome/browser/ui/bookmarks/bookmark_utils.cc b/chrome/browser/ui/bookmarks/bookmark_utils.cc
index 18ae5ae..15c8b36 100644
--- a/chrome/browser/ui/bookmarks/bookmark_utils.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_utils.cc
@@ -222,7 +222,7 @@
                              const ui::DropTargetEvent& event,
                              const bookmarks::BookmarkNodeData& data,
                              const BookmarkNode* parent,
-                             int index) {
+                             size_t index) {
   const base::FilePath& profile_path = profile->GetPath();
 
   if (data.IsFromProfilePath(profile_path) && data.size() > 1)
@@ -256,7 +256,7 @@
 bool IsValidBookmarkDropLocation(Profile* profile,
                                  const bookmarks::BookmarkNodeData& data,
                                  const BookmarkNode* drop_parent,
-                                 int index) {
+                                 size_t index) {
   if (!drop_parent->is_folder()) {
     NOTREACHED();
     return false;
@@ -278,7 +278,8 @@
       const BookmarkNode* node = nodes[i];
       int node_index = (drop_parent == node->parent()) ?
           drop_parent->GetIndexOf(nodes[i]) : -1;
-      if (node_index != -1 && (index == node_index || index == node_index + 1))
+      if (node_index != -1 &&
+          (index == size_t{node_index} || index == size_t{node_index} + 1))
         return false;
 
       // drop_parent can't accept a child that is an ancestor.
diff --git a/chrome/browser/ui/bookmarks/bookmark_utils.h b/chrome/browser/ui/bookmarks/bookmark_utils.h
index 84b222d..f24aa30f 100644
--- a/chrome/browser/ui/bookmarks/bookmark_utils.h
+++ b/chrome/browser/ui/bookmarks/bookmark_utils.h
@@ -88,7 +88,7 @@
                              const ui::DropTargetEvent& event,
                              const bookmarks::BookmarkNodeData& data,
                              const bookmarks::BookmarkNode* parent,
-                             int index);
+                             size_t index);
 
 // Returns true if the bookmark data can be dropped on |drop_parent| at
 // |index|. A drop from a separate profile is always allowed, where as
@@ -98,7 +98,7 @@
 bool IsValidBookmarkDropLocation(Profile* profile,
                                  const bookmarks::BookmarkNodeData& data,
                                  const bookmarks::BookmarkNode* drop_parent,
-                                 int index);
+                                 size_t index);
 
 #if defined(TOOLKIT_VIEWS)
 // |text_color| is the color of associated text and is used to derive the icon's
diff --git a/chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc b/chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc
index 7b7d099..aec6d0d 100644
--- a/chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc
@@ -186,7 +186,7 @@
 
   const BookmarkNode* parent = GetParentForNewNodes(model);
   BookmarkEditor::EditDetails details =
-      BookmarkEditor::EditDetails::AddFolder(parent, parent->child_count());
+      BookmarkEditor::EditDetails::AddFolder(parent, parent->children().size());
   GetURLsForOpenTabs(browser, &(details.urls));
   DCHECK(!details.urls.empty());
 
diff --git a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.cc b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.cc
index 5d25f8e0..369c1c2 100644
--- a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.cc
+++ b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.cc
@@ -145,21 +145,19 @@
 void RecentlyUsedFoldersComboModel::BookmarkNodeMoved(
     BookmarkModel* model,
     const BookmarkNode* old_parent,
-    int old_index,
+    size_t old_index,
     const BookmarkNode* new_parent,
-    int new_index) {
-}
+    size_t new_index) {}
 
 void RecentlyUsedFoldersComboModel::BookmarkNodeAdded(
     BookmarkModel* model,
     const BookmarkNode* parent,
-    int index) {
-}
+    size_t index) {}
 
 void RecentlyUsedFoldersComboModel::OnWillRemoveBookmarks(
     BookmarkModel* model,
     const BookmarkNode* parent,
-    int old_index,
+    size_t old_index,
     const BookmarkNode* node) {
   // Changing is rare enough that we don't attempt to readjust the contents.
   // Update |items_| so we aren't left pointing to a deleted node.
@@ -181,7 +179,7 @@
 void RecentlyUsedFoldersComboModel::BookmarkNodeRemoved(
     BookmarkModel* model,
     const BookmarkNode* parent,
-    int old_index,
+    size_t old_index,
     const BookmarkNode* node,
     const std::set<GURL>& removed_urls) {}
 
@@ -230,7 +228,7 @@
   const BookmarkNode* new_parent = GetNodeAt(selected_index);
   if (new_parent != node->parent()) {
     base::RecordAction(base::UserMetricsAction("BookmarkBubble_ChangeParent"));
-    bookmark_model_->Move(node, new_parent, new_parent->child_count());
+    bookmark_model_->Move(node, new_parent, new_parent->children().size());
   }
 }
 
diff --git a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h
index 9de5257b..73b769ef 100644
--- a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h
+++ b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h
@@ -43,19 +43,19 @@
   void BookmarkModelBeingDeleted(bookmarks::BookmarkModel* model) override;
   void BookmarkNodeMoved(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* old_parent,
-                         int old_index,
+                         size_t old_index,
                          const bookmarks::BookmarkNode* new_parent,
-                         int new_index) override;
+                         size_t new_index) override;
   void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* parent,
-                         int index) override;
+                         size_t index) override;
   void OnWillRemoveBookmarks(bookmarks::BookmarkModel* model,
                              const bookmarks::BookmarkNode* parent,
-                             int old_index,
+                             size_t old_index,
                              const bookmarks::BookmarkNode* node) override;
   void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
                            const bookmarks::BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const bookmarks::BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override;
   void BookmarkNodeChanged(bookmarks::BookmarkModel* model,
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 91a770f..e9165bee 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1026,9 +1026,12 @@
                       replace->index);
       break;
     }
-    case TabStripModelChange::kGroupChanged:
-      // TODO(crbug.com/930991): Save Tab Group data to SessionService.
+    case TabStripModelChange::kGroupChanged: {
+      auto* group_change = change.GetGroupChange();
+      OnTabGroupChanged(group_change->index, group_change->old_group,
+                        group_change->new_group);
       break;
+    }
     case TabStripModelChange::kSelectionOnly:
       break;
   }
@@ -1794,7 +1797,7 @@
 bool Browser::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   Profile* profile = Profile::FromBrowserContext(
       content::WebContents::FromRenderFrameHost(render_frame_host)
           ->GetBrowserContext());
@@ -1805,8 +1808,9 @@
                                    extension);
 }
 
-std::string Browser::GetDefaultMediaDeviceID(content::WebContents* web_contents,
-                                             blink::MediaStreamType type) {
+std::string Browser::GetDefaultMediaDeviceID(
+    content::WebContents* web_contents,
+    blink::mojom::MediaStreamType type) {
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
   return MediaCaptureDevicesDispatcher::GetInstance()
@@ -2260,6 +2264,24 @@
   }
 }
 
+void Browser::OnTabGroupChanged(int index,
+                                base::Optional<TabGroupId> old_group,
+                                base::Optional<TabGroupId> new_group) {
+  content::WebContents* const web_contents =
+      tab_strip_model_->GetWebContentsAt(index);
+  SessionService* const session_service =
+      SessionServiceFactory::GetForProfile(profile_);
+  if (session_service) {
+    SessionTabHelper* const session_tab_helper =
+        SessionTabHelper::FromWebContents(web_contents);
+    const base::Optional<base::Token> raw_id =
+        new_group.has_value() ? base::make_optional(new_group.value().token())
+                              : base::nullopt;
+    session_service->SetTabGroup(session_id(), session_tab_helper->session_id(),
+                                 raw_id);
+  }
+}
+
 void Browser::OnDevToolsAvailabilityChanged() {
   using DTPH = policy::DeveloperToolsPolicyHandler;
   // We close all windows as a safety measure, even for
@@ -2428,6 +2450,15 @@
         session_service->SetPinnedState(session_id(),
                                         session_tab_helper->session_id(),
                                         tab_strip_model_->IsTabPinned(i));
+
+        base::Optional<TabGroupId> group_id =
+            tab_strip_model_->GetTabGroupForTab(i);
+        base::Optional<base::Token> raw_group_id =
+            group_id.has_value()
+                ? base::Optional<base::Token>(group_id.value().token())
+                : base::nullopt;
+        session_service->SetTabGroup(
+            session_id(), session_tab_helper->session_id(), raw_group_id);
       }
     }
   }
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 1633ee2..5483fef1 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -762,9 +762,10 @@
       content::MediaResponseCallback callback) override;
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type) override;
-  std::string GetDefaultMediaDeviceID(content::WebContents* web_contents,
-                                      blink::MediaStreamType type) override;
+                                  blink::mojom::MediaStreamType type) override;
+  std::string GetDefaultMediaDeviceID(
+      content::WebContents* web_contents,
+      blink::mojom::MediaStreamType type) override;
   void RequestPpapiBrokerPermission(
       content::WebContents* web_contents,
       const GURL& url,
@@ -835,6 +836,9 @@
   void OnTabReplacedAt(content::WebContents* old_contents,
                        content::WebContents* new_contents,
                        int index);
+  void OnTabGroupChanged(int index,
+                         base::Optional<TabGroupId> old_group,
+                         base::Optional<TabGroupId> new_group);
 
   // Handle changes to kDevToolsAvailability preference.
   void OnDevToolsAvailabilityChanged();
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index 946ccf10..ed95d35 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -2531,23 +2531,23 @@
   ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab"));
   WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  auto GetDeviceID = [web_contents](blink::MediaStreamType type) {
+  auto GetDeviceID = [web_contents](blink::mojom::MediaStreamType type) {
     return web_contents->GetDelegate()->GetDefaultMediaDeviceID(web_contents,
                                                                 type);
   };
   EXPECT_EQ(kDefaultAudioCapture1,
-            GetDeviceID(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
+            GetDeviceID(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
   EXPECT_EQ(kDefaultVideoCapture1,
-            GetDeviceID(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+            GetDeviceID(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
 
   const std::string kDefaultAudioCapture2 = "test_default_audio_capture_2";
   const std::string kDefaultVideoCapture2 = "test_default_video_capture_2";
   SetString(prefs::kDefaultAudioCaptureDevice, kDefaultAudioCapture2);
   SetString(prefs::kDefaultVideoCaptureDevice, kDefaultVideoCapture2);
   EXPECT_EQ(kDefaultAudioCapture2,
-            GetDeviceID(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
+            GetDeviceID(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE));
   EXPECT_EQ(kDefaultVideoCapture2,
-            GetDeviceID(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
+            GetDeviceID(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
 }
 
 namespace {
diff --git a/chrome/browser/ui/browser_list.cc b/chrome/browser/ui/browser_list.cc
index ed1a235..600ae7f 100644
--- a/chrome/browser/ui/browser_list.cc
+++ b/chrome/browser/ui/browser_list.cc
@@ -9,6 +9,7 @@
 #include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/user_metrics.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -90,6 +91,16 @@
 
   if (browser->window()->IsActive())
     SetLastActive(browser);
+
+  if (browser->profile()->IsGuestSession()) {
+    base::UmaHistogramCounts100(
+        "Browser.WindowCount.Guest",
+        GetIncognitoSessionsActiveForProfile(browser->profile()));
+  } else if (browser->profile()->IsIncognitoProfile()) {
+    base::UmaHistogramCounts100(
+        "Browser.WindowCount.Incognito",
+        GetIncognitoSessionsActiveForProfile(browser->profile()));
+  }
 }
 
 // static
diff --git a/chrome/browser/ui/browser_live_tab_context.cc b/chrome/browser/ui/browser_live_tab_context.cc
index 81d6154..a19d07d 100644
--- a/chrome/browser/ui/browser_live_tab_context.cc
+++ b/chrome/browser/ui/browser_live_tab_context.cc
@@ -90,8 +90,8 @@
 
   WebContents* web_contents = chrome::AddRestoredTab(
       browser_, navigations, tab_index, selected_navigation, extension_app_id,
-      select, pin, from_last_session, base::TimeTicks(), storage_namespace,
-      user_agent_override, false /* from_session_restore */);
+      base::nullopt, select, pin, from_last_session, base::TimeTicks(),
+      storage_namespace, user_agent_override, false /* from_session_restore */);
 
 #if BUILDFLAG(ENABLE_SESSION_SERVICE)
   // The focused tab will be loaded by Browser, and TabLoader will load the
@@ -104,7 +104,7 @@
   }
   std::vector<TabLoader::RestoredTab> restored_tabs;
   restored_tabs.emplace_back(web_contents, select, !extension_app_id.empty(),
-                             pin);
+                             pin, base::nullopt);
   TabLoader::RestoreTabs(restored_tabs, base::TimeTicks::Now());
 #else   // BUILDFLAG(ENABLE_SESSION_SERVICE)
   // Load the tab manually if there is no TabLoader.
diff --git a/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc b/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
index b276528..4b0a321 100644
--- a/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
@@ -17,6 +17,8 @@
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/web_applications/system_web_app_manager.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -38,6 +40,10 @@
 
 // This test verifies that the settings page is opened in a new browser window.
 IN_PROC_BROWSER_TEST_F(BrowserNavigatorTestChromeOS, NavigateToSettings) {
+  // Install the Settings App.
+  web_app::WebAppProvider::Get(browser()->profile())
+      ->system_web_app_manager()
+      .InstallSystemAppsForTesting();
   GURL old_url = browser()->tab_strip_model()->GetActiveWebContents()->GetURL();
   {
     content::WindowedNotificationObserver observer(
diff --git a/chrome/browser/ui/browser_tabrestore.cc b/chrome/browser/ui/browser_tabrestore.cc
index 055965a..2b5f55bc 100644
--- a/chrome/browser/ui/browser_tabrestore.cc
+++ b/chrome/browser/ui/browser_tabrestore.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/tab_contents/tab_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_group_id.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/web_contents_sizer.h"
 #include "components/sessions/content/content_serialized_navigation_builder.h"
@@ -100,6 +101,7 @@
     int tab_index,
     int selected_navigation,
     const std::string& extension_app_id,
+    base::Optional<base::Token> raw_group_id,
     bool select,
     bool pin,
     bool from_last_session,
@@ -118,9 +120,16 @@
         tab_index, browser->tab_strip_model()->IndexOfFirstNonPinnedTab());
     add_types |= TabStripModel::ADD_PINNED;
   }
+
   WebContents* raw_web_contents = web_contents.get();
-  browser->tab_strip_model()->InsertWebContentsAt(
+  const int actual_index = browser->tab_strip_model()->InsertWebContentsAt(
       tab_index, std::move(web_contents), add_types);
+
+  if (raw_group_id.has_value()) {
+    auto group_id = TabGroupId::FromRawToken(raw_group_id.value());
+    browser->tab_strip_model()->AddToGroupForRestore({actual_index}, group_id);
+  }
+
   if (select) {
     if (!browser->window()->IsMinimized())
       browser->window()->Activate();
diff --git a/chrome/browser/ui/browser_tabrestore.h b/chrome/browser/ui/browser_tabrestore.h
index 451ead1..78fdbb6 100644
--- a/chrome/browser/ui/browser_tabrestore.h
+++ b/chrome/browser/ui/browser_tabrestore.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "base/token.h"
 #include "components/sessions/core/session_types.h"
 
 class Browser;
@@ -28,11 +29,12 @@
 // |tab_index| gives the index to insert the tab at. |selected_navigation| is
 // the index of the SerializedNavigationEntry in |navigations| to select. If
 // |extension_app_id| is non-empty the tab is an app tab and |extension_app_id|
-// is the id of the extension. If |pin| is true and |tab_index|/ is the last
-// pinned tab, then the newly created tab is pinned. If |from_last_session| is
-// true, |navigations| are from the previous session. |user_agent_override|
-// contains the string being used as the user agent for all of the tab's
-// navigations when the regular user agent is overridden. If
+// is the id of the extension. If |raw_group_id| has a value, it specifies the
+// token corresponding to the tab's group. If |pin| is true and |tab_index|/ is
+// the last pinned tab, then the newly created tab is pinned. If
+// |from_last_session| is true, |navigations| are from the previous session.
+// |user_agent_override| contains the string being used as the user agent for
+// all of the tab's navigations when the regular user agent is overridden. If
 // |from_session_restore| is true, the restored tab is created by session
 // restore. |last_active_time| is the value to use to indicate the last time the
 // WebContents was made active, if this is left default initialized then the
@@ -43,6 +45,7 @@
     int tab_index,
     int selected_navigation,
     const std::string& extension_app_id,
+    base::Optional<base::Token> raw_group_id,
     bool select,
     bool pin,
     bool from_last_session,
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h
index 91defb4..4f62996 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h
@@ -49,15 +49,15 @@
   void BookmarkModelBeingDeleted(bookmarks::BookmarkModel* model) override;
   void BookmarkNodeMoved(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* old_parent,
-                         int old_index,
+                         size_t old_index,
                          const bookmarks::BookmarkNode* new_parent,
-                         int new_index) override;
+                         size_t new_index) override;
   void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* parent,
-                         int index) override;
+                         size_t index) override;
   void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
                            const bookmarks::BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const bookmarks::BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override;
   void BookmarkAllUserNodesRemoved(bookmarks::BookmarkModel* model,
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.mm
index 9055baf..d871cc3 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.mm
@@ -151,22 +151,22 @@
 
 void BookmarkMenuBridge::BookmarkNodeMoved(BookmarkModel* model,
                                            const BookmarkNode* old_parent,
-                                           int old_index,
+                                           size_t old_index,
                                            const BookmarkNode* new_parent,
-                                           int new_index) {
+                                           size_t new_index) {
   InvalidateMenu();
 }
 
 void BookmarkMenuBridge::BookmarkNodeAdded(BookmarkModel* model,
                                            const BookmarkNode* parent,
-                                           int index) {
+                                           size_t index) {
   InvalidateMenu();
 }
 
 void BookmarkMenuBridge::BookmarkNodeRemoved(
     BookmarkModel* model,
     const BookmarkNode* parent,
-    int old_index,
+    size_t old_index,
     const BookmarkNode* node,
     const std::set<GURL>& removed_urls) {
   InvalidateMenu();
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
index f6e95a30..fae835f7 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -1205,13 +1205,13 @@
 }
 
 void ContentSettingMediaStreamBubbleModel::UpdateDefaultDeviceForType(
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const std::string& device) {
   PrefService* prefs = GetProfile()->GetPrefs();
-  if (type == blink::MEDIA_DEVICE_AUDIO_CAPTURE) {
+  if (type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
     prefs->SetString(prefs::kDefaultAudioCaptureDevice, device);
   } else {
-    DCHECK_EQ(blink::MEDIA_DEVICE_VIDEO_CAPTURE, type);
+    DCHECK_EQ(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, type);
     prefs->SetString(prefs::kDefaultVideoCaptureDevice, device);
   }
 }
@@ -1250,7 +1250,8 @@
       mic_menu.default_device = GetMediaDeviceById(preferred_mic, microphones);
       mic_menu.selected_device = mic_menu.default_device;
     }
-    add_media_menu(blink::MEDIA_DEVICE_AUDIO_CAPTURE, mic_menu);
+    add_media_menu(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                   mic_menu);
   }
 
   if (CameraAccessed()) {
@@ -1275,7 +1276,8 @@
           GetMediaDeviceById(preferred_camera, cameras);
       camera_menu.selected_device = camera_menu.default_device;
     }
-    add_media_menu(blink::MEDIA_DEVICE_VIDEO_CAPTURE, camera_menu);
+    add_media_menu(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+                   camera_menu);
   }
 }
 
@@ -1294,15 +1296,15 @@
 }
 
 void ContentSettingMediaStreamBubbleModel::OnMediaMenuClicked(
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const std::string& selected_device_id) {
-  DCHECK(type == blink::MEDIA_DEVICE_AUDIO_CAPTURE ||
-         type == blink::MEDIA_DEVICE_VIDEO_CAPTURE);
+  DCHECK(type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE ||
+         type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
   DCHECK_EQ(1U, bubble_content().media_menus.count(type));
   MediaCaptureDevicesDispatcher* dispatcher =
       MediaCaptureDevicesDispatcher::GetInstance();
   const blink::MediaStreamDevices& devices =
-      (type == blink::MEDIA_DEVICE_AUDIO_CAPTURE)
+      (type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE)
           ? dispatcher->GetAudioCaptureDevices()
           : dispatcher->GetVideoCaptureDevices();
   set_selected_device(GetMediaDeviceById(selected_device_id, devices));
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.h b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
index 0385d2b..5de31fb 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.h
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
@@ -126,7 +126,7 @@
     blink::MediaStreamDevice selected_device;
     bool disabled;
   };
-  typedef std::map<blink::MediaStreamType, MediaMenu> MediaMenuMap;
+  typedef std::map<blink::mojom::MediaStreamType, MediaMenu> MediaMenuMap;
 
   enum class ManageTextStyle {
     // No Manage button or checkbox is displayed.
@@ -180,7 +180,7 @@
   virtual void OnManageButtonClicked() {}
   virtual void OnManageCheckboxChecked(bool is_checked) {}
   virtual void OnLearnMoreClicked() {}
-  virtual void OnMediaMenuClicked(blink::MediaStreamType type,
+  virtual void OnMediaMenuClicked(blink::mojom::MediaStreamType type,
                                   const std::string& selected_device_id) {}
 
   // Called by the view code when the bubble is closed
@@ -249,7 +249,8 @@
   void set_manage_text_style(ManageTextStyle manage_text_style) {
     bubble_content_.manage_text_style = manage_text_style;
   }
-  void add_media_menu(blink::MediaStreamType type, const MediaMenu& menu) {
+  void add_media_menu(blink::mojom::MediaStreamType type,
+                      const MediaMenu& menu) {
     bubble_content_.media_menus[type] = menu;
   }
   void set_selected_device(const blink::MediaStreamDevice& device) {
@@ -367,11 +368,11 @@
 
   // Updates the camera and microphone default device with the passed |type|
   // and device.
-  void UpdateDefaultDeviceForType(blink::MediaStreamType type,
+  void UpdateDefaultDeviceForType(blink::mojom::MediaStreamType type,
                                   const std::string& device);
 
   // ContentSettingBubbleModel implementation.
-  void OnMediaMenuClicked(blink::MediaStreamType type,
+  void OnMediaMenuClicked(blink::mojom::MediaStreamType type,
                           const std::string& selected_device) override;
 
   // The content settings that are associated with the individual radio
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
index 56b79d13..738b00a 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
@@ -364,11 +364,14 @@
 
   blink::MediaStreamDevices audio_devices;
   blink::MediaStreamDevice fake_audio_device1(
-      blink::MEDIA_DEVICE_AUDIO_CAPTURE, "fake_dev1", "Fake Audio Device 1");
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, "fake_dev1",
+      "Fake Audio Device 1");
   blink::MediaStreamDevice fake_audio_device2(
-      blink::MEDIA_DEVICE_AUDIO_CAPTURE, "fake_dev2", "Fake Audio Device 2");
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, "fake_dev2",
+      "Fake Audio Device 2");
   blink::MediaStreamDevice fake_audio_device3(
-      blink::MEDIA_DEVICE_AUDIO_CAPTURE, "fake_dev3", "Fake Audio Device 3");
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, "fake_dev3",
+      "Fake Audio Device 3");
   audio_devices.push_back(fake_audio_device1);
   audio_devices.push_back(fake_audio_device2);
   audio_devices.push_back(fake_audio_device3);
@@ -396,7 +399,7 @@
     EXPECT_TRUE(bubble_content.custom_link.empty());
 
     EXPECT_EQ(1U, bubble_content.media_menus.size());
-    EXPECT_EQ(blink::MEDIA_DEVICE_AUDIO_CAPTURE,
+    EXPECT_EQ(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
               bubble_content.media_menus.begin()->first);
     EXPECT_FALSE(bubble_content.media_menus.begin()->second.disabled);
     // The first audio device should be selected by default.
@@ -405,7 +408,8 @@
 
     // Select a different (the second) device.
     content_setting_bubble_model->OnMediaMenuClicked(
-        blink::MEDIA_DEVICE_AUDIO_CAPTURE, fake_audio_device2.id);
+        blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+        fake_audio_device2.id);
     content_setting_bubble_model->CommitChanges();
   }
   {
@@ -416,7 +420,7 @@
     std::unique_ptr<FakeOwner> owner = FakeOwner::Create(
         *content_setting_bubble_model, bubble_content.radio_group.default_item);
     EXPECT_EQ(1U, bubble_content.media_menus.size());
-    EXPECT_EQ(blink::MEDIA_DEVICE_AUDIO_CAPTURE,
+    EXPECT_EQ(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
               bubble_content.media_menus.begin()->first);
     EXPECT_FALSE(bubble_content.media_menus.begin()->second.disabled);
     // The second audio device should be selected.
@@ -457,7 +461,7 @@
     EXPECT_TRUE(bubble_content.custom_link.empty());
 
     EXPECT_EQ(1U, bubble_content.media_menus.size());
-    EXPECT_EQ(blink::MEDIA_DEVICE_AUDIO_CAPTURE,
+    EXPECT_EQ(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
               bubble_content.media_menus.begin()->first);
     EXPECT_FALSE(bubble_content.media_menus.begin()->second.disabled);
     EXPECT_TRUE(fake_audio_device2.IsSameDevice(
@@ -465,7 +469,8 @@
 
     // Select a different different device.
     content_setting_bubble_model->OnMediaMenuClicked(
-        blink::MEDIA_DEVICE_AUDIO_CAPTURE, fake_audio_device3.id);
+        blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+        fake_audio_device3.id);
     content_setting_bubble_model->CommitChanges();
   }
 
@@ -506,7 +511,7 @@
 
     // Though the audio menu setting should have persisted.
     EXPECT_EQ(1U, bubble_content.media_menus.size());
-    EXPECT_EQ(blink::MEDIA_DEVICE_AUDIO_CAPTURE,
+    EXPECT_EQ(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
               bubble_content.media_menus.begin()->first);
     EXPECT_FALSE(bubble_content.media_menus.begin()->second.disabled);
     EXPECT_TRUE(fake_audio_device3.IsSameDevice(
@@ -552,7 +557,7 @@
   EXPECT_FALSE(bubble_content.custom_link_enabled);
   EXPECT_FALSE(bubble_content.manage_text.empty());
   EXPECT_EQ(1U, bubble_content.media_menus.size());
-  EXPECT_EQ(blink::MEDIA_DEVICE_AUDIO_CAPTURE,
+  EXPECT_EQ(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
             bubble_content.media_menus.begin()->first);
 
   // Change the microphone access.
@@ -583,7 +588,7 @@
   EXPECT_FALSE(new_bubble_content.custom_link_enabled);
   EXPECT_FALSE(new_bubble_content.manage_text.empty());
   EXPECT_EQ(1U, new_bubble_content.media_menus.size());
-  EXPECT_EQ(blink::MEDIA_DEVICE_AUDIO_CAPTURE,
+  EXPECT_EQ(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
             new_bubble_content.media_menus.begin()->first);
 }
 
@@ -625,7 +630,7 @@
   EXPECT_FALSE(bubble_content.custom_link_enabled);
   EXPECT_FALSE(bubble_content.manage_text.empty());
   EXPECT_EQ(1U, bubble_content.media_menus.size());
-  EXPECT_EQ(blink::MEDIA_DEVICE_VIDEO_CAPTURE,
+  EXPECT_EQ(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
             bubble_content.media_menus.begin()->first);
 
   // Change the camera access.
@@ -657,7 +662,7 @@
   EXPECT_FALSE(new_bubble_content.custom_link_enabled);
   EXPECT_FALSE(new_bubble_content.manage_text.empty());
   EXPECT_EQ(1U, new_bubble_content.media_menus.size());
-  EXPECT_EQ(blink::MEDIA_DEVICE_VIDEO_CAPTURE,
+  EXPECT_EQ(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
             new_bubble_content.media_menus.begin()->first);
 }
 
@@ -698,7 +703,7 @@
             l10n_util::GetStringUTF16(IDS_ALLOWED_MEDIASTREAM_MIC_BLOCK));
   EXPECT_EQ(0, bubble_content.radio_group.default_item);
   EXPECT_EQ(1U, bubble_content.media_menus.size());
-  EXPECT_EQ(blink::MEDIA_DEVICE_AUDIO_CAPTURE,
+  EXPECT_EQ(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
             bubble_content.media_menus.begin()->first);
 
   // Then add camera access.
diff --git a/chrome/browser/ui/content_settings/framebust_block_browsertest.cc b/chrome/browser/ui/content_settings/framebust_block_browsertest.cc
index 9f18008..a50e70f 100644
--- a/chrome/browser/ui/content_settings/framebust_block_browsertest.cc
+++ b/chrome/browser/ui/content_settings/framebust_block_browsertest.cc
@@ -37,6 +37,11 @@
 #include "ui/events/event_constants.h"
 #include "url/gurl.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/web_applications/system_web_app_manager.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#endif
+
 namespace {
 
 const int kAllowRadioButtonIndex = 0;
@@ -206,6 +211,12 @@
 }
 
 IN_PROC_BROWSER_TEST_F(FramebustBlockBrowserTest, ManageButtonClicked) {
+#if defined(OS_CHROMEOS)
+  web_app::WebAppProvider::Get(browser()->profile())
+      ->system_web_app_manager()
+      .InstallSystemAppsForTesting();
+#endif
+
   const GURL url = embedded_test_server()->GetURL("/iframe.html");
   ui_test_utils::NavigateToURL(browser(), url);
 
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
index b154ee4..e9b97ff6 100644
--- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/callback_forward.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
+#include "base/files/file_path.h"
 #include "base/json/json_reader.h"
 #include "base/run_loop.h"
 #include "base/strings/string_split.h"
@@ -18,6 +19,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/badging/badge_manager.h"
@@ -80,6 +82,8 @@
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/request_handler_util.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/manifest/manifest.h"
 #include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/gfx/geometry/rect.h"
@@ -917,6 +921,69 @@
             app_browser->GetWindowTitleForCurrentTab(false));
 }
 
+class HostedAppFileHandlingTest : public HostedAppTest {
+ public:
+  HostedAppFileHandlingTest() {
+    scoped_feature_list_.InitWithFeatures(
+        {blink::features::kNativeFileSystemAPI,
+         blink::features::kFileHandlingAPI},
+        {});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_P(HostedAppFileHandlingTest, PWAsCanViewLaunchParams) {
+  ASSERT_TRUE(https_server()->Start());
+
+  GURL url = GetSecureAppURL();
+
+  WebApplicationInfo web_app_info;
+  web_app_info.app_url = url;
+  web_app_info.scope = url.GetWithoutFilename();
+  web_app_info.title = base::ASCIIToUTF16("A Hosted App");
+  web_app_info.file_handler = blink::Manifest::FileHandler();
+  web_app_info.file_handler->action =
+      GURL(https_server()->GetURL("app.com", "/ssl/blank_page.html"));
+
+  {
+    std::vector<blink::Manifest::FileFilter> filters;
+    blink::Manifest::FileFilter text = {
+        base::ASCIIToUTF16("text"),
+        {base::ASCIIToUTF16(".txt"), base::ASCIIToUTF16("text/*")}};
+    filters.push_back(text);
+    web_app_info.file_handler->files = std::move(filters);
+  }
+
+  const extensions::Extension* app = InstallBookmarkApp(web_app_info);
+
+  AppLaunchParams params(browser()->profile(), app,
+                         extensions::LaunchContainer::LAUNCH_CONTAINER_WINDOW,
+                         WindowOpenDisposition::NEW_WINDOW,
+                         extensions::AppLaunchSource::SOURCE_FILE_HANDLER);
+
+  content::TestNavigationObserver navigation_observer(
+      web_app_info.file_handler->action);
+  navigation_observer.StartWatchingNewWebContents();
+
+  content::WebContents* web_contents =
+      OpenApplicationWindow(params, web_app_info.file_handler->action);
+  navigation_observer.Wait();
+
+  base::Value expected(base::Value::Type::DICTIONARY);
+  expected.SetStringKey("cause", "default");
+  expected.SetIntKey("length", 0);
+
+  EXPECT_EQ(expected,
+            content::EvalJsWithManualReply(
+                web_contents,
+                "getLaunchParams().then(params => {"
+                "domAutomationController.send({ cause: params.cause, length: "
+                "params.files.length });"
+                "});"));
+}
+
 #if !defined(OS_ANDROID)
 class HostedAppBadgingTest : public HostedAppTest {
  public:
@@ -2908,6 +2975,11 @@
     HostedAppSitePerProcessTest,
     ::testing::Values(AppType::HOSTED_APP));
 
+INSTANTIATE_TEST_SUITE_P(
+    /* no prefix */,
+    HostedAppFileHandlingTest,
+    ::testing::Values(AppType::BOOKMARK_APP));
+
 #if !defined(OS_CHROMEOS)
 INSTANTIATE_TEST_SUITE_P(
     /* no prefix */,
diff --git a/chrome/browser/ui/libgtkui/settings_provider.h b/chrome/browser/ui/libgtkui/settings_provider.h
index eca7fe6..ec06c6c 100644
--- a/chrome/browser/ui/libgtkui/settings_provider.h
+++ b/chrome/browser/ui/libgtkui/settings_provider.h
@@ -5,9 +5,6 @@
 #ifndef CHROME_BROWSER_UI_LIBGTKUI_SETTINGS_PROVIDER_H_
 #define CHROME_BROWSER_UI_LIBGTKUI_SETTINGS_PROVIDER_H_
 
-#include <string>
-#include <vector>
-
 namespace libgtkui {
 
 // This class is just a switch between SettingsProviderGSettings and
diff --git a/chrome/browser/ui/libgtkui/settings_provider_gsettings.cc b/chrome/browser/ui/libgtkui/settings_provider_gsettings.cc
index 379e641..384cd6fa 100644
--- a/chrome/browser/ui/libgtkui/settings_provider_gsettings.cc
+++ b/chrome/browser/ui/libgtkui/settings_provider_gsettings.cc
@@ -6,6 +6,8 @@
 
 #include <gio/gio.h>
 
+#include <memory>
+
 #include "base/environment.h"
 #include "base/nix/xdg_util.h"
 #include "chrome/browser/ui/libgtkui/gtk_ui.h"
diff --git a/chrome/browser/ui/libgtkui/settings_provider_gsettings.h b/chrome/browser/ui/libgtkui/settings_provider_gsettings.h
index 295f041..52c4a92 100644
--- a/chrome/browser/ui/libgtkui/settings_provider_gsettings.h
+++ b/chrome/browser/ui/libgtkui/settings_provider_gsettings.h
@@ -7,6 +7,8 @@
 
 #include <gio/gio.h>
 
+#include <string>
+
 #include "base/macros.h"
 #include "chrome/browser/ui/libgtkui/settings_provider.h"
 #include "ui/base/glib/glib_signal.h"
diff --git a/chrome/browser/ui/libgtkui/settings_provider_gtk.h b/chrome/browser/ui/libgtkui/settings_provider_gtk.h
index 58863b6..889726181 100644
--- a/chrome/browser/ui/libgtkui/settings_provider_gtk.h
+++ b/chrome/browser/ui/libgtkui/settings_provider_gtk.h
@@ -5,7 +5,9 @@
 #ifndef CHROME_BROWSER_UI_LIBGTKUI_SETTINGS_PROVIDER_GTK_H_
 #define CHROME_BROWSER_UI_LIBGTKUI_SETTINGS_PROVIDER_GTK_H_
 
+#include <memory>
 #include <string>
+#include <vector>
 
 #include "base/macros.h"
 #include "chrome/browser/ui/libgtkui/settings_provider.h"
diff --git a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
index c77f594..90f205de 100644
--- a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
@@ -27,6 +27,8 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/web_applications/system_web_app_manager.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -1642,6 +1644,11 @@
   OmniboxView* omnibox_view = nullptr;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
 #if defined(OS_CHROMEOS)
+  // Install the Settings App.
+  web_app::WebAppProvider::Get(browser()->profile())
+      ->system_web_app_manager()
+      .InstallSystemAppsForTesting();
+
   EXPECT_FALSE(
       chrome::SettingsWindowManager::GetInstance()->FindBrowserForProfile(
           browser()->profile()));
diff --git a/chrome/browser/ui/search/local_ntp_browsertest.cc b/chrome/browser/ui/search/local_ntp_browsertest.cc
index 62f47202..5c2bbe3d 100644
--- a/chrome/browser/ui/search/local_ntp_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_browsertest.cc
@@ -513,14 +513,14 @@
   // favicon images.
   int succeeded_favicons = 0;
   ASSERT_TRUE(instant_test_utils::GetIntFromJS(
-      iframe, "document.querySelectorAll('.md-icon img').length",
+      iframe,
+      "document.querySelectorAll('.md-icon:not(.failed-favicon)').length",
       &succeeded_favicons));
   int failed_favicons = 0;
   ASSERT_TRUE(instant_test_utils::GetIntFromJS(
       iframe, "document.querySelectorAll('.md-icon.failed-favicon').length",
       &failed_favicons));
-  // Check if only one add button exists in the frame. This will be included in
-  // the total favicon count.
+  // And check if only one add button exists in the frame.
   int add_button_favicon = 0;
   ASSERT_TRUE(instant_test_utils::GetIntFromJS(
       iframe, "document.querySelectorAll('.md-add-icon').length",
@@ -529,14 +529,13 @@
 
   // First, sanity check that the numbers line up (none of the css classes was
   // renamed, etc).
-  EXPECT_EQ(total_favicons,
-            succeeded_favicons + add_button_favicon + failed_favicons);
+  EXPECT_EQ(total_favicons, succeeded_favicons + failed_favicons);
 
   // Since we're in a non-signed-in, fresh profile with no history, there should
   // be the default TopSites tiles (see history::PrepopulatedPage).
   // Check that there is at least one tile, and that all of them loaded their
   // images successfully.
-  EXPECT_EQ(total_favicons, succeeded_favicons + add_button_favicon);
+  EXPECT_EQ(total_favicons, succeeded_favicons);
   EXPECT_EQ(0, failed_favicons);
 }
 
@@ -610,15 +609,15 @@
       &title));
 
   // Move the tile to the front.
-  std::string tid;
+  std::string rid;
   ASSERT_TRUE(instant_test_utils::GetStringFromJS(
       iframe,
       "document.querySelectorAll('#mv-tiles "
-      ".md-tile')[1].getAttribute('data-tid')",
-      &tid));
+      ".md-tile')[1].getAttribute('data-rid')",
+      &rid));
   local_ntp_test_utils::ExecuteScriptOnNTPAndWaitUntilLoaded(
       iframe, "window.chrome.embeddedSearch.newTabPage.reorderCustomLink(" +
-                  tid + ", 0)");
+                  rid + ", 0)");
 
   // Check that the first tile is the tile that was moved.
   std::string new_title;
@@ -632,11 +631,11 @@
   ASSERT_TRUE(instant_test_utils::GetStringFromJS(
       iframe,
       "document.querySelectorAll('#mv-tiles "
-      ".md-tile')[0].getAttribute('data-tid')",
-      &tid));
+      ".md-tile')[0].getAttribute('data-rid')",
+      &rid));
   local_ntp_test_utils::ExecuteScriptOnNTPAndWaitUntilLoaded(
       iframe, "window.chrome.embeddedSearch.newTabPage.reorderCustomLink(" +
-                  tid + ", " + end_index + ")");
+                  rid + ", " + end_index + ")");
 
   // Check that the last tile is the tile that was moved.
   new_title = std::string();
diff --git a/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc b/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc
index 5477afae..7c5179f 100644
--- a/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc
+++ b/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc
@@ -63,6 +63,7 @@
     if (!EnableSystemWebApps())
       return;
 
+    // Install the Settings App.
     web_app::WebAppProvider::Get(browser()->profile())
         ->system_web_app_manager()
         .InstallSystemAppsForTesting();
diff --git a/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.cc b/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.cc
index d72f0729..433c0c64 100644
--- a/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.cc
+++ b/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.cc
@@ -12,6 +12,7 @@
 #include "components/sync_sessions/sync_sessions_client.h"
 #include "components/sync_sessions/synced_window_delegate.h"
 #include "components/sync_sessions/synced_window_delegates_getter.h"
+#include "components/translate/content/browser/content_record_page_language.h"
 #include "content/public/browser/favicon_status.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
@@ -72,11 +73,13 @@
 }
 
 GURL TabContentsSyncedTabDelegate::GetVirtualURLAtIndex(int i) const {
+  DCHECK(web_contents_);
   NavigationEntry* entry = GetPossiblyPendingEntryAtIndex(web_contents_, i);
   return entry ? entry->GetVirtualURL() : GURL();
 }
 
 GURL TabContentsSyncedTabDelegate::GetFaviconURLAtIndex(int i) const {
+  DCHECK(web_contents_);
   NavigationEntry* entry = GetPossiblyPendingEntryAtIndex(web_contents_, i);
   return entry ? (entry->GetFavicon().valid ? entry->GetFavicon().url : GURL())
                : GURL();
@@ -84,6 +87,7 @@
 
 ui::PageTransition TabContentsSyncedTabDelegate::GetTransitionAtIndex(
     int i) const {
+  DCHECK(web_contents_);
   NavigationEntry* entry = GetPossiblyPendingEntryAtIndex(web_contents_, i);
   // If we don't have an entry, there's not a coherent PageTransition we can
   // supply. There's no PageTransition::Unknown, so we just use the default,
@@ -92,9 +96,18 @@
                : ui::PageTransition::PAGE_TRANSITION_LINK;
 }
 
+std::string TabContentsSyncedTabDelegate::GetPageLanguageAtIndex(int i) const {
+  DCHECK(web_contents_);
+  NavigationEntry* entry = GetPossiblyPendingEntryAtIndex(web_contents_, i);
+  // If we don't have an entry, return empty language.
+  return entry ? translate::GetPageLanguageFromNavigation(entry)
+               : std::string();
+}
+
 void TabContentsSyncedTabDelegate::GetSerializedNavigationAtIndex(
     int i,
     sessions::SerializedNavigationEntry* serialized_entry) const {
+  DCHECK(web_contents_);
   NavigationEntry* entry = GetPossiblyPendingEntryAtIndex(web_contents_, i);
   if (entry) {
     // Explicitly exclude page state when serializing the navigation entry.
diff --git a/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h b/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h
index cb0a22f0..0f06e30 100644
--- a/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h
+++ b/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h
@@ -37,6 +37,7 @@
   GURL GetVirtualURLAtIndex(int i) const override;
   GURL GetFaviconURLAtIndex(int i) const override;
   ui::PageTransition GetTransitionAtIndex(int i) const override;
+  std::string GetPageLanguageAtIndex(int i) const override;
   void GetSerializedNavigationAtIndex(
       int i,
       sessions::SerializedNavigationEntry* serialized_entry) const override;
diff --git a/chrome/browser/ui/tabs/tab_group_id.cc b/chrome/browser/ui/tabs/tab_group_id.cc
index fdb59328..80f07f2f 100644
--- a/chrome/browser/ui/tabs/tab_group_id.cc
+++ b/chrome/browser/ui/tabs/tab_group_id.cc
@@ -9,6 +9,11 @@
   return TabGroupId(base::Token::CreateRandom());
 }
 
+// static
+TabGroupId TabGroupId::FromRawToken(base::Token token) {
+  return TabGroupId(token);
+}
+
 TabGroupId::TabGroupId(const TabGroupId& other) = default;
 
 TabGroupId& TabGroupId::operator=(const TabGroupId& other) = default;
diff --git a/chrome/browser/ui/tabs/tab_group_id.h b/chrome/browser/ui/tabs/tab_group_id.h
index 04bc9e6b..d61a8fd 100644
--- a/chrome/browser/ui/tabs/tab_group_id.h
+++ b/chrome/browser/ui/tabs/tab_group_id.h
@@ -11,6 +11,10 @@
  public:
   static TabGroupId GenerateNew();
 
+  // This should only called with |token| returned from a previous |token()|
+  // call on a valid TabGroupId.
+  static TabGroupId FromRawToken(base::Token token);
+
   TabGroupId(const TabGroupId& other);
 
   TabGroupId& operator=(const TabGroupId& other);
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index 9f14771..c5c87a4 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -290,6 +290,15 @@
     : delegate_(delegate), profile_(profile), weak_factory_(this) {
   DCHECK(delegate_);
   order_controller_.reset(new TabStripModelOrderController(this));
+
+  constexpr base::TimeDelta kTabScrubbingHistogramIntervalTime =
+      base::TimeDelta::FromSeconds(30);
+
+  last_tab_switch_timestamp_ = base::TimeTicks::Now();
+  tab_scrubbing_interval_timer_.Start(
+      FROM_HERE, kTabScrubbingHistogramIntervalTime,
+      base::BindRepeating(&TabStripModel::RecordTabScrubbingMetrics,
+                          base::Unretained(this)));
 }
 
 TabStripModel::~TabStripModel() {
@@ -509,6 +518,23 @@
   DCHECK(ContainsIndex(index));
   TRACE_EVENT0("ui", "TabStripModel::ActivateTabAt");
 
+  // Maybe increment count of tabs 'scrubbed' by mouse or key press for
+  // histogram data.
+  if (user_gesture.type == GestureType::kMouse ||
+      user_gesture.type == GestureType::kKeyboard) {
+    constexpr base::TimeDelta kMaxTimeConsideredScrubbing =
+        base::TimeDelta::FromMilliseconds(1500);
+    base::TimeDelta elapsed_time_since_tab_switch =
+        base::TimeTicks::Now() - last_tab_switch_timestamp_;
+    if (elapsed_time_since_tab_switch <= kMaxTimeConsideredScrubbing) {
+      if (user_gesture.type == GestureType::kMouse)
+        ++tabs_scrubbed_by_mouse_press_count_;
+      else if (user_gesture.type == GestureType::kKeyboard)
+        ++tabs_scrubbed_by_key_press_count_;
+    }
+  }
+  last_tab_switch_timestamp_ = base::TimeTicks::Now();
+
   TabSwitchEventLatencyRecorder::EventType event_type;
   switch (user_gesture.type) {
     case GestureType::kMouse:
@@ -538,6 +564,15 @@
                /*triggered_by_other_operation=*/false);
 }
 
+void TabStripModel::RecordTabScrubbingMetrics() {
+  UMA_HISTOGRAM_COUNTS_10000("Tabs.ScrubbedInInterval.MousePress",
+                             tabs_scrubbed_by_mouse_press_count_);
+  UMA_HISTOGRAM_COUNTS_10000("Tabs.ScrubbedInInterval.KeyPress",
+                             tabs_scrubbed_by_key_press_count_);
+  tabs_scrubbed_by_mouse_press_count_ = 0;
+  tabs_scrubbed_by_key_press_count_ = 0;
+}
+
 int TabStripModel::MoveWebContentsAt(int index,
                                      int to_position,
                                      bool select_after_move) {
@@ -734,7 +769,7 @@
 }
 
 bool TabStripModel::IsTabPinned(int index) const {
-  DCHECK(ContainsIndex(index));
+  DCHECK(ContainsIndex(index)) << index;
   return contents_data_[index]->pinned();
 }
 
@@ -946,39 +981,17 @@
   MoveWebContentsAt(active_index(), new_index, true);
 }
 
-void TabStripModel::AddToNewGroup(const std::vector<int>& indices) {
+TabGroupId TabStripModel::AddToNewGroup(const std::vector<int>& indices) {
   DCHECK(!reentrancy_guard_);
   base::AutoReset<bool> resetter(&reentrancy_guard_, true);
 
+  // The odds of |new_group| colliding with an existing group are astronomically
+  // low. If there is a collision, a DCHECK will fail in |AddToNewGroupImpl()|,
+  // in which case there is probably something wrong with
+  // |TabGroupId::GenerateNew()|.
   const TabGroupId new_group = TabGroupId::GenerateNew();
-  // If the random generator is working correctly, the odds of this happening
-  // are astronomically low. If this DCHECK fails, something is probably wrong
-  // in |TabGroupId::GenerateNew()|.
-  DCHECK(!std::any_of(
-      contents_data_.cbegin(), contents_data_.cend(),
-      [new_group](const auto& datum) { return datum->group() == new_group; }));
-
-  group_data_[new_group] = std::make_unique<TabGroupData>();
-
-  // Find a destination for the first tab that's not inside another group. We
-  // will stack the rest of the tabs up to its right.
-  int destination_index = -1;
-  for (int i = indices[0]; i < count(); i++) {
-    const int destination_candidate = i + 1;
-    const bool end_of_strip = !ContainsIndex(destination_candidate);
-    if (end_of_strip || !GetTabGroupForTab(destination_candidate).has_value() ||
-        GetTabGroupForTab(destination_candidate) !=
-            GetTabGroupForTab(indices[0])) {
-      destination_index = destination_candidate;
-      break;
-    }
-  }
-
-  std::vector<int> new_indices = indices;
-  if (IsTabPinned(new_indices[0]))
-    new_indices = SetTabsPinned(new_indices, true);
-
-  MoveTabsIntoGroup(new_indices, destination_index, new_group);
+  AddToNewGroupImpl(indices, new_group);
+  return new_group;
 }
 
 void TabStripModel::AddToExistingGroup(const std::vector<int>& indices,
@@ -986,25 +999,19 @@
   DCHECK(!reentrancy_guard_);
   base::AutoReset<bool> resetter(&reentrancy_guard_, true);
 
-  int destination_index = -1;
-  bool pin = false;
-  for (int i = contents_data_.size() - 1; i >= 0; i--) {
-    if (contents_data_[i]->group() == group) {
-      destination_index = i + 1;
-      pin = IsTabPinned(i);
-      break;
-    }
-  }
+  AddToExistingGroupImpl(indices, group);
+}
 
-  // Ignore indices that are already in the group.
-  std::vector<int> new_indices;
-  for (int candidate_index : indices) {
-    if (GetTabGroupForTab(candidate_index) != group)
-      new_indices.push_back(candidate_index);
-  }
-  new_indices = SetTabsPinned(new_indices, pin);
+void TabStripModel::AddToGroupForRestore(const std::vector<int>& indices,
+                                         TabGroupId group) {
+  DCHECK(!reentrancy_guard_);
+  base::AutoReset<bool> resetter(&reentrancy_guard_, true);
 
-  MoveTabsIntoGroup(new_indices, destination_index, group);
+  const bool group_exists = base::ContainsKey(group_data_, group);
+  if (group_exists)
+    AddToExistingGroupImpl(indices, group);
+  else
+    AddToNewGroupImpl(indices, group);
 }
 
 void TabStripModel::RemoveFromGroup(const std::vector<int>& indices) {
@@ -1732,6 +1739,58 @@
   }
 }
 
+void TabStripModel::AddToNewGroupImpl(const std::vector<int>& indices,
+                                      TabGroupId new_group) {
+  DCHECK(!std::any_of(
+      contents_data_.cbegin(), contents_data_.cend(),
+      [new_group](const auto& datum) { return datum->group() == new_group; }));
+
+  group_data_[new_group] = std::make_unique<TabGroupData>();
+
+  // Find a destination for the first tab that's not inside another group. We
+  // will stack the rest of the tabs up to its right.
+  int destination_index = -1;
+  for (int i = indices[0]; i < count(); i++) {
+    const int destination_candidate = i + 1;
+    const bool end_of_strip = !ContainsIndex(destination_candidate);
+    if (end_of_strip || !GetTabGroupForTab(destination_candidate).has_value() ||
+        GetTabGroupForTab(destination_candidate) !=
+            GetTabGroupForTab(indices[0])) {
+      destination_index = destination_candidate;
+      break;
+    }
+  }
+
+  std::vector<int> new_indices = indices;
+  if (IsTabPinned(new_indices[0]))
+    new_indices = SetTabsPinned(new_indices, true);
+
+  MoveTabsIntoGroup(new_indices, destination_index, new_group);
+}
+
+void TabStripModel::AddToExistingGroupImpl(const std::vector<int>& indices,
+                                           TabGroupId group) {
+  int destination_index = -1;
+  bool pin = false;
+  for (int i = contents_data_.size() - 1; i >= 0; i--) {
+    if (contents_data_[i]->group() == group) {
+      destination_index = i + 1;
+      pin = IsTabPinned(i);
+      break;
+    }
+  }
+
+  // Ignore indices that are already in the group.
+  std::vector<int> new_indices;
+  for (int candidate_index : indices) {
+    if (GetTabGroupForTab(candidate_index) != group)
+      new_indices.push_back(candidate_index);
+  }
+  new_indices = SetTabsPinned(new_indices, pin);
+
+  MoveTabsIntoGroup(new_indices, destination_index, group);
+}
+
 void TabStripModel::MoveTabsIntoGroup(const std::vector<int>& indices,
                                       int destination_index,
                                       TabGroupId group) {
diff --git a/chrome/browser/ui/tabs/tab_strip_model.h b/chrome/browser/ui/tabs/tab_strip_model.h
index 200796c..3b22c4cb 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.h
+++ b/chrome/browser/ui/tabs/tab_strip_model.h
@@ -19,6 +19,7 @@
 #include "base/observer_list.h"
 #include "base/optional.h"
 #include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/tabs/tab_group_data.h"
 #include "chrome/browser/ui/tabs/tab_group_id.h"
@@ -226,6 +227,11 @@
                      UserGestureDetails gesture_detail =
                          UserGestureDetails(GestureType::kNone));
 
+  // Report histogram metrics for the number of tabs 'scrubbed' within a given
+  // interval of time. Scrubbing is considered to be a tab activated for <= 1.5
+  // seconds for this metric.
+  void RecordTabScrubbingMetrics();
+
   // Move the WebContents at the specified index to another index. This
   // method does NOT send Detached/Attached notifications, rather it moves the
   // WebContents inline and sends a Moved notification instead.
@@ -386,10 +392,10 @@
 
   // Create a new tab group and add the set of tabs pointed to be |indices| to
   // it. Pins all of the tabs if any of them were pinned, and reorders the tabs
-  // so they are contiguous and do not split an existing group in half.
-  // |indices| must be sorted in ascending order. This feature is in development
-  // and gated behind a feature flag. https://crbug.com/915956.
-  void AddToNewGroup(const std::vector<int>& indices);
+  // so they are contiguous and do not split an existing group in half. Returns
+  // the new group. |indices| must be sorted in ascending order. This feature is
+  // in development and gated behind a feature flag. https://crbug.com/915956.
+  TabGroupId AddToNewGroup(const std::vector<int>& indices);
 
   // Add the set of tabs pointed to by |indices| to the given tab group |group|.
   // The tabs take on the pinnedness of the tabs already in the group, and are
@@ -398,6 +404,11 @@
   // behind a feature flag (see https://crbug.com/915956).
   void AddToExistingGroup(const std::vector<int>& indices, TabGroupId group);
 
+  // Similar to AddToExistingGroup(), but creates a group with id |group| if it
+  // doesn't exist. This is only intended to be called from session restore
+  // code.
+  void AddToGroupForRestore(const std::vector<int>& indices, TabGroupId group);
+
   // Removes the set of tabs pointed to by |indices| from the the groups they
   // are in, if any. The tabs are moved out of the group if necessary. |indices|
   // must be sorted in ascending order. This feature is in development and gated
@@ -603,6 +614,15 @@
   // starting at |start| to |index|. See MoveSelectedTabsTo for more details.
   void MoveSelectedTabsToImpl(int index, size_t start, size_t length);
 
+  // Adds tabs to newly-allocated group id |new_group|. This group must be new
+  // and have no tabs in it.
+  void AddToNewGroupImpl(const std::vector<int>& indices, TabGroupId new_group);
+
+  // Adds tabs to existing group |group|. This group must have been initialized
+  // by a previous call to |AddToNewGroupImpl()|.
+  void AddToExistingGroupImpl(const std::vector<int>& indices,
+                              TabGroupId group);
+
   // Moves the set of tabs indicated by |indices| to precede the tab at index
   // |destination_index|, maintaining their order and the order of tabs not
   // being moved, and adds them to the tab group |group|.
@@ -675,6 +695,19 @@
   // A recorder for recording tab switching input latency to UMA
   TabSwitchEventLatencyRecorder tab_switch_event_latency_recorder_;
 
+  // Timer used to mark intervals for metric collection on how many tabs are
+  // scrubbed over a certain interval of time.
+  base::RepeatingTimer tab_scrubbing_interval_timer_;
+  // Timestamp marking the last time a tab was activated by mouse press. This is
+  // used in determining how long a tab was active for metrics.
+  base::TimeTicks last_tab_switch_timestamp_ = base::TimeTicks();
+  // Counter used to keep track of tab scrubs during intervals set by
+  // |tab_scrubbing_interval_timer_|.
+  size_t tabs_scrubbed_by_mouse_press_count_ = 0;
+  // Counter used to keep track of tab scrubs during intervals set by
+  // |tab_scrubbing_interval_timer_|.
+  size_t tabs_scrubbed_by_key_press_count_ = 0;
+
   base::WeakPtrFactory<TabStripModel> weak_factory_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(TabStripModel);
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
index 31f6e8e..281c2dd 100644
--- a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
@@ -83,6 +83,8 @@
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
+#include "chrome/browser/web_applications/system_web_app_manager.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #endif
 
 using base::Bucket;
@@ -161,6 +163,11 @@
         IdentityManagerFactory::GetForProfile(browser()->profile())
             ->GetPrimaryAccountInfo();
     username = info.email;
+
+    // Install the Settings App.
+    web_app::WebAppProvider::Get(browser()->profile())
+        ->system_web_app_manager()
+        .InstallSystemAppsForTesting();
 #endif
     if (username.empty())
       username = "user@gmail.com";
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index bbbab343..d337a25 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -428,11 +428,6 @@
 // DropLocation ---------------------------------------------------------------
 
 struct BookmarkBarView::DropLocation {
-  DropLocation()
-      : operation(ui::DragDropTypes::DRAG_NONE),
-        on(false),
-        button_type(DROP_BOOKMARK) {}
-
   bool Equals(const DropLocation& other) {
     return ((other.index == index) && (other.on == on) &&
             (other.button_type == button_type));
@@ -442,13 +437,13 @@
   base::Optional<size_t> index;
 
   // Drop constants.
-  int operation;
+  int operation = ui::DragDropTypes::DRAG_NONE;
 
   // If true, the user is dropping on a folder.
-  bool on;
+  bool on = false;
 
   // Type of button.
-  DropButtonType button_type;
+  DropButtonType button_type = DROP_BOOKMARK;
 };
 
 // DropInfo -------------------------------------------------------------------
@@ -618,7 +613,7 @@
 
 const BookmarkNode* BookmarkBarView::GetNodeForButtonAtModelIndex(
     const gfx::Point& loc,
-    int* model_start_index) {
+    size_t* model_start_index) {
   *model_start_index = 0;
 
   if (loc.x() < 0 || loc.x() >= width() || loc.y() < 0 || loc.y() >= height())
@@ -1076,16 +1071,16 @@
   }
 
   const BookmarkNode* parent_node;
-  int index;
+  size_t index;
   if (drop_info_->location.button_type == DROP_OTHER_FOLDER) {
     parent_node = root;
-    index = parent_node->child_count();
+    index = parent_node->children().size();
   } else if (drop_info_->location.on) {
     parent_node = root->children()[drop_info_->location.index.value()].get();
-    index = parent_node->child_count();
+    index = parent_node->children().size();
   } else {
     parent_node = root;
-    index = int{drop_info_->location.index.value()};
+    index = drop_info_->location.index.value();
   }
   const BookmarkNodeData data = drop_info_->data;
   DCHECK(data.is_valid());
@@ -1180,9 +1175,9 @@
 
 void BookmarkBarView::BookmarkNodeMoved(BookmarkModel* model,
                                         const BookmarkNode* old_parent,
-                                        int old_index,
+                                        size_t old_index,
                                         const BookmarkNode* new_parent,
-                                        int new_index) {
+                                        size_t new_index) {
   bool was_throbbing =
       throbbing_view_ &&
       throbbing_view_ == DetermineViewToThrobFromRemove(old_parent, old_index);
@@ -1192,22 +1187,22 @@
       BookmarkNodeRemovedImpl(model, old_parent, old_index);
   if (BookmarkNodeAddedImpl(model, new_parent, new_index))
     needs_layout_and_paint = true;
-  if (was_throbbing && new_index < int{bookmark_buttons_.size()})
-    StartThrobbing(new_parent->GetChild(new_index), false);
+  if (was_throbbing && new_index < bookmark_buttons_.size())
+    StartThrobbing(new_parent->children()[new_index].get(), false);
   if (needs_layout_and_paint)
     LayoutAndPaint();
 }
 
 void BookmarkBarView::BookmarkNodeAdded(BookmarkModel* model,
                                         const BookmarkNode* parent,
-                                        int index) {
+                                        size_t index) {
   if (BookmarkNodeAddedImpl(model, parent, index))
     LayoutAndPaint();
 }
 
 void BookmarkBarView::BookmarkNodeRemoved(BookmarkModel* model,
                                           const BookmarkNode* parent,
-                                          int old_index,
+                                          size_t old_index,
                                           const BookmarkNode* node,
                                           const std::set<GURL>& removed_urls) {
   // Close the menu if the menu is showing for the deleted node.
@@ -1250,8 +1245,10 @@
   bookmark_buttons_.clear();
 
   // Create the new buttons.
-  for (int i = 0; i < node->child_count(); ++i)
-    InsertBookmarkButtonAtIndex(CreateBookmarkButton(node->GetChild(i)), i);
+  for (size_t i = 0; i < node->children().size(); ++i) {
+    InsertBookmarkButtonAtIndex(CreateBookmarkButton(node->children()[i].get()),
+                                i);
+  }
 
   LayoutAndPaint();
 }
@@ -1322,7 +1319,7 @@
                                           const ui::Event* event) {
   const BookmarkNode* node;
 
-  int start_index = 0;
+  size_t start_index = 0;
   if (view == other_bookmarks_button_) {
     node = model_->other_node();
   } else if (view == managed_bookmarks_button_) {
@@ -1331,9 +1328,9 @@
     node = model_->bookmark_bar_node();
     start_index = GetFirstHiddenNodeIndex();
   } else {
-    int button_index = GetIndexForButton(view);
-    DCHECK_NE(-1, button_index);
-    node = model_->bookmark_bar_node()->GetChild(button_index);
+    size_t button_index = GetIndexForButton(view);
+    DCHECK_NE(size_t{-1}, button_index);
+    node = model_->bookmark_bar_node()->children()[button_index].get();
   }
 
   // Clicking the middle mouse button or clicking with Control/Command key down
@@ -1368,9 +1365,10 @@
     return;
   }
 
-  int index = GetIndexForButton(sender);
-  DCHECK_NE(-1, index);
-  const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(index);
+  size_t index = GetIndexForButton(sender);
+  DCHECK_NE(size_t{-1}, index);
+  const BookmarkNode* node =
+      model_->bookmark_bar_node()->children()[index].get();
   DCHECK(page_navigator_);
 
   // Only URL nodes have regular buttons on the bookmarks bar; folder clicks
@@ -1406,11 +1404,11 @@
     // User clicked on one of the bookmark buttons, find which one they
     // clicked on, except for the apps page shortcut, which must behave as if
     // the user clicked on the bookmark bar background.
-    int bookmark_button_index = GetIndexForButton(source);
-    DCHECK_NE(-1, bookmark_button_index);
-    DCHECK_LT(size_t{bookmark_button_index}, bookmark_buttons_.size());
+    size_t bookmark_button_index = GetIndexForButton(source);
+    DCHECK_NE(size_t{-1}, bookmark_button_index);
+    DCHECK_LT(bookmark_button_index, bookmark_buttons_.size());
     const BookmarkNode* node =
-        model_->bookmark_bar_node()->GetChild(bookmark_button_index);
+        model_->bookmark_bar_node()->children()[bookmark_button_index].get();
     nodes.push_back(node);
     parent = node->parent();
   } else {
@@ -1485,7 +1483,7 @@
   return BOOKMARK_LAUNCH_LOCATION_ATTACHED_BAR;
 }
 
-int BookmarkBarView::GetFirstHiddenNodeIndex() {
+size_t BookmarkBarView::GetFirstHiddenNodeIndex() {
   const auto i =
       std::find_if(bookmark_buttons_.cbegin(), bookmark_buttons_.cend(),
                    [](const auto* button) { return !button->GetVisible(); });
@@ -1608,24 +1606,24 @@
 
 bool BookmarkBarView::BookmarkNodeAddedImpl(BookmarkModel* model,
                                             const BookmarkNode* parent,
-                                            int index) {
+                                            size_t index) {
   const bool needs_layout_and_paint = UpdateOtherAndManagedButtonsVisibility();
   if (parent != model->bookmark_bar_node())
     return needs_layout_and_paint;
-  if (index < int{bookmark_buttons_.size()}) {
-    const BookmarkNode* node = parent->GetChild(index);
+  if (index < bookmark_buttons_.size()) {
+    const BookmarkNode* node = parent->children()[index].get();
     InsertBookmarkButtonAtIndex(CreateBookmarkButton(node), index);
     return true;
   }
   // If the new node was added after the last button we've created we may be
   // able to fit it. Assume we can by returning true, which forces a Layout()
   // and creation of the button (if it fits).
-  return index == int{bookmark_buttons_.size()};
+  return index == bookmark_buttons_.size();
 }
 
 bool BookmarkBarView::BookmarkNodeRemovedImpl(BookmarkModel* model,
                                               const BookmarkNode* parent,
-                                              int index) {
+                                              size_t index) {
   const bool needs_layout = UpdateOtherAndManagedButtonsVisibility();
 
   StopThrobbing(true);
@@ -1636,10 +1634,10 @@
     // Only children of the bookmark_bar_node get buttons.
     return needs_layout;
   }
-  if (size_t{index} >= bookmark_buttons_.size())
+  if (index >= bookmark_buttons_.size())
     return needs_layout;
 
-  views::LabelButton* button = bookmark_buttons_[size_t{index}];
+  views::LabelButton* button = bookmark_buttons_[index];
   bookmark_buttons_.erase(bookmark_buttons_.cbegin() + index);
   delete button;
   return true;
@@ -1683,7 +1681,7 @@
   if (!menu_button)
     return;
 
-  int start_index = 0;
+  size_t start_index = 0;
   if (node == model_->bookmark_bar_node())
     start_index = GetFirstHiddenNodeIndex();
 
@@ -1856,12 +1854,12 @@
     parent_on_bb = parent;
   }
   if (parent_on_bb) {
-    int index = bbn->GetIndexOf(parent_on_bb);
+    size_t index = size_t{bbn->GetIndexOf(parent_on_bb)};
     if (index >= GetFirstHiddenNodeIndex()) {
       // Node is hidden, animate the overflow button.
       throbbing_view_ = overflow_button_;
     } else if (!overflow_only) {
-      throbbing_view_ = static_cast<Button*>(bookmark_buttons_[size_t{index}]);
+      throbbing_view_ = static_cast<Button*>(bookmark_buttons_[index]);
     }
   } else if (bookmarks::IsDescendantOf(node, managed_->managed_node())) {
     throbbing_view_ = managed_bookmarks_button_;
@@ -1876,14 +1874,14 @@
 
 views::Button* BookmarkBarView::DetermineViewToThrobFromRemove(
     const BookmarkNode* parent,
-    int old_index) {
+    size_t old_index) {
   const BookmarkNode* bbn = model_->bookmark_bar_node();
   const BookmarkNode* old_node = parent;
-  int old_index_on_bb = old_index;
+  size_t old_index_on_bb = old_index;
   while (old_node && old_node != bbn) {
     const BookmarkNode* parent = old_node->parent();
     if (parent == bbn) {
-      old_index_on_bb = bbn->GetIndexOf(old_node);
+      old_index_on_bb = size_t{bbn->GetIndexOf(old_node)};
       break;
     }
     old_node = parent;
@@ -1893,7 +1891,7 @@
       // Node is hidden, animate the overflow button.
       return overflow_button_;
     }
-    return static_cast<Button*>(bookmark_buttons_[size_t{old_index_on_bb}]);
+    return static_cast<Button*>(bookmark_buttons_[old_index_on_bb]);
   }
   if (bookmarks::IsDescendantOf(parent, managed_->managed_node()))
     return managed_bookmarks_button_;
@@ -1981,7 +1979,7 @@
 }
 
 void BookmarkBarView::InsertBookmarkButtonAtIndex(views::View* button,
-                                                  int index) {
+                                                  size_t index) {
 // All of the secondary buttons are always in the view hierarchy, even if
 // they're not visible. The order should be: [Apps shortcut] [Managed bookmark
 // button] ..bookmark buttons.. [Overflow chevron] [Other bookmarks]
@@ -1999,16 +1997,17 @@
   DCHECK_EQ(*i++, overflow_button_);
   DCHECK_EQ(*i++, other_bookmarks_button_);
 #endif
-  AddChildViewAt(button, GetIndexOf(managed_bookmarks_button_) + 1 + index);
+  AddChildViewAt(button,
+                 GetIndexOf(managed_bookmarks_button_) + 1 + int{index});
 }
 
-int BookmarkBarView::GetIndexForButton(views::View* button) {
+size_t BookmarkBarView::GetIndexForButton(views::View* button) {
   auto it =
       std::find(bookmark_buttons_.cbegin(), bookmark_buttons_.cend(), button);
   if (it == bookmark_buttons_.cend())
-    return -1;
+    return size_t{-1};
 
-  return it - bookmark_buttons_.cbegin();
+  return size_t{it - bookmark_buttons_.cbegin()};
 }
 
 SkColor BookmarkBarView::GetBookmarkBarTextColor() {
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h
index c596fbe..f10d150 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h
@@ -106,7 +106,7 @@
   // contained in the overflow menu.
   const bookmarks::BookmarkNode* GetNodeForButtonAtModelIndex(
       const gfx::Point& loc,
-      int* model_start_index);
+      size_t* model_start_index);
 
   // Returns the MenuButton for node.
   views::MenuButton* GetMenuButtonForNode(const bookmarks::BookmarkNode* node);
@@ -194,15 +194,15 @@
   void BookmarkModelBeingDeleted(bookmarks::BookmarkModel* model) override;
   void BookmarkNodeMoved(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* old_parent,
-                         int old_index,
+                         size_t old_index,
                          const bookmarks::BookmarkNode* new_parent,
-                         int new_index) override;
+                         size_t new_index) override;
   void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* parent,
-                         int index) override;
+                         size_t index) override;
   void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
                            const bookmarks::BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const bookmarks::BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override;
   void BookmarkAllUserNodesRemoved(bookmarks::BookmarkModel* model,
@@ -266,7 +266,7 @@
 
   // Returns the index of the first hidden bookmark button. If all buttons are
   // visible, this returns GetBookmarkButtonCount().
-  int GetFirstHiddenNodeIndex();
+  size_t GetFirstHiddenNodeIndex();
 
   // Creates the button showing the "Other Bookmarks" folder.
   views::MenuButton* CreateOtherBookmarksButton();
@@ -292,13 +292,13 @@
   // is required.
   bool BookmarkNodeAddedImpl(bookmarks::BookmarkModel* model,
                              const bookmarks::BookmarkNode* parent,
-                             int index);
+                             size_t index);
 
   // Implementation for BookmarkNodeRemoved. Returns true if LayoutAndPaint() is
   // required.
   bool BookmarkNodeRemovedImpl(bookmarks::BookmarkModel* model,
                                const bookmarks::BookmarkNode* parent,
-                               int index);
+                               size_t index);
 
   // If the node is a child of the root node, the button is updated
   // appropriately.
@@ -338,7 +338,7 @@
   // removed.
   views::Button* DetermineViewToThrobFromRemove(
       const bookmarks::BookmarkNode* parent,
-      int old_index);
+      size_t old_index);
 
   // Sets/updates the colors and icons for all the child objects in the
   // bookmarks bar.
@@ -364,11 +364,11 @@
 
   // Inserts |button| in logical position |index| in the bar, maintaining
   // correct focus traversal order.
-  void InsertBookmarkButtonAtIndex(views::View* button, int index);
+  void InsertBookmarkButtonAtIndex(views::View* button, size_t index);
 
   // Returns the model index for the bookmark associated with |button|,
-  // or -1 if |button| is not a bookmark button from this bar.
-  int GetIndexForButton(views::View* button);
+  // or size_t{-1} if |button| is not a bookmark button from this bar.
+  size_t GetIndexForButton(views::View* button);
 
   // Returns the color that should be used to draw text on the bookmark bar.
   SkColor GetBookmarkBarTextColor();
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc
index b7a6570..f5343f6 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc
@@ -76,7 +76,7 @@
     test_helper_ = std::make_unique<BookmarkBarViewTestHelper>(bookmark_bar());
   }
 
-  views::LabelButton* GetBookmarkButton(int index) {
+  views::LabelButton* GetBookmarkButton(size_t index) {
     return test_helper_->GetBookmarkButton(index);
   }
 
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
index 96b9894d2..e7f6bd6e 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
@@ -396,7 +396,7 @@
     model_->AddURL(f11, 0, ASCIIToUTF16("f11a"), GURL(test_base + "f11a"));
     model_->AddURL(f1, 2, ASCIIToUTF16("f1b"), GURL(test_base + "f1b"));
     if (big_menu) {
-      for (int i = 1; i <= 100; ++i) {
+      for (size_t i = 1; i <= 100; ++i) {
         model_->AddURL(f1, i + 1, ASCIIToUTF16("f") + base::NumberToString16(i),
                        GURL(test_base + "f" + base::NumberToString(i)));
       }
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_unittest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_unittest.cc
index e6d5547..c8dcfbf 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_unittest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_unittest.cc
@@ -72,7 +72,7 @@
   void SizeUntilButtonsVisible(size_t count) {
     const int start_width = bookmark_bar_view_->width();
     const int height = bookmark_bar_view_->GetPreferredSize().height();
-    for (int i = 0;
+    for (size_t i = 0;
          i < 100 && (test_helper_->GetBookmarkButtonCount() < count ||
                      !test_helper_->GetBookmarkButton(count - 1)->GetVisible());
          ++i) {
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
index e0a3d2b..31a06268 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
@@ -258,22 +258,22 @@
 
 void BookmarkEditorView::BookmarkNodeMoved(BookmarkModel* model,
                                            const BookmarkNode* old_parent,
-                                           int old_index,
+                                           size_t old_index,
                                            const BookmarkNode* new_parent,
-                                           int new_index) {
+                                           size_t new_index) {
   Reset();
 }
 
 void BookmarkEditorView::BookmarkNodeAdded(BookmarkModel* model,
                                            const BookmarkNode* parent,
-                                           int index) {
+                                           size_t index) {
   Reset();
 }
 
 void BookmarkEditorView::BookmarkNodeRemoved(
     BookmarkModel* model,
     const BookmarkNode* parent,
-    int index,
+    size_t index,
     const BookmarkNode* node,
     const std::set<GURL>& removed_urls) {
   if ((details_.type == EditDetails::EXISTING_NODE &&
@@ -594,7 +594,7 @@
     const BookmarkNode* child_bb_node = nullptr;
     if (child_b_node->value == 0) {
       // New folder.
-      child_bb_node = bb_model_->AddFolder(bb_node, bb_node->child_count(),
+      child_bb_node = bb_model_->AddFolder(bb_node, bb_node->children().size(),
                                            child_b_node->GetTitle());
       child_b_node->value = child_bb_node->id();
     } else {
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h
index 54e099c..f4caf00 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h
@@ -134,15 +134,15 @@
                            bool ids_reassigned) override {}
   void BookmarkNodeMoved(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* old_parent,
-                         int old_index,
+                         size_t old_index,
                          const bookmarks::BookmarkNode* new_parent,
-                         int new_index) override;
+                         size_t new_index) override;
   void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* parent,
-                         int index) override;
+                         size_t index) override;
   void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
                            const bookmarks::BookmarkNode* parent,
-                           int index,
+                           size_t index,
                            const bookmarks::BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override;
   void BookmarkAllUserNodesRemoved(bookmarks::BookmarkModel* model,
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view_unittest.cc b/chrome/browser/ui/views/bookmarks/bookmark_editor_view_unittest.cc
index 72416ad..1b44574 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view_unittest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view_unittest.cc
@@ -153,7 +153,7 @@
 TEST_F(BookmarkEditorViewTest, ModelsMatch) {
   CreateEditor(profile_.get(), NULL,
                BookmarkEditor::EditDetails::AddNodeInFolder(
-                   NULL, -1, GURL(), base::string16()),
+                   NULL, size_t{-1}, GURL(), base::string16()),
                BookmarkEditorView::SHOW_TREE);
   BookmarkEditorView::EditorNode* editor_root = editor_tree_model()->GetRoot();
   // The root should have two or three children: bookmark bar, other bookmarks
@@ -401,7 +401,7 @@
 // in then the editor is initially created showing.
 TEST_F(BookmarkEditorViewTest, MoveFolder) {
   BookmarkEditor::EditDetails details = BookmarkEditor::EditDetails::AddFolder(
-      model_->bookmark_bar_node(), -1);
+      model_->bookmark_bar_node(), size_t{-1});
   details.urls.push_back(std::make_pair(GURL(base_path() + "x"),
                                         ASCIIToUTF16("z")));
   CreateEditor(profile_.get(), model_->bookmark_bar_node(),
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc
index 4f8c5e4..28208233 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc
@@ -28,14 +28,14 @@
                                                PageNavigator* page_navigator,
                                                views::Widget* parent,
                                                const BookmarkNode* node,
-                                               int start_child_index,
+                                               size_t start_child_index,
                                                bool for_drop)
     : menu_delegate_(new BookmarkMenuDelegate(browser, page_navigator, parent)),
       node_(node),
       observer_(NULL),
       for_drop_(for_drop),
       bookmark_bar_(NULL) {
-  menu_delegate_->Init(this, NULL, node, start_child_index,
+  menu_delegate_->Init(this, NULL, node, int{start_child_index},
                        BookmarkMenuDelegate::HIDE_PERMANENT_FOLDERS,
                        BOOKMARK_LAUNCH_LOCATION_BAR_SUBFOLDER);
   int run_type = 0;
@@ -163,13 +163,13 @@
     return NULL;
   gfx::Point bookmark_bar_loc(screen_point);
   views::View::ConvertPointFromScreen(bookmark_bar_, &bookmark_bar_loc);
-  int start_index;
+  size_t start_index;
   const BookmarkNode* node = bookmark_bar_->GetNodeForButtonAtModelIndex(
       bookmark_bar_loc, &start_index);
   if (!node || !node->is_folder())
     return NULL;
 
-  menu_delegate_->SetActiveMenu(node, start_index);
+  menu_delegate_->SetActiveMenu(node, int{start_index});
   *button = bookmark_bar_->GetMenuButtonForNode(node);
   bookmark_bar_->GetAnchorPositionForButton(*button, anchor);
   *has_mnemonics = false;
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h
index 1caaa0e6..1663145 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h
@@ -50,7 +50,7 @@
                          content::PageNavigator* page_navigator,
                          views::Widget* parent,
                          const bookmarks::BookmarkNode* node,
-                         int start_child_index,
+                         size_t start_child_index,
                          bool for_drop);
 
   void RunMenuAt(BookmarkBarView* bookmark_bar);
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc b/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc
index 726ac4c5..a07fa5d 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc
@@ -254,7 +254,7 @@
 
   const BookmarkNode* node = menu_id_to_node_map_[item->GetCommand()];
   const BookmarkNode* drop_parent = node->parent();
-  int index_to_drop_at = drop_parent->GetIndexOf(node);
+  size_t index_to_drop_at = size_t{drop_parent->GetIndexOf(node)};
   BookmarkModel* model = GetBookmarkModel();
   switch (*position) {
     case views::MenuDelegate::DROP_AFTER:
@@ -274,7 +274,7 @@
 
     case views::MenuDelegate::DROP_ON:
       drop_parent = node;
-      index_to_drop_at = node->child_count();
+      index_to_drop_at = node->children().size();
       break;
 
     default:
@@ -295,7 +295,7 @@
   DCHECK(model);
   const BookmarkNode* drop_parent = drop_node->parent();
   DCHECK(drop_parent);
-  int index_to_drop_at = drop_parent->GetIndexOf(drop_node);
+  size_t index_to_drop_at = size_t{drop_parent->GetIndexOf(drop_node)};
   switch (position) {
     case views::MenuDelegate::DROP_AFTER:
       index_to_drop_at++;
diff --git a/chrome/browser/ui/views/chrome_typography_provider.cc b/chrome/browser/ui/views/chrome_typography_provider.cc
index e1ffcda..cd04df9 100644
--- a/chrome/browser/ui/views/chrome_typography_provider.cc
+++ b/chrome/browser/ui/views/chrome_typography_provider.cc
@@ -107,26 +107,6 @@
 
 }  // namespace
 
-#if defined(OS_WIN)
-// static
-int ChromeTypographyProvider::GetPlatformFontHeight(int font_context) {
-  switch (font_context) {
-    case CONTEXT_HEADLINE:
-      return 27;
-    case views::style::CONTEXT_DIALOG_TITLE:
-      return 20;
-    case CONTEXT_BODY_TEXT_LARGE:
-    case CONTEXT_TAB_HOVER_CARD_TITLE:
-    case views::style::CONTEXT_MESSAGE_BOX_BODY_TEXT:
-      return 18;
-    case CONTEXT_BODY_TEXT_SMALL:
-      return 16;
-  }
-  NOTREACHED();
-  return 0;
-}
-#endif
-
 const gfx::FontList& ChromeTypographyProvider::GetFont(int context,
                                                        int style) const {
   // "Target" font size constants.
@@ -284,14 +264,10 @@
   constexpr int kBodyTextLargePlatformHeight = 16;
   constexpr int kBodyTextSmallPlatformHeight = 15;
 #elif defined(OS_WIN)
-  static const int kHeadlinePlatformHeight =
-      GetPlatformFontHeight(CONTEXT_HEADLINE);
-  static const int kTitlePlatformHeight =
-      GetPlatformFontHeight(views::style::CONTEXT_DIALOG_TITLE);
-  static const int kBodyTextLargePlatformHeight =
-      GetPlatformFontHeight(CONTEXT_BODY_TEXT_LARGE);
-  static const int kBodyTextSmallPlatformHeight =
-      GetPlatformFontHeight(CONTEXT_BODY_TEXT_SMALL);
+  constexpr int kHeadlinePlatformHeight = 27;
+  constexpr int kTitlePlatformHeight = 20;
+  constexpr int kBodyTextLargePlatformHeight = 18;
+  constexpr int kBodyTextSmallPlatformHeight = 16;
 #else
   constexpr int kHeadlinePlatformHeight = 24;
   constexpr int kTitlePlatformHeight = 18;
diff --git a/chrome/browser/ui/views/chrome_typography_provider.h b/chrome/browser/ui/views/chrome_typography_provider.h
index 5354cf4f..a3076616 100644
--- a/chrome/browser/ui/views/chrome_typography_provider.h
+++ b/chrome/browser/ui/views/chrome_typography_provider.h
@@ -14,13 +14,6 @@
  public:
   ChromeTypographyProvider() = default;
 
-#if defined(OS_WIN)
-  // Returns the expected platform font height for the current system
-  // configuration. Different configurations can produce slightly different
-  // results.
-  static int GetPlatformFontHeight(int font_context);
-#endif
-
   // TypographyProvider:
   const gfx::FontList& GetFont(int context, int style) const override;
   SkColor GetColor(const views::View& view,
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.cc b/chrome/browser/ui/views/content_setting_bubble_contents.cc
index b6333c99..3134546c 100644
--- a/chrome/browser/ui/views/content_setting_bubble_contents.cc
+++ b/chrome/browser/ui/views/content_setting_bubble_contents.cc
@@ -67,10 +67,10 @@
 // in the content setting bubble.
 class MediaComboboxModel : public ui::ComboboxModel {
  public:
-  explicit MediaComboboxModel(blink::MediaStreamType type);
+  explicit MediaComboboxModel(blink::mojom::MediaStreamType type);
   ~MediaComboboxModel() override;
 
-  blink::MediaStreamType type() const { return type_; }
+  blink::mojom::MediaStreamType type() const { return type_; }
   const blink::MediaStreamDevices& GetDevices() const;
   int GetDeviceIndex(const blink::MediaStreamDevice& device) const;
 
@@ -79,7 +79,7 @@
   base::string16 GetItemAt(int index) override;
 
  private:
-  blink::MediaStreamType type_;
+  blink::mojom::MediaStreamType type_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaComboboxModel);
 };
@@ -117,7 +117,7 @@
       first_row = false;
 
       layout->StartRow(views::GridLayout::kFixedSize, kColumnSetId);
-      blink::MediaStreamType stream_type = i->first;
+      blink::mojom::MediaStreamType stream_type = i->first;
       const ContentSettingBubbleModel::MediaMenu& menu = i->second;
 
       views::Label* label = new views::Label(menu.label);
@@ -151,10 +151,10 @@
 
 // MediaComboboxModel ----------------------------------------------------------
 
-MediaComboboxModel::MediaComboboxModel(blink::MediaStreamType type)
+MediaComboboxModel::MediaComboboxModel(blink::mojom::MediaStreamType type)
     : type_(type) {
-  DCHECK(type_ == blink::MEDIA_DEVICE_AUDIO_CAPTURE ||
-         type_ == blink::MEDIA_DEVICE_VIDEO_CAPTURE);
+  DCHECK(type_ == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE ||
+         type_ == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
 }
 
 MediaComboboxModel::~MediaComboboxModel() {}
@@ -162,7 +162,7 @@
 const blink::MediaStreamDevices& MediaComboboxModel::GetDevices() const {
   MediaCaptureDevicesDispatcher* dispatcher =
       MediaCaptureDevicesDispatcher::GetInstance();
-  return type_ == blink::MEDIA_DEVICE_AUDIO_CAPTURE
+  return type_ == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE
              ? dispatcher->GetAudioCaptureDevices()
              : dispatcher->GetVideoCaptureDevices();
 }
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index 4d98d30..27c3d4a 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -708,6 +708,8 @@
   } else if (browser_view()->IsBrowserTypeHostedApp()) {
     active_color = browser_view()->browser()->app_controller()->GetThemeColor();
   } else if (!browser_view()->browser()->is_app()) {
+    // TODO(crbug.com/836128): Remove when System Web Apps flag is removed, as
+    // the above Hosted App branch will render the theme color.
     active_color =
         base::FeatureList::IsEnabled(chromeos::features::kSplitSettings)
             ? gfx::kGoogleGrey050
diff --git a/chrome/browser/ui/views/layout_provider_unittest.cc b/chrome/browser/ui/views/layout_provider_unittest.cc
index fcf1651..2f79ca9e 100644
--- a/chrome/browser/ui/views/layout_provider_unittest.cc
+++ b/chrome/browser/ui/views/layout_provider_unittest.cc
@@ -245,8 +245,7 @@
 #if defined(OS_MACOSX)
   EXPECT_EQ(25, headline_font.GetHeight());
 #elif defined(OS_WIN)
-  EXPECT_EQ(ChromeTypographyProvider::GetPlatformFontHeight(CONTEXT_HEADLINE),
-            headline_font.GetHeight());
+  EXPECT_EQ(27, headline_font.GetHeight());
 #else
   EXPECT_EQ(24, headline_font.GetHeight());
 #endif
@@ -268,9 +267,7 @@
 #if defined(OS_MACOSX)
   EXPECT_EQ(16, body1_font.GetHeight());  // Add 4.
 #elif defined(OS_WIN)
-  EXPECT_EQ(
-      ChromeTypographyProvider::GetPlatformFontHeight(CONTEXT_BODY_TEXT_LARGE),
-      body1_font.GetHeight());
+  EXPECT_EQ(18, body1_font.GetHeight());
 #else  // Linux.
   EXPECT_EQ(17, body1_font.GetHeight());  // Add 3.
 #endif
@@ -279,9 +276,7 @@
 
 // Body2 font leading should be 20.
 #if defined(OS_WIN)
-  EXPECT_EQ(
-      ChromeTypographyProvider::GetPlatformFontHeight(CONTEXT_BODY_TEXT_SMALL),
-      body2_font.GetHeight());
+  EXPECT_EQ(16, body2_font.GetHeight());
 #else
   EXPECT_EQ(15, body2_font.GetHeight());  // Other platforms: Add 5.
 #endif
@@ -290,9 +285,7 @@
 
 // Button leading not specified (shouldn't be needed: no multiline buttons).
 #if defined(OS_WIN)
-  EXPECT_EQ(
-      ChromeTypographyProvider::GetPlatformFontHeight(CONTEXT_BODY_TEXT_SMALL),
-      button_font.GetHeight());
+  EXPECT_EQ(16, button_font.GetHeight());
 #else
   EXPECT_EQ(15, button_font.GetHeight());
 #endif
diff --git a/chrome/browser/ui/views/payments/payment_request_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_browsertest.cc
index 67b6cc1..976f17e0 100644
--- a/chrome/browser/ui/views/payments/payment_request_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_browsertest.cc
@@ -6,6 +6,7 @@
 
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/browser/ui/views/payments/payment_request_browsertest_base.h"
@@ -24,6 +25,11 @@
 #include "ui/views/controls/styled_label.h"
 #include "url/gurl.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/web_applications/system_web_app_manager.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#endif
+
 namespace payments {
 
 using ::testing::UnorderedElementsAre;
@@ -400,6 +406,13 @@
 
 // Tests that clicking the settings link brings the user to settings.
 IN_PROC_BROWSER_TEST_F(PaymentRequestSettingsLinkTest, ClickSettingsLink) {
+#if defined(OS_CHROMEOS)
+  // Install the Settings App.
+  web_app::WebAppProvider::Get(browser()->profile())
+      ->system_web_app_manager()
+      .InstallSystemAppsForTesting();
+#endif
+
   NavigateTo("/payment_request_no_shipping_test.html");
   // Setup a credit card with an associated billing address.
   autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_chromeos.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_chromeos.cc
index d4329d1..f01d4fa 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_chromeos.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_chromeos.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_chromeos.h"
 
-#include "ash/public/interfaces/update.mojom.h"
+#include "ash/public/cpp/update_types.h"
 #include "base/bind.h"
 #include "chrome/browser/ui/ash/system_tray_client.h"
 #include "chrome/browser/ui/views/relaunch_notification/relaunch_notification_metrics.h"
@@ -52,8 +52,7 @@
 
 void RelaunchNotificationControllerPlatformImpl::CloseRelaunchNotification() {
   SystemTrayClient::Get()->SetUpdateNotificationState(
-      ash::mojom::NotificationStyle::DEFAULT, base::string16(),
-      base::string16());
+      ash::NotificationStyle::kDefault, base::string16(), base::string16());
   recorded_shown_ = false;
   relaunch_required_timer_.reset();
 }
@@ -67,12 +66,12 @@
     RefreshRelaunchRecommendedTitle(bool past_deadline) {
   if (past_deadline) {
     SystemTrayClient::Get()->SetUpdateNotificationState(
-        ash::mojom::NotificationStyle::ADMIN_RECOMMENDED,
+        ash::NotificationStyle::kAdminRecommended,
         l10n_util::GetStringUTF16(IDS_RELAUNCH_RECOMMENDED_OVERDUE_TITLE),
         l10n_util::GetStringUTF16(IDS_RELAUNCH_RECOMMENDED_OVERDUE_BODY));
   } else {
     SystemTrayClient::Get()->SetUpdateNotificationState(
-        ash::mojom::NotificationStyle::ADMIN_RECOMMENDED,
+        ash::NotificationStyle::kAdminRecommended,
         l10n_util::GetStringUTF16(IDS_RELAUNCH_RECOMMENDED_TITLE),
         l10n_util::GetStringUTF16(IDS_RELAUNCH_RECOMMENDED_BODY));
   }
@@ -81,7 +80,7 @@
 void RelaunchNotificationControllerPlatformImpl::
     RefreshRelaunchRequiredTitle() {
   SystemTrayClient::Get()->SetUpdateNotificationState(
-      ash::mojom::NotificationStyle::ADMIN_REQUIRED,
+      ash::NotificationStyle::kAdminRequired,
       relaunch_required_timer_->GetWindowTitle(),
       l10n_util::GetStringUTF16(IDS_RELAUNCH_REQUIRED_BODY));
 }
diff --git a/chrome/browser/ui/views/tabs/DEPS b/chrome/browser/ui/views/tabs/DEPS
index fa42a14a..74eaac5 100644
--- a/chrome/browser/ui/views/tabs/DEPS
+++ b/chrome/browser/ui/views/tabs/DEPS
@@ -1,9 +1,5 @@
 specific_include_rules = {
   # mash-ok
-  "window_finder_chromeos\.cc": [
-    "+ash/wm/window_finder.h",
-  ],
-  # mash-ok
   "tab_drag_controller_interactive_uitest\.cc": [
     "+ash/shell.h",
     "+ash/wm/splitview/split_view_controller.h",
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index fde2737..d55a70a1 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -485,8 +485,7 @@
   if (event_source == EVENT_SOURCE_TOUCH)
     SetCapture(source_context_);
 
-  window_finder_ = WindowFinder::Create(
-      event_source, source_context->AsView()->GetWidget()->GetNativeWindow());
+  window_finder_ = std::make_unique<WindowFinder>();
 }
 
 // static
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index c1eb48a81..d8dcf65 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -1001,14 +1001,14 @@
   TabAnimationState::TabPinnedness pinnedness =
       pinned ? TabAnimationState::TabPinnedness::kPinned
              : TabAnimationState::TabPinnedness::kUnpinned;
-  animator_->InsertTabAtNoAnimation(
-      model_index,
-      base::BindOnce(&TabStrip::OnTabCloseAnimationCompleted,
-                     base::Unretained(this), base::Unretained(tab)),
-      activeness, pinnedness);
   if (tab_count() > 1 && GetWidget() && GetWidget()->IsVisible()) {
-    StartInsertTabAnimation(model_index);
+    StartInsertTabAnimation(model_index, activeness, pinnedness);
   } else {
+    animator_->InsertTabAtNoAnimation(
+        model_index,
+        base::BindOnce(&TabStrip::OnTabCloseAnimationCompleted,
+                       base::Unretained(this), base::Unretained(tab)),
+        activeness, pinnedness);
     CompleteAnimationAndLayout();
   }
 
@@ -2081,30 +2081,51 @@
     bounds_animator_.SetAnimationDuration(0);
 }
 
-void TabStrip::StartInsertTabAnimation(int model_index) {
-  PrepareForAnimation();
+void TabStrip::StartInsertTabAnimation(
+    int model_index,
+    TabAnimationState::TabActiveness activeness,
+    TabAnimationState::TabPinnedness pinnedness) {
+  if (!bounds_animator_.IsAnimating() && !in_tab_close_) {
+    animator_->InsertTabAt(
+        model_index,
+        base::BindOnce(&TabStrip::OnTabCloseAnimationCompleted,
+                       base::Unretained(this),
+                       base::Unretained(tab_at(model_index))),
+        activeness, pinnedness);
+  } else {
+    // TODO(958173): Delete this branch once |animator_| has taken over all
+    // animation responsibilities.
+    animator_->InsertTabAtNoAnimation(
+        model_index,
+        base::BindOnce(&TabStrip::OnTabCloseAnimationCompleted,
+                       base::Unretained(this),
+                       base::Unretained(tab_at(model_index))),
+        activeness, pinnedness);
 
-  // The TabStrip can now use its entire width to lay out Tabs.
-  in_tab_close_ = false;
-  available_width_for_tabs_ = -1;
+    PrepareForAnimation();
 
-  UpdateIdealBounds();
+    // The TabStrip can now use its entire width to lay out Tabs.
+    in_tab_close_ = false;
+    available_width_for_tabs_ = -1;
 
-  // Insert the tab just after the current right edge of the previous tab, if
-  // any.
-  gfx::Rect bounds = ideal_bounds(model_index);
-  const int tab_overlap = TabStyle::GetTabOverlap();
-  if (model_index > 0)
-    bounds.set_x(tab_at(model_index - 1)->bounds().right() - tab_overlap);
+    UpdateIdealBounds();
 
-  // Start at the width of the overlap in order to animate at the same speed the
-  // surrounding tabs are moving, since at this width the subsequent tab is
-  // naturally positioned at the same X coordinate.
-  bounds.set_width(tab_overlap);
+    // Insert the tab just after the current right edge of the previous tab, if
+    // any.
+    gfx::Rect bounds = ideal_bounds(model_index);
+    const int tab_overlap = TabStyle::GetTabOverlap();
+    if (model_index > 0)
+      bounds.set_x(tab_at(model_index - 1)->bounds().right() - tab_overlap);
 
-  // Animate in to the full width.
-  tab_at(model_index)->SetBoundsRect(bounds);
-  AnimateToIdealBounds();
+    // Start at the width of the overlap in order to animate at the same speed
+    // the surrounding tabs are moving, since at this width the subsequent tab
+    // is naturally positioned at the same X coordinate.
+    bounds.set_width(tab_overlap);
+
+    // Animate in to the full width.
+    tab_at(model_index)->SetBoundsRect(bounds);
+    AnimateToIdealBounds();
+  }
 }
 
 void TabStrip::StartMoveTabAnimation() {
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index db8a787..478519e 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -336,7 +336,9 @@
   void Init();
 
   // Invoked from |AddTabAt| after the newly created tab has been inserted.
-  void StartInsertTabAnimation(int model_index);
+  void StartInsertTabAnimation(int model_index,
+                               TabAnimationState::TabActiveness activeness,
+                               TabAnimationState::TabPinnedness pinnedness);
 
   // Invoked from |MoveTab| after |tab_data_| has been updated to animate the
   // move.
diff --git a/chrome/browser/ui/views/tabs/tab_strip_animator.cc b/chrome/browser/ui/views/tabs/tab_strip_animator.cc
index 39173902..334ca3f 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_animator.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_animator.cc
@@ -81,18 +81,16 @@
 
 void TabStripAnimator::SetActiveTab(int prev_active_index,
                                     int new_active_index) {
-  // Set activeness without animating by immediately completing animations.
+  // Set activeness without animating by retargeting the existing animation.
   if (prev_active_index >= 0) {
-    animations_[prev_active_index].AnimateTo(
+    animations_[prev_active_index].RetargetTo(
         animations_[prev_active_index].target_state().WithActiveness(
             TabAnimationState::TabActiveness::kInactive));
-    animations_[prev_active_index].CompleteAnimation();
   }
   if (new_active_index >= 0) {
-    animations_[new_active_index].AnimateTo(
+    animations_[new_active_index].RetargetTo(
         animations_[new_active_index].target_state().WithActiveness(
             TabAnimationState::TabActiveness::kActive));
-    animations_[new_active_index].CompleteAnimation();
   }
 }
 
diff --git a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
index 5e20097..65827b3 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
@@ -794,8 +794,11 @@
 
   // Create a lot of tabs in order to make inactive tabs tiny.
   const int min_inactive_width = TabStyleViews::GetMinimumInactiveWidth();
-  while (GetInactiveTabWidth() != min_inactive_width)
+  while (GetInactiveTabWidth() != min_inactive_width) {
     controller_->CreateNewTab();
+    CompleteAnimationAndLayout();
+  }
+
   EXPECT_GT(tab_strip_->tab_count(), 1);
 
   const int active_index = controller_->GetActiveIndex();
@@ -826,6 +829,7 @@
   const int min_active_width = TabStyleViews::GetMinimumActiveWidth();
   while (GetInactiveTabWidth() >= (min_inactive_width + min_active_width) / 2) {
     controller_->CreateNewTab();
+    CompleteAnimationAndLayout();
   }
 
   // During mouse-based tab closure, inactive tabs shouldn't shrink
diff --git a/chrome/browser/ui/views/tabs/tab_style_views.cc b/chrome/browser/ui/views/tabs/tab_style_views.cc
index 95bb3d37..c77952d 100644
--- a/chrome/browser/ui/views/tabs/tab_style_views.cc
+++ b/chrome/browser/ui/views/tabs/tab_style_views.cc
@@ -612,7 +612,8 @@
   const float range_start = float{GetStandardWidth()};
   const float range_end = float{GetMinimumInactiveWidth()};
   const float value_in_range = float{tab_->width()};
-  const float t = (value_in_range - range_start) / (range_end - range_start);
+  const float t = base::ClampToRange(
+      (value_in_range - range_start) / (range_end - range_start), 0.0f, 1.0f);
   return tab_->controller()->GetHoverOpacityForTab(t * t);
 }
 
diff --git a/chrome/browser/ui/views/tabs/window_finder.cc b/chrome/browser/ui/views/tabs/window_finder.cc
deleted file mode 100644
index a54636c..0000000
--- a/chrome/browser/ui/views/tabs/window_finder.cc
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/tabs/window_finder.h"
-
-#include "base/memory/ptr_util.h"
-
-// static
-std::unique_ptr<WindowFinder> WindowFinder::Create(
-    TabDragController::EventSource source,
-    gfx::NativeWindow window) {
-  return base::WrapUnique(new WindowFinder());
-}
diff --git a/chrome/browser/ui/views/tabs/window_finder.h b/chrome/browser/ui/views/tabs/window_finder.h
index 6d9ae96c..1e25b1381 100644
--- a/chrome/browser/ui/views/tabs/window_finder.h
+++ b/chrome/browser/ui/views/tabs/window_finder.h
@@ -5,10 +5,9 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_TABS_WINDOW_FINDER_H_
 #define CHROME_BROWSER_UI_VIEWS_TABS_WINDOW_FINDER_H_
 
-#include <memory>
 #include <set>
 
-#include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
+#include "base/macros.h"
 #include "ui/gfx/native_widget_types.h"
 
 namespace gfx {
@@ -18,13 +17,9 @@
 // Class used by the tabstrip to find chrome windows that we can attach tabs to.
 class WindowFinder {
  public:
+  WindowFinder() = default;
   virtual ~WindowFinder() = default;
 
-  // Creates the default implementation of WindowFinder.
-  static std::unique_ptr<WindowFinder> Create(
-      TabDragController::EventSource source,
-      gfx::NativeWindow window);
-
   // Finds the topmost visible chrome window at |screen_point|. This should
   // return nullptr if |screen_point| is in another program's window which
   // occludes the topmost chrome window. Ignores the windows in |ignore|, which
@@ -33,9 +28,6 @@
       const gfx::Point& screen_point,
       const std::set<gfx::NativeWindow>& ignore);
 
- protected:
-  WindowFinder() = default;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(WindowFinder);
 };
diff --git a/chrome/browser/ui/views/tabs/window_finder_chromeos.cc b/chrome/browser/ui/views/tabs/window_finder_chromeos.cc
index d52f0267..41b6284 100644
--- a/chrome/browser/ui/views/tabs/window_finder_chromeos.cc
+++ b/chrome/browser/ui/views/tabs/window_finder_chromeos.cc
@@ -4,38 +4,10 @@
 
 #include "chrome/browser/ui/views/tabs/window_finder.h"
 
-#include "ash/wm/window_finder.h"
-#include "ui/aura/window.h"
-
-namespace {
-
-// The class to be used by ash to find an eligible chrome window that we can
-// attach the dragged tabs into.
-class AshWindowFinder : public WindowFinder {
- public:
-  AshWindowFinder() = default;
-
-  gfx::NativeWindow GetLocalProcessWindowAtPoint(
-      const gfx::Point& screen_point,
-      const std::set<gfx::NativeWindow>& ignore) override {
-    return ash::wm::GetTopmostWindowAtPoint(screen_point, ignore, nullptr);
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AshWindowFinder);
-};
-
-}  // namespace
-
-std::unique_ptr<WindowFinder> WindowFinder::Create(
-    TabDragController::EventSource source,
-    gfx::NativeWindow window) {
-  return std::make_unique<AshWindowFinder>();
-}
+#include "ash/public/cpp/window_finder.h"
 
 gfx::NativeWindow WindowFinder::GetLocalProcessWindowAtPoint(
     const gfx::Point& screen_point,
     const std::set<gfx::NativeWindow>& ignore) {
-  NOTREACHED();
-  return nullptr;
+  return ash::GetTopmostWindowAtPoint(screen_point, ignore, nullptr);
 }
diff --git a/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
index ae12b90..75ff54b 100644
--- a/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
@@ -207,8 +207,15 @@
 }
 
 void AssistantOptInFlowScreenHandler::SetupAssistantConnection() {
-  // Make sure enable Assistant service since we need it during the flow.
   PrefService* prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
+
+  // If Assistant is disabled by domain admin, end the flow.
+  if (prefs->GetBoolean(::assistant::prefs::kAssistantDisabledByPolicy)) {
+    HandleFlowFinished();
+    return;
+  }
+
+  // Make sure enable Assistant service since we need it during the flow.
   prefs->SetBoolean(arc::prefs::kVoiceInteractionEnabled, true);
 
   if (arc::VoiceInteractionControllerClient::Get()->voice_interaction_state() ==
@@ -329,10 +336,19 @@
 
   if (settings_ui.has_gaia_user_context_ui()) {
     auto gaia_user_context_ui = settings_ui.gaia_user_context_ui();
-    if (gaia_user_context_ui.waa_disabled_by_dasher_domain() ||
-        gaia_user_context_ui.assistant_disabled_by_dasher_domain()) {
-      DVLOG(1) << "Assistant/web & app activity is disabled by dasher domain. "
-                  "Skip Assistant opt-in flow.";
+    if (gaia_user_context_ui.assistant_disabled_by_dasher_domain()) {
+      DVLOG(1) << "Assistant is disabled by domain policy. Skip Assistant "
+                  "opt-in flow.";
+      PrefService* prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
+      prefs->SetBoolean(::assistant::prefs::kAssistantDisabledByPolicy, true);
+      prefs->SetBoolean(arc::prefs::kVoiceInteractionEnabled, false);
+      HandleFlowFinished();
+      return;
+    }
+
+    if (gaia_user_context_ui.waa_disabled_by_dasher_domain()) {
+      DVLOG(1) << "Web & app activity is disabled by domain policy. Skip "
+                  "Assistant opt-in flow.";
       HandleFlowFinished();
       return;
     }
@@ -579,6 +595,12 @@
 
 void AssistantOptInFlowScreenHandler::HandleFlowInitialized(
     const int flow_type) {
+  auto* prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
+  if (!prefs->GetBoolean(arc::prefs::kVoiceInteractionEnabled)) {
+    HandleFlowFinished();
+    return;
+  }
+
   initialized_ = true;
 
   if (on_initialized_)
diff --git a/chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.cc b/chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.cc
index 4728bfe..0779b397 100644
--- a/chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.cc
@@ -122,9 +122,11 @@
 }
 
 void SmbHandler::HandleGatherSharesResponse(
-    const std::vector<smb_client::SmbUrl>& shares_gathered) {
+    const std::vector<smb_client::SmbUrl>& shares_gathered,
+    bool done) {
   AllowJavascript();
-  FireWebUIListener("on-shares-found", BuildShareList(shares_gathered));
+  FireWebUIListener("on-shares-found", BuildShareList(shares_gathered),
+                    base::Value(done));
 }
 
 void SmbHandler::HandleUpdateCredentials(const base::ListValue* args) {
diff --git a/chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.h b/chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.h
index 493b9ba3..38354b5 100644
--- a/chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.h
+++ b/chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.h
@@ -43,7 +43,8 @@
 
   // Callback handler for StartDiscovery.
   void HandleGatherSharesResponse(
-      const std::vector<smb_client::SmbUrl>& shares_gathered);
+      const std::vector<smb_client::SmbUrl>& shares_gathered,
+      bool done);
 
   // Callback handler that indicates discovery is complete.
   void HandleDiscoveryDone();
diff --git a/chrome/browser/ui/webui/cookies_tree_model_util.cc b/chrome/browser/ui/webui/cookies_tree_model_util.cc
index 9c411e46..495e6b7 100644
--- a/chrome/browser/ui/webui/cookies_tree_model_util.cc
+++ b/chrome/browser/ui/webui/cookies_tree_model_util.cc
@@ -324,13 +324,13 @@
 }
 
 void CookiesTreeModelUtil::GetChildNodeList(const CookieTreeNode* parent,
-                                            int start,
-                                            int count,
+                                            size_t start,
+                                            size_t count,
                                             bool include_quota_nodes,
                                             base::ListValue* nodes) {
-  for (int i = 0; i < count; ++i) {
+  for (size_t i = 0; i < count; ++i) {
     std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
-    const CookieTreeNode* child = parent->GetChild(start + i);
+    const CookieTreeNode* child = parent->children()[start + i].get();
     if (GetCookieTreeNodeDictionary(*child, include_quota_nodes, dict.get()))
       nodes->Append(std::move(dict));
   }
diff --git a/chrome/browser/ui/webui/cookies_tree_model_util.h b/chrome/browser/ui/webui/cookies_tree_model_util.h
index 02c30f4..bac87974 100644
--- a/chrome/browser/ui/webui/cookies_tree_model_util.h
+++ b/chrome/browser/ui/webui/cookies_tree_model_util.h
@@ -38,8 +38,8 @@
 
   // Append the children nodes of |parent| in specified range to |nodes| list.
   void GetChildNodeList(const CookieTreeNode* parent,
-                        int start,
-                        int count,
+                        size_t start,
+                        size_t count,
                         bool include_quota_nodes,
                         base::ListValue* nodes);
 
diff --git a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
index 0ca2cad8..fe9a0f7 100644
--- a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
@@ -17,9 +17,11 @@
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/browser/local_discovery/test_service_discovery_client.h"
+#include "chrome/browser/media/router/media_router_feature.h"
 #include "chrome/browser/media/router/providers/cast/dual_media_sink_service.h"
 #include "chrome/browser/media/router/test/noop_dual_media_sink_service.h"
 #include "chrome/browser/profiles/profile.h"
@@ -347,6 +349,7 @@
     // a DCHECK during TestServiceDiscoveryClient construction.
     media_router::DualMediaSinkService::SetInstanceForTest(
         new media_router::NoopDualMediaSinkService());
+    feature_list_.InitAndDisableFeature(media_router::kDialMediaRouteProvider);
     WebUIBrowserTest::SetUp();
   }
 
@@ -444,6 +447,7 @@
   network::TestURLLoaderFactory test_url_loader_factory_;
   local_discovery::LocalDiscoveryUIHandler::SetURLLoaderFactoryForTesting
       set_url_loader_factory_;
+  base::test::ScopedFeatureList feature_list_;
 
   DISALLOW_COPY_AND_ASSIGN(LocalDiscoveryUITest);
 };
diff --git a/chrome/browser/ui/webui/management_ui_browsertest.cc b/chrome/browser/ui/webui/management_ui_browsertest.cc
index e921d9e..8cd3f96 100644
--- a/chrome/browser/ui/webui/management_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/management_ui_browsertest.cc
@@ -81,9 +81,6 @@
        l10n_util::GetStringUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED)},
       {"pageSubtitle",
        l10n_util::GetStringUTF16(IDS_MANAGEMENT_NOT_MANAGED_SUBTITLE)},
-      {"accountManagedInfo.overview", base::string16()},
-      {"accountManagedInfo.data", base::string16()},
-      {"accountManagedInfo.setup", base::string16()},
   };
 
   VerifyTexts(unmanaged_value_ptr.get(), expected_unmanaged_values);
@@ -114,13 +111,6 @@
       {"extensionReportingTitle",
        l10n_util::GetStringUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED)},
       {"pageSubtitle", l10n_util::GetStringUTF16(IDS_MANAGEMENT_SUBTITLE)},
-      {"accountManagedInfo.overview",
-       l10n_util::GetStringUTF16(
-           IDS_MANAGEMENT_ACCOUNT_MANAGED_CLARIFICATION_UNKNOWN_DOMAIN)},
-      {"accountManagedInfo.data",
-       l10n_util::GetStringUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_DATA)},
-      {"accountManagedInfo.setup",
-       l10n_util::GetStringUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_SETUP)},
   };
 
   VerifyTexts(managed_value_ptr.get(), expected_managed_values);
diff --git a/chrome/browser/ui/webui/management_ui_handler.cc b/chrome/browser/ui/webui/management_ui_handler.cc
index 7901fd8e..e6cdb78a 100644
--- a/chrome/browser/ui/webui/management_ui_handler.cc
+++ b/chrome/browser/ui/webui/management_ui_handler.cc
@@ -119,13 +119,11 @@
     "managementReportNetworkInterfaces";
 const char kManagementReportUsers[] = "managementReportUsers";
 const char kManagementPrinting[] = "managementPrinting";
+const char kAccountManagedInfo[] = "accountManagedInfo";
 const char kDeviceManagedInfo[] = "deviceManagedInfo";
+const char kOverview[] = "overview";
 #endif  // defined(OS_CHROMEOS)
 
-const char kOverview[] = "overview";
-const char kAccountManagedInfo[] = "accountManagedInfo";
-const char kSetup[] = "setup";
-const char kData[] = "data";
 const char kCustomerLogo[] = "customerLogo";
 
 namespace {
@@ -625,26 +623,6 @@
   FireWebUIListener("managed_data_changed");
 }
 
-void AddStatusAccountManagedInfo(base::Value* status,
-                                 const std::string& account_domain) {
-  base::Value info(base::Value::Type::DICTIONARY);
-  if (account_domain.empty()) {
-    info.SetKey(
-        kOverview,
-        base::Value(l10n_util::GetStringUTF16(
-            IDS_MANAGEMENT_ACCOUNT_MANAGED_CLARIFICATION_UNKNOWN_DOMAIN)));
-  } else {
-    info.SetKey(kOverview, base::Value(l10n_util::GetStringFUTF16(
-                               IDS_MANAGEMENT_ACCOUNT_MANAGED_CLARIFICATION,
-                               base::UTF8ToUTF16(account_domain))));
-  }
-  info.SetKey(kSetup, base::Value(l10n_util::GetStringUTF16(
-                          IDS_MANAGEMENT_ACCOUNT_MANAGED_SETUP)));
-  info.SetKey(kData, base::Value(l10n_util::GetStringUTF16(
-                         IDS_MANAGEMENT_ACCOUNT_MANAGED_DATA)));
-  status->SetKey(kAccountManagedInfo, std::move(info));
-}
-
 #if defined(OS_CHROMEOS)
 void AddStatusOverviewManagedDeviceAndAccount(
     base::Value* status,
@@ -657,40 +635,14 @@
     status->SetKey(kOverview, base::Value(l10n_util::GetStringFUTF16(
                                   IDS_MANAGEMENT_DEVICE_AND_ACCOUNT_MANAGED_BY,
                                   base::UTF8ToUTF16(device_domain))));
-    base::Value info(base::Value::Type::DICTIONARY);
-    info.SetKey(kOverview,
-                base::Value(l10n_util::GetStringFUTF16(
-                    IDS_MANAGEMENT_DEVICE_AND_ACCOUNT_MANAGED_CLARIFICATION,
-                    base::UTF8ToUTF16(device_domain))));
-    info.SetKey(kSetup, base::Value(l10n_util::GetStringUTF16(
-                            IDS_MANAGEMENT_DEVICE_AND_ACCOUNT_MANAGED_SETUP)));
-    info.SetKey(kData, base::Value(l10n_util::GetStringUTF16(
-                           IDS_MANAGEMENT_DEVICE_AND_ACCOUNT_MANAGED_DATA)));
-    status->SetKey(kDeviceManagedInfo, std::move(info));
 
     return;
   }
 
-  if (account_managed) {
-    AddStatusAccountManagedInfo(
-        status, !account_domain.empty() ? account_domain : device_domain);
-    if (!account_domain.empty()) {
-      status->SetKey(kOverview, base::Value(l10n_util::GetStringFUTF16(
-                                    IDS_MANAGEMENT_ACCOUNT_MANAGED_BY,
-                                    base::UTF8ToUTF16(account_domain))));
-    }
-  }
-
-  if (device_managed) {
-    base::Value info(base::Value::Type::DICTIONARY);
-    info.SetKey(kOverview, base::Value(l10n_util::GetStringFUTF16(
-                               IDS_MANAGEMENT_DEVICE_MANAGED_CLARIFICATION,
-                               base::UTF8ToUTF16(device_domain))));
-    info.SetKey(kSetup, base::Value(l10n_util::GetStringUTF16(
-                            IDS_MANAGEMENT_DEVICE_MANAGED_SETUP)));
-    info.SetKey(kData, base::Value(l10n_util::GetStringUTF16(
-                           IDS_MANAGEMENT_DEVICE_MANAGED_DATA)));
-    status->SetKey(kDeviceManagedInfo, std::move(info));
+  if (account_managed && !account_domain.empty()) {
+    status->SetKey(kOverview, base::Value(l10n_util::GetStringFUTF16(
+                                  IDS_MANAGEMENT_ACCOUNT_MANAGED_BY,
+                                  base::UTF8ToUTF16(account_domain))));
   }
 
   if (account_managed && device_managed && !account_domain.empty() &&
@@ -744,9 +696,6 @@
   AddStatusOverviewManagedDeviceAndAccount(
       status, device_managed_, account_managed_ || primary_user_managed,
       device_domain, account_domain);
-#else
-  if (managed_())
-    AddStatusAccountManagedInfo(status, GetAccountDomain(profile));
 #endif  // defined(OS_CHROMEOS)
 }
 
diff --git a/chrome/browser/ui/webui/management_ui_handler_unittest.cc b/chrome/browser/ui/webui/management_ui_handler_unittest.cc
index 83ed53f7..c118fd3 100644
--- a/chrome/browser/ui/webui/management_ui_handler_unittest.cc
+++ b/chrome/browser/ui/webui/management_ui_handler_unittest.cc
@@ -35,13 +35,7 @@
 struct ContextualManagementSourceUpdate {
   base::string16* extension_reporting_title;
   base::string16* subtitle;
-  base::string16* management_account_overview;
-  base::string16* management_account_overview_data_notice;
-  base::string16* management_account_overview_setup_notice;
 #if defined(OS_CHROMEOS)
-  base::string16* management_device_overview;
-  base::string16* management_device_overview_data_notice;
-  base::string16* management_device_overview_setup_notice;
   base::string16* management_overview;
 #else
   base::string16* browser_management_notice;
@@ -114,19 +108,7 @@
     data.GetString("extensionReportingTitle",
                    extracted.extension_reporting_title);
     data.GetString("pageSubtitle", extracted.subtitle);
-    data.GetString("accountManagedInfo.overview",
-                   extracted.management_account_overview);
-    data.GetString("accountManagedInfo.data",
-                   extracted.management_account_overview_data_notice);
-    data.GetString("accountManagedInfo.setup",
-                   extracted.management_account_overview_setup_notice);
 #if defined(OS_CHROMEOS)
-    data.GetString("deviceManagedInfo.overview",
-                   extracted.management_device_overview);
-    data.GetString("deviceManagedInfo.data",
-                   extracted.management_device_overview_data_notice);
-    data.GetString("deviceManagedInfo.setup",
-                   extracted.management_device_overview_setup_notice);
     data.GetString("overview", extracted.management_overview);
 #else
     data.GetString("browserManagementNotice",
@@ -150,17 +132,11 @@
 
   base::string16 extension_reporting_title;
   base::string16 subtitle;
-  base::string16 management_account_overview;
-  base::string16 management_account_overview_data_notice;
-  base::string16 management_account_overview_setup_notice;
   base::string16 browser_management_notice;
   bool managed;
   ContextualManagementSourceUpdate extracted{
       &extension_reporting_title,
       &subtitle,
-      &management_account_overview,
-      &management_account_overview_data_notice,
-      &management_account_overview_setup_notice,
       &browser_management_notice,
       &managed};
 
@@ -177,9 +153,6 @@
                 base::UTF8ToUTF16(chrome::kManagedUiLearnMoreUrl)));
   EXPECT_EQ(subtitle,
             l10n_util::GetStringUTF16(IDS_MANAGEMENT_NOT_MANAGED_SUBTITLE));
-  EXPECT_EQ(management_account_overview, base::string16());
-  EXPECT_EQ(management_account_overview_data_notice, base::string16());
-  EXPECT_EQ(management_account_overview_setup_notice, base::string16());
 }
 
 TEST_F(ManagementUIHandlerTests,
@@ -188,17 +161,11 @@
 
   base::string16 extension_reporting_title;
   base::string16 subtitle;
-  base::string16 management_account_overview;
-  base::string16 management_account_overview_data_notice;
-  base::string16 management_account_overview_setup_notice;
   base::string16 browser_management_notice;
   bool managed;
   ContextualManagementSourceUpdate extracted{
       &extension_reporting_title,
       &subtitle,
-      &management_account_overview,
-      &management_account_overview_data_notice,
-      &management_account_overview_setup_notice,
       &browser_management_notice,
       &managed};
 
@@ -206,7 +173,7 @@
   auto data = handler_.GetContextualManagedDataForTesting(profile.get());
   ExtractContextualSourceUpdate(data, extracted);
 
-  EXPECT_EQ(data.DictSize(), 5u);
+  EXPECT_EQ(data.DictSize(), 4u);
   EXPECT_EQ(extension_reporting_title,
             l10n_util::GetStringUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED));
   EXPECT_EQ(browser_management_notice,
@@ -214,13 +181,6 @@
                 IDS_MANAGEMENT_BROWSER_NOTICE,
                 base::UTF8ToUTF16(chrome::kManagedUiLearnMoreUrl)));
   EXPECT_EQ(subtitle, l10n_util::GetStringUTF16(IDS_MANAGEMENT_SUBTITLE));
-  EXPECT_EQ(management_account_overview,
-            l10n_util::GetStringUTF16(
-                IDS_MANAGEMENT_ACCOUNT_MANAGED_CLARIFICATION_UNKNOWN_DOMAIN));
-  EXPECT_EQ(management_account_overview_data_notice,
-            l10n_util::GetStringUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_DATA));
-  EXPECT_EQ(management_account_overview_setup_notice,
-            l10n_util::GetStringUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_SETUP));
   EXPECT_TRUE(managed);
 }
 
@@ -232,17 +192,11 @@
 
   base::string16 extensions_installed;
   base::string16 subtitle;
-  base::string16 management_account_overview;
-  base::string16 management_account_overview_data_notice;
-  base::string16 management_account_overview_setup_notice;
   base::string16 browser_management_notice;
   bool managed;
   ContextualManagementSourceUpdate extracted{
       &extensions_installed,
       &subtitle,
-      &management_account_overview,
-      &management_account_overview_data_notice,
-      &management_account_overview_setup_notice,
       &browser_management_notice,
       &managed};
 
@@ -250,7 +204,7 @@
   auto data = handler_.GetContextualManagedDataForTesting(profile.get());
   ExtractContextualSourceUpdate(data, extracted);
 
-  EXPECT_EQ(data.DictSize(), 5u);
+  EXPECT_EQ(data.DictSize(), 4u);
   EXPECT_EQ(extensions_installed,
             l10n_util::GetStringUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED));
   EXPECT_EQ(browser_management_notice,
@@ -258,13 +212,6 @@
                 IDS_MANAGEMENT_BROWSER_NOTICE,
                 base::UTF8ToUTF16(chrome::kManagedUiLearnMoreUrl)));
   EXPECT_EQ(subtitle, l10n_util::GetStringUTF16(IDS_MANAGEMENT_SUBTITLE));
-  EXPECT_EQ(management_account_overview,
-            l10n_util::GetStringUTF16(
-                IDS_MANAGEMENT_ACCOUNT_MANAGED_CLARIFICATION_UNKNOWN_DOMAIN));
-  EXPECT_EQ(management_account_overview_data_notice,
-            l10n_util::GetStringUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_DATA));
-  EXPECT_EQ(management_account_overview_setup_notice,
-            l10n_util::GetStringUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_SETUP));
   EXPECT_TRUE(managed);
 }
 
@@ -276,17 +223,11 @@
 
   base::string16 extension_reporting_title;
   base::string16 subtitle;
-  base::string16 management_account_overview;
-  base::string16 management_account_overview_data_notice;
-  base::string16 management_account_overview_setup_notice;
   base::string16 browser_management_notice;
   bool managed;
   ContextualManagementSourceUpdate extracted{
       &extension_reporting_title,
       &subtitle,
-      &management_account_overview,
-      &management_account_overview_data_notice,
-      &management_account_overview_setup_notice,
       &browser_management_notice,
       &managed};
 
@@ -305,9 +246,6 @@
                 base::UTF8ToUTF16(chrome::kManagedUiLearnMoreUrl)));
   EXPECT_EQ(subtitle,
             l10n_util::GetStringUTF16(IDS_MANAGEMENT_NOT_MANAGED_SUBTITLE));
-  EXPECT_EQ(management_account_overview, base::string16());
-  EXPECT_EQ(management_account_overview_data_notice, base::string16());
-  EXPECT_EQ(management_account_overview_setup_notice, base::string16());
   EXPECT_FALSE(managed);
 }
 
@@ -319,17 +257,11 @@
 
   base::string16 extension_reporting_title;
   base::string16 subtitle;
-  base::string16 management_account_overview;
-  base::string16 management_account_overview_data_notice;
-  base::string16 management_account_overview_setup_notice;
   base::string16 browser_management_notice;
   bool managed;
   ContextualManagementSourceUpdate extracted{
       &extension_reporting_title,
       &subtitle,
-      &management_account_overview,
-      &management_account_overview_data_notice,
-      &management_account_overview_setup_notice,
       &browser_management_notice,
       &managed};
 
@@ -347,9 +279,6 @@
                 base::UTF8ToUTF16(chrome::kManagedUiLearnMoreUrl)));
   EXPECT_EQ(subtitle,
             l10n_util::GetStringUTF16(IDS_MANAGEMENT_NOT_MANAGED_SUBTITLE));
-  EXPECT_EQ(management_account_overview, base::string16());
-  EXPECT_EQ(management_account_overview_data_notice, base::string16());
-  EXPECT_EQ(management_account_overview_setup_notice, base::string16());
   EXPECT_FALSE(managed);
 }
 
@@ -361,17 +290,11 @@
 
   base::string16 extension_reporting_title;
   base::string16 subtitle;
-  base::string16 management_account_overview;
-  base::string16 management_account_overview_data_notice;
-  base::string16 management_account_overview_setup_notice;
   base::string16 browser_management_notice;
   bool managed;
   ContextualManagementSourceUpdate extracted{
       &extension_reporting_title,
       &subtitle,
-      &management_account_overview,
-      &management_account_overview_data_notice,
-      &management_account_overview_setup_notice,
       &browser_management_notice,
       &managed};
 
@@ -380,7 +303,7 @@
   auto data = handler_.GetContextualManagedDataForTesting(profile.get());
   ExtractContextualSourceUpdate(data, extracted);
 
-  EXPECT_EQ(data.DictSize(), 5u);
+  EXPECT_EQ(data.DictSize(), 4u);
   EXPECT_EQ(extension_reporting_title,
             l10n_util::GetStringFUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED_BY,
                                        base::UTF8ToUTF16("manager.com")));
@@ -391,14 +314,6 @@
   EXPECT_EQ(subtitle,
             l10n_util::GetStringFUTF16(IDS_MANAGEMENT_SUBTITLE_MANAGED_BY,
                                        base::UTF8ToUTF16("manager.com")));
-  EXPECT_EQ(
-      management_account_overview,
-      l10n_util::GetStringFUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_CLARIFICATION,
-                                 base::UTF8ToUTF16("manager.com")));
-  EXPECT_EQ(management_account_overview_data_notice,
-            l10n_util::GetStringUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_DATA));
-  EXPECT_EQ(management_account_overview_setup_notice,
-            l10n_util::GetStringUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_SETUP));
   EXPECT_TRUE(managed);
 }
 
@@ -414,23 +329,11 @@
 
   base::string16 extension_reporting_title;
   base::string16 subtitle;
-  base::string16 management_account_overview;
-  base::string16 management_account_overview_data_notice;
-  base::string16 management_account_overview_setup_notice;
-  base::string16 management_device_overview;
-  base::string16 management_device_overview_data_notice;
-  base::string16 management_device_overview_setup_notice;
   base::string16 management_overview;
   bool managed;
   ContextualManagementSourceUpdate extracted{
       &extension_reporting_title,
       &subtitle,
-      &management_account_overview,
-      &management_account_overview_data_notice,
-      &management_account_overview_setup_notice,
-      &management_device_overview,
-      &management_device_overview_data_notice,
-      &management_device_overview_setup_notice,
       &management_overview,
       &managed};
 
@@ -447,17 +350,6 @@
             l10n_util::GetStringFUTF16(IDS_MANAGEMENT_SUBTITLE_MANAGED_BY,
                                        l10n_util::GetStringUTF16(device_type),
                                        base::UTF8ToUTF16("manager.com")));
-  EXPECT_EQ(
-      management_account_overview,
-      l10n_util::GetStringFUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_CLARIFICATION,
-                                 base::UTF8ToUTF16("manager.com")));
-  EXPECT_EQ(management_account_overview_data_notice,
-            l10n_util::GetStringUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_DATA));
-  EXPECT_EQ(management_account_overview_setup_notice,
-            l10n_util::GetStringUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_SETUP));
-  EXPECT_EQ(management_device_overview, base::string16());
-  EXPECT_EQ(management_device_overview_data_notice, base::string16());
-  EXPECT_EQ(management_device_overview_setup_notice, base::string16());
   EXPECT_EQ(management_overview,
             l10n_util::GetStringFUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_BY,
                                        base::UTF8ToUTF16("manager.com")));
@@ -472,23 +364,11 @@
 
   base::string16 extension_reporting_title;
   base::string16 subtitle;
-  base::string16 management_account_overview;
-  base::string16 management_account_overview_data_notice;
-  base::string16 management_account_overview_setup_notice;
-  base::string16 management_device_overview;
-  base::string16 management_device_overview_data_notice;
-  base::string16 management_device_overview_setup_notice;
   base::string16 management_overview;
   bool managed;
   ContextualManagementSourceUpdate extracted{
       &extension_reporting_title,
       &subtitle,
-      &management_account_overview,
-      &management_account_overview_data_notice,
-      &management_account_overview_setup_notice,
-      &management_device_overview,
-      &management_device_overview_data_notice,
-      &management_device_overview_setup_notice,
       &management_overview,
       &managed};
 
@@ -503,13 +383,6 @@
   EXPECT_EQ(subtitle,
             l10n_util::GetStringFUTF16(IDS_MANAGEMENT_SUBTITLE_MANAGED,
                                        l10n_util::GetStringUTF16(device_type)));
-  EXPECT_EQ(management_account_overview_data_notice,
-            l10n_util::GetStringUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_DATA));
-  EXPECT_EQ(management_account_overview_setup_notice,
-            l10n_util::GetStringUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_SETUP));
-  EXPECT_EQ(management_device_overview, base::string16());
-  EXPECT_EQ(management_device_overview_data_notice, base::string16());
-  EXPECT_EQ(management_device_overview_setup_notice, base::string16());
   EXPECT_EQ(management_overview, base::string16());
   EXPECT_TRUE(managed);
 }
@@ -523,23 +396,11 @@
 
   base::string16 extension_reporting_title;
   base::string16 subtitle;
-  base::string16 management_account_overview;
-  base::string16 management_account_overview_data_notice;
-  base::string16 management_account_overview_setup_notice;
-  base::string16 management_device_overview;
-  base::string16 management_device_overview_data_notice;
-  base::string16 management_device_overview_setup_notice;
   base::string16 management_overview;
   bool managed;
   ContextualManagementSourceUpdate extracted{
       &extension_reporting_title,
       &subtitle,
-      &management_account_overview,
-      &management_account_overview_data_notice,
-      &management_account_overview_setup_notice,
-      &management_device_overview,
-      &management_device_overview_data_notice,
-      &management_device_overview_setup_notice,
       &management_overview,
       &managed};
 
@@ -555,16 +416,6 @@
   EXPECT_EQ(extension_reporting_title,
             l10n_util::GetStringFUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED_BY,
                                        device_domain()));
-  EXPECT_EQ(management_account_overview, base::string16());
-  EXPECT_EQ(management_account_overview_data_notice, base::string16());
-  EXPECT_EQ(management_account_overview_setup_notice, base::string16());
-  EXPECT_EQ(management_device_overview,
-            l10n_util::GetStringFUTF16(
-                IDS_MANAGEMENT_DEVICE_MANAGED_CLARIFICATION, device_domain()));
-  EXPECT_EQ(management_device_overview_data_notice,
-            l10n_util::GetStringUTF16(IDS_MANAGEMENT_DEVICE_MANAGED_DATA));
-  EXPECT_EQ(management_device_overview_setup_notice,
-            l10n_util::GetStringUTF16(IDS_MANAGEMENT_DEVICE_MANAGED_SETUP));
   EXPECT_EQ(management_overview, base::string16());
   EXPECT_TRUE(managed);
 }
@@ -578,23 +429,11 @@
 
   base::string16 extension_reporting_title;
   base::string16 subtitle;
-  base::string16 management_account_overview;
-  base::string16 management_account_overview_data_notice;
-  base::string16 management_account_overview_setup_notice;
-  base::string16 management_device_overview;
-  base::string16 management_device_overview_data_notice;
-  base::string16 management_device_overview_setup_notice;
   base::string16 management_overview;
   bool managed;
   ContextualManagementSourceUpdate extracted{
       &extension_reporting_title,
       &subtitle,
-      &management_account_overview,
-      &management_account_overview_data_notice,
-      &management_account_overview_setup_notice,
-      &management_device_overview,
-      &management_device_overview_data_notice,
-      &management_device_overview_setup_notice,
       &management_overview,
       &managed};
 
@@ -610,19 +449,6 @@
   EXPECT_EQ(extension_reporting_title,
             l10n_util::GetStringFUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED_BY,
                                        device_domain()));
-  EXPECT_EQ(management_account_overview, base::string16());
-  EXPECT_EQ(management_account_overview_data_notice, base::string16());
-  EXPECT_EQ(management_account_overview_setup_notice, base::string16());
-  EXPECT_EQ(management_device_overview,
-            l10n_util::GetStringFUTF16(
-                IDS_MANAGEMENT_DEVICE_AND_ACCOUNT_MANAGED_CLARIFICATION,
-                device_domain()));
-  EXPECT_EQ(management_device_overview_data_notice,
-            l10n_util::GetStringUTF16(
-                IDS_MANAGEMENT_DEVICE_AND_ACCOUNT_MANAGED_DATA));
-  EXPECT_EQ(management_device_overview_setup_notice,
-            l10n_util::GetStringUTF16(
-                IDS_MANAGEMENT_DEVICE_AND_ACCOUNT_MANAGED_SETUP));
   EXPECT_EQ(management_overview,
             l10n_util::GetStringFUTF16(
                 IDS_MANAGEMENT_DEVICE_AND_ACCOUNT_MANAGED_BY, device_domain()));
@@ -638,23 +464,11 @@
 
   base::string16 extension_reporting_title;
   base::string16 subtitle;
-  base::string16 management_account_overview;
-  base::string16 management_account_overview_data_notice;
-  base::string16 management_account_overview_setup_notice;
-  base::string16 management_device_overview;
-  base::string16 management_device_overview_data_notice;
-  base::string16 management_device_overview_setup_notice;
   base::string16 management_overview;
   bool managed;
   ContextualManagementSourceUpdate extracted{
       &extension_reporting_title,
       &subtitle,
-      &management_account_overview,
-      &management_account_overview_data_notice,
-      &management_account_overview_setup_notice,
-      &management_device_overview,
-      &management_device_overview_data_notice,
-      &management_device_overview_setup_notice,
       &management_overview,
       &managed};
 
@@ -670,17 +484,6 @@
   EXPECT_EQ(extension_reporting_title,
             l10n_util::GetStringFUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED_BY,
                                        device_domain()));
-  EXPECT_EQ(management_account_overview_data_notice,
-            l10n_util::GetStringUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_DATA));
-  EXPECT_EQ(management_account_overview_setup_notice,
-            l10n_util::GetStringUTF16(IDS_MANAGEMENT_ACCOUNT_MANAGED_SETUP));
-  EXPECT_EQ(management_device_overview,
-            l10n_util::GetStringFUTF16(
-                IDS_MANAGEMENT_DEVICE_MANAGED_CLARIFICATION, device_domain()));
-  EXPECT_EQ(management_device_overview_data_notice,
-            l10n_util::GetStringUTF16(IDS_MANAGEMENT_DEVICE_MANAGED_DATA));
-  EXPECT_EQ(management_device_overview_setup_notice,
-            l10n_util::GetStringUTF16(IDS_MANAGEMENT_DEVICE_MANAGED_SETUP));
   EXPECT_EQ(management_overview,
             l10n_util::GetStringFUTF16(
                 IDS_MANAGEMENT_DEVICE_MANAGED_BY_ACCOUNT_MANAGED_BY,
@@ -694,23 +497,11 @@
 
   base::string16 extension_reporting_title;
   base::string16 subtitle;
-  base::string16 management_account_overview;
-  base::string16 management_account_overview_data_notice;
-  base::string16 management_account_overview_setup_notice;
-  base::string16 management_device_overview;
-  base::string16 management_device_overview_data_notice;
-  base::string16 management_device_overview_setup_notice;
   base::string16 management_overview;
   bool managed;
   ContextualManagementSourceUpdate extracted{
       &extension_reporting_title,
       &subtitle,
-      &management_account_overview,
-      &management_account_overview_data_notice,
-      &management_account_overview_setup_notice,
-      &management_device_overview,
-      &management_device_overview_data_notice,
-      &management_device_overview_setup_notice,
       &management_overview,
       &managed};
 
@@ -723,12 +514,6 @@
                                        l10n_util::GetStringUTF16(device_type)));
   EXPECT_EQ(extension_reporting_title,
             l10n_util::GetStringUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED));
-  EXPECT_EQ(management_account_overview, base::string16());
-  EXPECT_EQ(management_account_overview_data_notice, base::string16());
-  EXPECT_EQ(management_account_overview_setup_notice, base::string16());
-  EXPECT_EQ(management_device_overview, base::string16());
-  EXPECT_EQ(management_device_overview_data_notice, base::string16());
-  EXPECT_EQ(management_device_overview_setup_notice, base::string16());
   EXPECT_EQ(management_overview,
             l10n_util::GetStringUTF16(IDS_MANAGEMENT_DEVICE_NOT_MANAGED));
   EXPECT_FALSE(managed);
diff --git a/chrome/browser/ui/webui/policy_ui.cc b/chrome/browser/ui/webui/policy_ui.cc
index d06ccd1..ced53b8 100644
--- a/chrome/browser/ui/webui/policy_ui.cc
+++ b/chrome/browser/ui/webui/policy_ui.cc
@@ -27,6 +27,8 @@
       {"exportPoliciesJSON", IDS_EXPORT_POLICIES_JSON},
       {"filterPlaceholder", IDS_POLICY_FILTER_PLACEHOLDER},
       {"hideExpandedStatus", IDS_POLICY_HIDE_EXPANDED_STATUS},
+      {"isAffiliatedYes", IDS_POLICY_IS_AFFILIATED_YES},
+      {"isAffiliatedNo", IDS_POLICY_IS_AFFILIATED_NO},
       {"labelAssetId", IDS_POLICY_LABEL_ASSET_ID},
       {"labelClientId", IDS_POLICY_LABEL_CLIENT_ID},
       {"labelDirectoryApiId", IDS_POLICY_LABEL_DIRECTORY_API_ID},
@@ -35,6 +37,7 @@
       {"labelEnterpriseEnrollmentDomain",
        IDS_POLICY_LABEL_ENTERPRISE_ENROLLMENT_DOMAIN},
       {"labelGaiaId", IDS_POLICY_LABEL_GAIA_ID},
+      {"labelIsAffiliated", IDS_POLICY_LABEL_IS_AFFILIATED},
       {"labelLocation", IDS_POLICY_LABEL_LOCATION},
       {"labelMachineEnrollmentDomain",
        IDS_POLICY_LABEL_MACHINE_ENROLLMENT_DOMAIN},
diff --git a/chrome/browser/ui/webui/policy_ui_handler.cc b/chrome/browser/ui/webui/policy_ui_handler.cc
index 33b1ba6..fe23bb6d 100644
--- a/chrome/browser/ui/webui/policy_ui_handler.cc
+++ b/chrome/browser/ui/webui/policy_ui_handler.cc
@@ -68,6 +68,7 @@
 #include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h"
 #include "chrome/browser/chromeos/policy/device_local_account_policy_service.h"
 #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "components/user_manager/user_manager.h"
 #else
 #include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
@@ -171,6 +172,22 @@
                                  last_refresh_time));
 }
 
+#if defined(OS_CHROMEOS)
+// Adds a new entry to |dict| with the affiliation status of the user associated
+// with |profile|. Device scope policy status providers call this method with
+// nullptr |profile|. In this case no entry is added as affiliation status only
+// makes sense for user scope policy status providers.
+void GetUserAffiliationStatus(base::DictionaryValue* dict, Profile* profile) {
+  if (!profile)
+    return;
+  const user_manager::User* user =
+      chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
+  if (!user)
+    return;
+  dict->SetBoolean("isAffiliated", user->IsAffiliated());
+}
+#endif  // defined(OS_CHROMEOS)
+
 void ExtractDomainFromUsername(base::DictionaryValue* dict) {
   std::string username;
   dict->GetString("username", &username);
@@ -239,6 +256,24 @@
   DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyStatusProvider);
 };
 
+#if defined(OS_CHROMEOS)
+// A cloud policy status provider for user policy on Chrome OS.
+class UserCloudPolicyStatusProviderChromeOS
+    : public UserCloudPolicyStatusProvider {
+ public:
+  explicit UserCloudPolicyStatusProviderChromeOS(policy::CloudPolicyCore* core,
+                                                 Profile* profile);
+  ~UserCloudPolicyStatusProviderChromeOS() override;
+
+  // CloudPolicyCoreStatusProvider implementation.
+  void GetStatus(base::DictionaryValue* dict) override;
+
+ private:
+  Profile* profile_;
+  DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyStatusProviderChromeOS);
+};
+#endif  // defined(OS_CHROMEOS)
+
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
 class MachineLevelUserCloudPolicyStatusProvider
     : public PolicyStatusProvider,
@@ -263,11 +298,12 @@
 
 #if defined(OS_CHROMEOS)
 // A cloud policy status provider for device policy.
-class DeviceCloudPolicyStatusProvider : public CloudPolicyCoreStatusProvider {
+class DeviceCloudPolicyStatusProviderChromeOS
+    : public CloudPolicyCoreStatusProvider {
  public:
-  explicit DeviceCloudPolicyStatusProvider(
+  explicit DeviceCloudPolicyStatusProviderChromeOS(
       policy::BrowserPolicyConnectorChromeOS* connector);
-  ~DeviceCloudPolicyStatusProvider() override;
+  ~DeviceCloudPolicyStatusProviderChromeOS() override;
 
   // CloudPolicyCoreStatusProvider implementation.
   void GetStatus(base::DictionaryValue* dict) override;
@@ -276,7 +312,7 @@
   std::string enterprise_enrollment_domain_;
   std::string enterprise_display_domain_;
 
-  DISALLOW_COPY_AND_ASSIGN(DeviceCloudPolicyStatusProvider);
+  DISALLOW_COPY_AND_ASSIGN(DeviceCloudPolicyStatusProviderChromeOS);
 };
 
 // A cloud policy status provider that reads policy status from the policy core
@@ -314,7 +350,8 @@
       public policy::CloudPolicyStore::Observer {
  public:
   explicit UserActiveDirectoryPolicyStatusProvider(
-      policy::ActiveDirectoryPolicyManager* policy_manager);
+      policy::ActiveDirectoryPolicyManager* policy_manager,
+      Profile* profile);
 
   ~UserActiveDirectoryPolicyStatusProvider() override;
 
@@ -327,7 +364,7 @@
 
  private:
   policy::ActiveDirectoryPolicyManager* const policy_manager_;  // not owned.
-
+  Profile* profile_;
   DISALLOW_COPY_AND_ASSIGN(UserActiveDirectoryPolicyStatusProvider);
 };
 
@@ -404,6 +441,26 @@
   ExtractDomainFromUsername(dict);
 }
 
+#if defined(OS_CHROMEOS)
+UserCloudPolicyStatusProviderChromeOS::UserCloudPolicyStatusProviderChromeOS(
+    policy::CloudPolicyCore* core,
+    Profile* profile)
+    : UserCloudPolicyStatusProvider(core) {
+  profile_ = profile;
+}
+
+UserCloudPolicyStatusProviderChromeOS::
+    ~UserCloudPolicyStatusProviderChromeOS() {}
+
+void UserCloudPolicyStatusProviderChromeOS::GetStatus(
+    base::DictionaryValue* dict) {
+  if (!core_->store()->is_managed())
+    return;
+  UserCloudPolicyStatusProvider::GetStatus(dict);
+  GetUserAffiliationStatus(dict, profile_);
+}
+#endif  // defined(OS_CHROMEOS)
+
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
 
 MachineLevelUserCloudPolicyStatusProvider::
@@ -472,17 +529,20 @@
 #endif  // !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
 
 #if defined(OS_CHROMEOS)
-DeviceCloudPolicyStatusProvider::DeviceCloudPolicyStatusProvider(
-    policy::BrowserPolicyConnectorChromeOS* connector)
+DeviceCloudPolicyStatusProviderChromeOS::
+    DeviceCloudPolicyStatusProviderChromeOS(
+        policy::BrowserPolicyConnectorChromeOS* connector)
     : CloudPolicyCoreStatusProvider(
           connector->GetDeviceCloudPolicyManager()->core()) {
   enterprise_enrollment_domain_ = connector->GetEnterpriseEnrollmentDomain();
   enterprise_display_domain_ = connector->GetEnterpriseDisplayDomain();
 }
 
-DeviceCloudPolicyStatusProvider::~DeviceCloudPolicyStatusProvider() = default;
+DeviceCloudPolicyStatusProviderChromeOS::
+    ~DeviceCloudPolicyStatusProviderChromeOS() = default;
 
-void DeviceCloudPolicyStatusProvider::GetStatus(base::DictionaryValue* dict) {
+void DeviceCloudPolicyStatusProviderChromeOS::GetStatus(
+    base::DictionaryValue* dict) {
   GetStatusFromCore(core_, dict);
   dict->SetString("enterpriseEnrollmentDomain", enterprise_enrollment_domain_);
   dict->SetString("enterpriseDisplayDomain", enterprise_display_domain_);
@@ -531,9 +591,11 @@
 
 UserActiveDirectoryPolicyStatusProvider::
     UserActiveDirectoryPolicyStatusProvider(
-        policy::ActiveDirectoryPolicyManager* policy_manager)
+        policy::ActiveDirectoryPolicyManager* policy_manager,
+        Profile* profile)
     : policy_manager_(policy_manager) {
   policy_manager_->store()->AddObserver(this);
+  profile_ = profile;
 }
 
 UserActiveDirectoryPolicyStatusProvider::
@@ -571,6 +633,7 @@
           : ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_ELAPSED,
                                    ui::TimeFormat::LENGTH_SHORT,
                                    base::Time::Now() - last_refresh_time));
+  GetUserAffiliationStatus(dict, profile_);
 }
 
 void UserActiveDirectoryPolicyStatusProvider::OnStoreLoaded(
@@ -588,7 +651,7 @@
         policy::ActiveDirectoryPolicyManager* policy_manager,
         const std::string& enterprise_realm,
         const std::string& enterprise_display_domain)
-    : UserActiveDirectoryPolicyStatusProvider(policy_manager),
+    : UserActiveDirectoryPolicyStatusProvider(policy_manager, nullptr),
       enterprise_realm_(enterprise_realm),
       enterprise_display_domain_(enterprise_display_domain) {}
 
@@ -665,7 +728,7 @@
               connector->GetRealm(), connector->GetEnterpriseDisplayDomain());
     } else {
       device_status_provider_ =
-          std::make_unique<DeviceCloudPolicyStatusProvider>(connector);
+          std::make_unique<DeviceCloudPolicyStatusProviderChromeOS>(connector);
     }
   }
 
@@ -685,12 +748,13 @@
             user_manager->GetActiveUser()->GetAccountId().GetUserEmail(),
             local_account_service);
   } else if (user_cloud_policy) {
-    user_status_provider_ = std::make_unique<UserCloudPolicyStatusProvider>(
-        user_cloud_policy->core());
+    user_status_provider_ =
+        std::make_unique<UserCloudPolicyStatusProviderChromeOS>(
+            user_cloud_policy->core(), profile);
   } else if (active_directory_policy) {
     user_status_provider_ =
         std::make_unique<UserActiveDirectoryPolicyStatusProvider>(
-            active_directory_policy);
+            active_directory_policy, profile);
   }
 #else
   policy::UserCloudPolicyManager* user_cloud_policy_manager =
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc
index 234e5d4..cf9b1c93 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc
@@ -10,6 +10,7 @@
 #include <string>
 
 #include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/files/file_util.h"
 #include "base/system/sys_info.h"
 #include "base/task/post_task.h"
@@ -34,6 +35,7 @@
 #include "chromeos/cryptohome/cryptohome_util.h"
 #include "chromeos/dbus/cryptohome/cryptohome_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
+#include "components/arc/arc_features.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/arc_util.h"
@@ -89,8 +91,10 @@
       source_name_(html_source->GetSource()),
       arc_observer_(this),
       weak_ptr_factory_(this) {
-  html_source->AddBoolean(kAndroidEnabled,
-                          arc::IsArcPlayStoreEnabledForProfile(profile));
+  html_source->AddBoolean(
+      kAndroidEnabled,
+      base::FeatureList::IsEnabled(arc::kUsbStorageUIFeature) &&
+          arc::IsArcPlayStoreEnabledForProfile(profile));
 }
 
 StorageHandler::~StorageHandler() {
@@ -125,7 +129,8 @@
 }
 
 void StorageHandler::OnJavascriptAllowed() {
-  arc_observer_.Add(arc::ArcSessionManager::Get());
+  if (base::FeatureList::IsEnabled(arc::kUsbStorageUIFeature))
+    arc_observer_.Add(arc::ArcSessionManager::Get());
 
   // Start observing the mojo connection UpdateAndroidSize() relies on. Note
   // that OnConnectionReady() will be called immediately if the connection has
@@ -148,7 +153,8 @@
       ->storage_manager()
       ->RemoveObserver(this);
 
-  arc_observer_.Remove(arc::ArcSessionManager::Get());
+  if (base::FeatureList::IsEnabled(arc::kUsbStorageUIFeature))
+    arc_observer_.Remove(arc::ArcSessionManager::Get());
 }
 
 void StorageHandler::HandleUpdateAndroidEnabled(
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
index cf35ca5..b1fdbcac 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
@@ -117,6 +117,7 @@
   if (web_app::SystemWebAppManager::IsEnabled()) {
     html_source->AddResourcePath("icon-192.png", IDR_SETTINGS_LOGO_192);
     html_source->AddResourcePath("pwa.html", IDR_PWA_HTML);
+    html_source->AddResourcePath("manifest.json", IDR_OS_SETTINGS_MANIFEST);
   }
 
 #if BUILDFLAG(OPTIMIZE_WEBUI)
@@ -126,7 +127,6 @@
   html_source->AddResourcePath("chromeos/lazy_load.html",
                                IDR_OS_SETTINGS_LAZY_LOAD_VULCANIZED_HTML);
   html_source->SetDefaultResource(IDR_OS_SETTINGS_VULCANIZED_HTML);
-  html_source->AddResourcePath("manifest.json", IDR_OS_SETTINGS_MANIFEST);
 #else
   // Add all settings resources.
   for (size_t i = 0; i < kOsSettingsResourcesSize; ++i) {
diff --git a/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc b/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc
index 47dd46f..6fa7096 100644
--- a/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc
+++ b/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc
@@ -171,8 +171,8 @@
 
 void CookiesViewHandler::TreeNodesAdded(ui::TreeModel* model,
                                         ui::TreeModelNode* parent,
-                                        int start,
-                                        int count) {
+                                        size_t start,
+                                        size_t count) {
   // Skip if there is a batch update in progress.
   if (batch_update_)
     return;
@@ -192,15 +192,15 @@
     args.Set(kId, std::make_unique<base::Value>());
   else
     args.SetString(kId, model_util_->GetTreeNodeId(parent_node));
-  args.SetInteger(kStart, start);
+  args.SetInteger(kStart, int{start});
   args.Set(kChildren, std::move(children));
   FireWebUIListener("on-tree-item-added", args);
 }
 
 void CookiesViewHandler::TreeNodesRemoved(ui::TreeModel* model,
                                           ui::TreeModelNode* parent,
-                                          int start,
-                                          int count) {
+                                          size_t start,
+                                          size_t count) {
   // Skip if there is a batch update in progress.
   if (batch_update_)
     return;
@@ -212,8 +212,8 @@
     args.Set(kId, std::make_unique<base::Value>());
   else
     args.SetString(kId, model_util_->GetTreeNodeId(tree_model->AsNode(parent)));
-  args.SetInteger(kStart, start);
-  args.SetInteger(kCount, count);
+  args.SetInteger(kStart, int{start});
+  args.SetInteger(kCount, int{count});
   FireWebUIListener("on-tree-item-removed", args);
 }
 
@@ -439,8 +439,8 @@
   // Passing false for |include_quota_nodes| since they don't reflect reality
   // until bug http://crbug.com/642955 is fixed and local/session storage is
   // counted against the total.
-  model_util_->GetChildNodeList(parent, /*start=*/0, parent->child_count(),
-      /*include_quota_nodes=*/false, children.get());
+  model_util_->GetChildNodeList(parent, /*start=*/0, parent->children().size(),
+                                /*include_quota_nodes=*/false, children.get());
 
   base::DictionaryValue args;
   if (parent == cookies_tree_model_->GetRoot())
diff --git a/chrome/browser/ui/webui/settings/settings_cookies_view_handler.h b/chrome/browser/ui/webui/settings/settings_cookies_view_handler.h
index d8debac0..66c77aee 100644
--- a/chrome/browser/ui/webui/settings/settings_cookies_view_handler.h
+++ b/chrome/browser/ui/webui/settings/settings_cookies_view_handler.h
@@ -34,12 +34,12 @@
   // CookiesTreeModel::Observer:
   void TreeNodesAdded(ui::TreeModel* model,
                       ui::TreeModelNode* parent,
-                      int start,
-                      int count) override;
+                      size_t start,
+                      size_t count) override;
   void TreeNodesRemoved(ui::TreeModel* model,
                         ui::TreeModelNode* parent,
-                        int start,
-                        int count) override;
+                        size_t start,
+                        size_t count) override;
   void TreeNodeChanged(ui::TreeModel* model, ui::TreeModelNode* node) override {
   }
   void TreeModelBeginBatch(CookiesTreeModel* model) override;
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index b2bca94..9a3ca49 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -1385,13 +1385,13 @@
 
 void SiteSettingsHandler::TreeNodesAdded(ui::TreeModel* model,
                                          ui::TreeModelNode* parent,
-                                         int start,
-                                         int count) {}
+                                         size_t start,
+                                         size_t count) {}
 
 void SiteSettingsHandler::TreeNodesRemoved(ui::TreeModel* model,
                                            ui::TreeModelNode* parent,
-                                           int start,
-                                           int count) {}
+                                           size_t start,
+                                           size_t count) {}
 
 void SiteSettingsHandler::TreeNodeChanged(ui::TreeModel* model,
                                           ui::TreeModelNode* node) {}
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.h b/chrome/browser/ui/webui/settings/site_settings_handler.h
index 297cae39..04848c7 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.h
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.h
@@ -55,12 +55,12 @@
   // WebUI
   void TreeNodesAdded(ui::TreeModel* model,
                       ui::TreeModelNode* parent,
-                      int start,
-                      int count) override;
+                      size_t start,
+                      size_t count) override;
   void TreeNodesRemoved(ui::TreeModel* model,
                         ui::TreeModelNode* parent,
-                        int start,
-                        int count) override;
+                        size_t start,
+                        size_t count) override;
   void TreeNodeChanged(ui::TreeModel* model, ui::TreeModelNode* node) override;
   void TreeModelEndBatch(CookiesTreeModel* model) override;
 
diff --git a/chrome/browser/ui/webui/settings_utils_win.cc b/chrome/browser/ui/webui/settings_utils_win.cc
index e787f68..69db074 100644
--- a/chrome/browser/ui/webui/settings_utils_win.cc
+++ b/chrome/browser/ui/webui/settings_utils_win.cc
@@ -16,7 +16,10 @@
 #include "base/task/post_task.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/win/windows_version.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/cryptuiapi_shim.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/gfx/font.h"
@@ -99,9 +102,17 @@
 }
 
 void ShowNetworkProxySettings(content::WebContents* web_contents) {
-  base::PostTaskWithTraits(FROM_HERE,
-                           {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
-                           base::BindOnce(&OpenConnectionDialogCallback));
+  if (base::win::GetVersion() >= base::win::Version::WIN10) {
+    // See
+    // https://docs.microsoft.com/en-us/windows/uwp/launch-resume/launch-settings-app#network--internet
+    platform_util::OpenExternal(
+        Profile::FromBrowserContext(web_contents->GetBrowserContext()),
+        GURL("ms-settings:network-proxy"));
+  } else {
+    base::PostTaskWithTraits(
+        FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
+        base::BindOnce(&OpenConnectionDialogCallback));
+  }
 }
 
 void ShowManageSSLCertificates(content::WebContents* web_contents) {
diff --git a/chrome/browser/vr/metrics/session_metrics_helper.cc b/chrome/browser/vr/metrics/session_metrics_helper.cc
index 846b9323..ed0203c 100644
--- a/chrome/browser/vr/metrics/session_metrics_helper.cc
+++ b/chrome/browser/vr/metrics/session_metrics_helper.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/vr/metrics/session_metrics_helper.h"
 
 #include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "components/rappor/public/rappor_utils.h"
 #include "components/ukm/content/source_url_recorder.h"
@@ -30,63 +31,6 @@
 constexpr base::TimeDelta kMaximumHeadsetSessionGap(
     base::TimeDelta::FromSecondsD(0));
 
-// We have several different session times that share code in SessionTimer.
-// Unfortunately, when the actual timer histogram is processed in
-// UMA_HISTOGRAM_CUSTOM_TIMES, there is a function-static variable initialized
-// with the name of the event, and all histograms going through the same
-// function must share the same event name.
-// In order to work around this and use different names, a templated function
-// is used to get different function-static variables for each histogram name.
-// Ideally this could be templated by the event name, but unfortunately
-// C++ doesn't allow templates by strings.  Instead we template by enum, and
-// have a function that translates enum to string.  For each template
-// instantiation, the inlined function will be optimized to just access the
-// string we want to return.
-enum SessionEventName {
-  MODE_FULLSCREEN,
-  MODE_BROWSER,
-  MODE_WEBVR,
-  SESSION_VR,
-  MODE_FULLSCREEN_WITH_VIDEO,
-  MODE_BROWSER_WITH_VIDEO,
-  MODE_WEBVR_WITH_VIDEO,
-  SESSION_VR_WITH_VIDEO,
-};
-
-const char* HistogramNameFromSessionType(SessionEventName name) {
-  // TODO(crbug.com/790682): Migrate all of these to the "VR." namespace.
-  static constexpr char kVrSession[] = "VRSessionTime";
-  static constexpr char kWebVr[] = "VRSessionTime.WebVR";
-  static constexpr char kBrowser[] = "VRSessionTime.Browser";
-  static constexpr char kFullscreen[] = "VRSessionTime.Fullscreen";
-  static constexpr char kVrSessionVideo[] = "VRSessionVideoTime";
-  static constexpr char kWebVrVideo[] = "VRSessionVideoTime.WebVR";
-  static constexpr char kBrowserVideo[] = "VRSessionVideoTime.Browser";
-  static constexpr char kFullscreenVideo[] = "VRSessionVideoTime.Fullscreen";
-
-  switch (name) {
-    case MODE_FULLSCREEN:
-      return kFullscreen;
-    case MODE_BROWSER:
-      return kBrowser;
-    case MODE_WEBVR:
-      return kWebVr;
-    case SESSION_VR:
-      return kVrSession;
-    case MODE_FULLSCREEN_WITH_VIDEO:
-      return kFullscreenVideo;
-    case MODE_BROWSER_WITH_VIDEO:
-      return kBrowserVideo;
-    case MODE_WEBVR_WITH_VIDEO:
-      return kWebVrVideo;
-    case SESSION_VR_WITH_VIDEO:
-      return kVrSessionVideo;
-    default:
-      NOTREACHED();
-      return nullptr;
-  }
-}
-
 void SendRapporEnteredMode(const GURL& origin, Mode mode) {
   switch (mode) {
     case Mode::kVrBrowsingFullscreen:
@@ -147,64 +91,90 @@
 
 }  // namespace
 
-template <SessionEventName SessionType>
-class SessionTimerImpl : public SessionTimer {
+// SessionTimer will monitor the time between calls to StartSession and
+// StopSession.  It will combine multiple segments into a single session if they
+// are sufficiently close in time.  It will also only include segments if they
+// are sufficiently long.
+// Because the session may be extended, the accumulated time is occasionally
+// sent on destruction or when a new session begins.
+class SessionTimer {
  public:
-  SessionTimerImpl(base::TimeDelta gap_time, base::TimeDelta minimum_duration) {
+  SessionTimer(char const* histogram_name,
+               base::TimeDelta gap_time,
+               base::TimeDelta minimum_duration) {
+    histogram_name_ = histogram_name;
     maximum_session_gap_time_ = gap_time;
     minimum_duration_ = minimum_duration;
   }
 
-  ~SessionTimerImpl() override { StopSession(false, base::Time::Now()); }
+  ~SessionTimer() { StopSession(false, base::Time::Now()); }
 
-  void SendAccumulatedSessionTime() override {
-    if (!accumulated_time_.is_zero()) {
-      UMA_HISTOGRAM_CUSTOM_TIMES(HistogramNameFromSessionType(SessionType),
-                                 accumulated_time_, base::TimeDelta(),
-                                 base::TimeDelta::FromHours(5), 100);
+  void StartSession(base::Time start_time) {
+    // If the new start time is within the minimum session gap time from the
+    // last stop, continue the previous session. Otherwise, start a new session,
+    // sending the event for the last session.
+    if (!stop_time_.is_null() &&
+        start_time - stop_time_ <= maximum_session_gap_time_) {
+      // Mark the previous segment as non-continuable, sending data and clearing
+      // state.
+      StopSession(false, stop_time_);
+    }
+
+    start_time_ = start_time;
+  }
+
+  void StopSession(bool continuable, base::Time stop_time) {
+    // first accumulate time from this segment of the session
+    base::TimeDelta segment_duration =
+        (start_time_.is_null() ? base::TimeDelta() : stop_time - start_time_);
+    if (!segment_duration.is_zero() && segment_duration > minimum_duration_) {
+      accumulated_time_ = accumulated_time_ + segment_duration;
+    }
+
+    if (continuable) {
+      // if we are continuable, accumulate the current segment to the session,
+      // and set stop_time_ so we may continue later
+      accumulated_time_ = stop_time - start_time_ + accumulated_time_;
+      stop_time_ = stop_time;
+      start_time_ = base::Time();
+    } else {
+      // send the histogram now if we aren't continuable, clearing segment state
+      SendAccumulatedSessionTime();
+
+      // clear out start/stop/accumulated time
+      start_time_ = base::Time();
+      stop_time_ = base::Time();
+      accumulated_time_ = base::TimeDelta();
     }
   }
+
+ private:
+  void SendAccumulatedSessionTime() {
+    if (!accumulated_time_.is_zero()) {
+      base::UmaHistogramCustomTimes(histogram_name_, accumulated_time_,
+                                    base::TimeDelta(),
+                                    base::TimeDelta::FromHours(5), 100);
+    }
+  }
+
+  char const* histogram_name_;
+
+  base::Time start_time_;
+  base::Time stop_time_;
+  base::TimeDelta accumulated_time_;
+
+  // Config members.
+  // Maximum time gap allowed between a StopSession and a StartSession before it
+  // will be logged as a separate session.
+  base::TimeDelta maximum_session_gap_time_;
+
+  // Minimum time between a StartSession and StopSession required before it is
+  // added to the duration.
+  base::TimeDelta minimum_duration_;
+
+  DISALLOW_COPY_AND_ASSIGN(SessionTimer);
 };
 
-void SessionTimer::StartSession(base::Time start_time) {
-  // If the new start time is within the minimum session gap time from the last
-  // stop, continue the previous session.
-  // Otherwise, start a new session, sending the event for the last session.
-  if (!stop_time_.is_null() &&
-      start_time - stop_time_ <= maximum_session_gap_time_) {
-    // Mark the previous segment as non-continuable, sending data and clearing
-    // state.
-    StopSession(false, stop_time_);
-  }
-
-  start_time_ = start_time;
-}
-
-void SessionTimer::StopSession(bool continuable, base::Time stop_time) {
-  // first accumulate time from this segment of the session
-  base::TimeDelta segment_duration =
-      (start_time_.is_null() ? base::TimeDelta() : stop_time - start_time_);
-  if (!segment_duration.is_zero() && segment_duration > minimum_duration_) {
-    accumulated_time_ = accumulated_time_ + segment_duration;
-  }
-
-  if (continuable) {
-    // if we are continuable, accumulate the current segment to the session, and
-    // set stop_time_ so we may continue later
-    accumulated_time_ = stop_time - start_time_ + accumulated_time_;
-    stop_time_ = stop_time;
-    start_time_ = base::Time();
-  } else {
-    // send the histogram now if we aren't continuable, clearing segment state
-    SendAccumulatedSessionTime();
-
-    // clear out start/stop/accumulated time
-    start_time_ = base::Time();
-    stop_time_ = base::Time();
-    accumulated_time_ = base::TimeDelta();
-  }
-}
-
 // static
 SessionMetricsHelper* SessionMetricsHelper::FromWebContents(
     content::WebContents* web_contents) {
@@ -239,11 +209,12 @@
   is_webvr_ = initial_mode == Mode::kWebXrVrPresentation;
   is_vr_enabled_ = initial_mode != Mode::kNoVr;
 
-  session_timer_ = std::make_unique<SessionTimerImpl<SESSION_VR>>(
-      kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
-  session_video_timer_ =
-      std::make_unique<SessionTimerImpl<SESSION_VR_WITH_VIDEO>>(
-          kMaximumVideoSessionGap, kMinimumVideoSessionDuration);
+  session_timer_ =
+      std::make_unique<SessionTimer>("VRSessionTime", kMaximumHeadsetSessionGap,
+                                     kMinimumHeadsetSessionDuration);
+  session_video_timer_ = std::make_unique<SessionTimer>(
+      "VRSessionVideoTime", kMaximumVideoSessionGap,
+      kMinimumVideoSessionDuration);
 
   Observe(contents);
   contents->SetUserData(kSessionMetricsHelperDataKey,
@@ -530,19 +501,22 @@
 }
 
 void SessionMetricsHelper::OnEnterRegularBrowsing() {
-  mode_timer_ = std::make_unique<SessionTimerImpl<MODE_BROWSER>>(
-      kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
-  mode_video_timer_ =
-      std::make_unique<SessionTimerImpl<MODE_BROWSER_WITH_VIDEO>>(
-          kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
+  mode_timer_ = std::make_unique<SessionTimer>("VRSessionTime.Browser",
+                                               kMaximumHeadsetSessionGap,
+                                               kMinimumHeadsetSessionDuration);
+  mode_video_timer_ = std::make_unique<SessionTimer>(
+      "VRSessionVideoTime.Browser", kMaximumHeadsetSessionGap,
+      kMinimumHeadsetSessionDuration);
 }
 
 void SessionMetricsHelper::OnEnterPresentation() {
-  mode_timer_ = std::make_unique<SessionTimerImpl<MODE_WEBVR>>(
-      kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
+  mode_timer_ = std::make_unique<SessionTimer>("VRSessionTime.WebVR",
+                                               kMaximumHeadsetSessionGap,
+                                               kMinimumHeadsetSessionDuration);
 
-  mode_video_timer_ = std::make_unique<SessionTimerImpl<MODE_WEBVR_WITH_VIDEO>>(
-      kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
+  mode_video_timer_ = std::make_unique<SessionTimer>(
+      "VRSessionVideoTime.WebVR", kMaximumHeadsetSessionGap,
+      kMinimumHeadsetSessionDuration);
 
   // If we are switching to WebVR presentation, start the new presentation
   // session.
@@ -575,12 +549,13 @@
 }
 
 void SessionMetricsHelper::OnEnterFullscreenBrowsing() {
-  mode_timer_ = std::make_unique<SessionTimerImpl<MODE_FULLSCREEN>>(
-      kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
+  mode_timer_ = std::make_unique<SessionTimer>("VRSessionTime.Fullscreen",
+                                               kMaximumHeadsetSessionGap,
+                                               kMinimumHeadsetSessionDuration);
 
-  mode_video_timer_ =
-      std::make_unique<SessionTimerImpl<MODE_FULLSCREEN_WITH_VIDEO>>(
-          kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
+  mode_video_timer_ = std::make_unique<SessionTimer>(
+      "VRSessionVideoTime.Fullscreen", kMaximumHeadsetSessionGap,
+      kMinimumHeadsetSessionDuration);
 
   if (page_session_tracker_)
     page_session_tracker_->ukm_entry()->SetEnteredFullscreen(1);
diff --git a/chrome/browser/vr/metrics/session_metrics_helper.h b/chrome/browser/vr/metrics/session_metrics_helper.h
index dbe202a..f5ed58a 100644
--- a/chrome/browser/vr/metrics/session_metrics_helper.h
+++ b/chrome/browser/vr/metrics/session_metrics_helper.h
@@ -62,39 +62,7 @@
   kMaxValue = 4,
 };
 
-// SessionTimer will monitor the time between calls to StartSession and
-// StopSession.  It will combine multiple segments into a single session if they
-// are sufficiently close in time.  It will also only include segments if they
-// are sufficiently long.
-// Because the session may be extended, the accumulated time is occasionally
-// sent on destruction or when a new session begins.
-class SessionTimer {
- public:
-  virtual ~SessionTimer() {}
-
-  void StartSession(base::Time start_time);
-  void StopSession(bool continuable, base::Time stop_time);
-
- protected:
-  SessionTimer() {}
-
-  virtual void SendAccumulatedSessionTime() = 0;
-
-  base::Time start_time_;
-  base::Time stop_time_;
-  base::TimeDelta accumulated_time_;
-
-  // Config members.
-  // Maximum time gap allowed between a StopSession and a StartSession before it
-  // will be logged as a seperate session.
-  base::TimeDelta maximum_session_gap_time_;
-
-  // Minimum time between a StartSession and StopSession required before it is
-  // added to the duration.
-  base::TimeDelta minimum_duration_;
-
-  DISALLOW_COPY_AND_ASSIGN(SessionTimer);
-};
+class SessionTimer;
 
 // SessionTracker tracks UKM data for sessions and sends the data upon request.
 template <class T>
diff --git a/chrome/browser/web_applications/system_web_app_manager.cc b/chrome/browser/web_applications/system_web_app_manager.cc
index 1b1b863..dad7c61 100644
--- a/chrome/browser/web_applications/system_web_app_manager.cc
+++ b/chrome/browser/web_applications/system_web_app_manager.cc
@@ -22,17 +22,29 @@
 #include "components/version_info/version_info.h"
 #include "content/public/common/content_switches.h"
 
+#if defined(OS_CHROMEOS)
+#include "chromeos/constants/chromeos_features.h"
+#endif  // defined(OS_CHROMEOS)
+
 namespace web_app {
 
 namespace {
 
 base::flat_map<SystemAppType, GURL> CreateSystemWebApps() {
   base::flat_map<SystemAppType, GURL> urls;
+
 // TODO(calamity): Split this into per-platform functions.
 #if defined(OS_CHROMEOS)
-  urls[SystemAppType::DISCOVER] = GURL(chrome::kChromeUIDiscoverURL);
-  constexpr char kChromeSettingsPWAURL[] = "chrome://settings/pwa.html";
-  urls[SystemAppType::SETTINGS] = GURL(kChromeSettingsPWAURL);
+  if (base::FeatureList::IsEnabled(chromeos::features::kDiscoverApp))
+    urls[SystemAppType::DISCOVER] = GURL(chrome::kChromeUIDiscoverURL);
+
+  if (base::FeatureList::IsEnabled(chromeos::features::kSplitSettings)) {
+    constexpr char kChromeSettingsPWAURL[] = "chrome://os-settings/pwa.html";
+    urls[SystemAppType::SETTINGS] = GURL(kChromeSettingsPWAURL);
+  } else {
+    constexpr char kChromeSettingsPWAURL[] = "chrome://settings/pwa.html";
+    urls[SystemAppType::SETTINGS] = GURL(kChromeSettingsPWAURL);
+  }
 #endif  // OS_CHROMEOS
 
   return urls;
diff --git a/chrome/browser/web_applications/system_web_app_manager.h b/chrome/browser/web_applications/system_web_app_manager.h
index 3d25523f..9d87e05 100644
--- a/chrome/browser/web_applications/system_web_app_manager.h
+++ b/chrome/browser/web_applications/system_web_app_manager.h
@@ -57,8 +57,11 @@
   static bool IsEnabled();
 
   // The SystemWebAppManager is disabled in browser tests by default because it
-  // pollutes the startup state. Call this to enable them for SystemWebApp
-  // specific tests.
+  // pollutes the startup state (several tests expect the Extensions state to be
+  // clean).
+  //
+  // Call this to install apps for SystemWebApp specific tests, e.g if a test
+  // needs to open OS Settings.
   void InstallSystemAppsForTesting();
 
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index e773334..5c8e885 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -354,6 +354,8 @@
       "extensions/manifest_handlers/linked_app_icons.h",
       "extensions/manifest_handlers/minimum_chrome_version_checker.cc",
       "extensions/manifest_handlers/minimum_chrome_version_checker.h",
+      "extensions/manifest_handlers/natively_connectable_handler.cc",
+      "extensions/manifest_handlers/natively_connectable_handler.h",
       "extensions/manifest_handlers/settings_overrides_handler.cc",
       "extensions/manifest_handlers/settings_overrides_handler.h",
       "extensions/manifest_handlers/theme_handler.cc",
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 79bbe0e8..00324b0 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -28,6 +28,12 @@
     "AddToHomescreenMessaging", base::FEATURE_DISABLED_BY_DEFAULT};
 #endif  // defined(OS_ANDROID)
 
+#if defined(OS_CHROMEOS)
+// Controls whether web apps can be installed via APKs on Chrome OS.
+const base::Feature kApkWebAppInstalls{"ApkWebAppInstalls",
+                                       base::FEATURE_ENABLED_BY_DEFAULT};
+#endif  // defined(OS_CHROMEOS)
+
 #if defined(OS_MACOSX)
 // Enables the menu item for Javascript execution via AppleScript.
 const base::Feature kAppleScriptExecuteJavaScriptMenuItem{
@@ -620,7 +626,7 @@
 
 // Enables or disables the System Web App manager.
 const base::Feature kSystemWebApps{"SystemWebApps",
-                                   base::FEATURE_DISABLED_BY_DEFAULT};
+                                   base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables or disables the App Management UI.
 const base::Feature kAppManagement{"AppManagement",
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index dd0b3bd..19e38e6 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -29,6 +29,11 @@
 extern const base::Feature kAddToHomescreenMessaging;
 #endif  // defined(OS_ANDROID)
 
+#if defined(OS_CHROMEOS)
+COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kApkWebAppInstalls;
+#endif  // defined(OS_CHROMEOS)
+
 #if defined(OS_MACOSX)
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kAppleScriptExecuteJavaScriptMenuItem;
diff --git a/chrome/common/extensions/api/BUILD.gn b/chrome/common/extensions/api/BUILD.gn
index d86054e..572c730 100644
--- a/chrome/common/extensions/api/BUILD.gn
+++ b/chrome/common/extensions/api/BUILD.gn
@@ -47,6 +47,7 @@
   schema_include_rules = chrome_extensions_api_schema_include_rules
 
   sources += [
+    "action.json",
     "app.json",
     "browser_action.json",
     "commands.json",
diff --git a/chrome/common/extensions/api/_manifest_features.json b/chrome/common/extensions/api/_manifest_features.json
index 402b0319..77eb698 100644
--- a/chrome/common/extensions/api/_manifest_features.json
+++ b/chrome/common/extensions/api/_manifest_features.json
@@ -173,6 +173,10 @@
       "extension", "legacy_packaged_app", "hosted_app", "platform_app"
     ]
   },
+  "natively_connectable": {
+    "channel": "dev",
+    "extension_types": ["extension"]
+  },
   "omnibox": {
     "channel": "stable",
     "extension_types": ["extension", "legacy_packaged_app"]
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index 2a5dd64..d097c11 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -727,6 +727,12 @@
     "channel": "stable",
     "extension_types": ["extension", "legacy_packaged_app"]
   },
+  "transientBackground": {
+    "channel": "dev",
+    "extension_types": [
+      "extension"
+    ]
+  },
   "tts": {
     "channel": "stable",
     "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
diff --git a/chrome/common/extensions/chrome_manifest_handlers.cc b/chrome/common/extensions/chrome_manifest_handlers.cc
index 941ff4d9..89b3032 100644
--- a/chrome/common/extensions/chrome_manifest_handlers.cc
+++ b/chrome/common/extensions/chrome_manifest_handlers.cc
@@ -21,6 +21,7 @@
 #include "chrome/common/extensions/manifest_handlers/extension_action_handler.h"
 #include "chrome/common/extensions/manifest_handlers/linked_app_icons.h"
 #include "chrome/common/extensions/manifest_handlers/minimum_chrome_version_checker.h"
+#include "chrome/common/extensions/manifest_handlers/natively_connectable_handler.h"
 #include "chrome/common/extensions/manifest_handlers/settings_overrides_handler.h"
 #include "chrome/common/extensions/manifest_handlers/theme_handler.h"
 #include "chrome/common/extensions/manifest_handlers/ui_overrides_handler.h"
@@ -55,6 +56,7 @@
   registry->RegisterHandler(std::make_unique<HomepageURLHandler>());
   registry->RegisterHandler(std::make_unique<LinkedAppIconsHandler>());
   registry->RegisterHandler(std::make_unique<MinimumChromeVersionChecker>());
+  registry->RegisterHandler(std::make_unique<NativelyConnectableHandler>());
   registry->RegisterHandler(std::make_unique<OmniboxHandler>());
   registry->RegisterHandler(std::make_unique<OptionsPageManifestHandler>());
   registry->RegisterHandler(std::make_unique<SettingsOverridesHandler>());
diff --git a/chrome/common/extensions/manifest_handlers/natively_connectable_handler.cc b/chrome/common/extensions/manifest_handlers/natively_connectable_handler.cc
new file mode 100644
index 0000000..c535284
--- /dev/null
+++ b/chrome/common/extensions/manifest_handlers/natively_connectable_handler.cc
@@ -0,0 +1,72 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/extensions/manifest_handlers/natively_connectable_handler.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "extensions/common/manifest.h"
+#include "extensions/common/manifest_constants.h"
+
+namespace extensions {
+
+namespace {
+
+const NativelyConnectableHosts* GetHosts(const Extension& extension) {
+  return static_cast<const NativelyConnectableHosts*>(
+      extension.GetManifestData(manifest_keys::kNativelyConnectable));
+}
+
+}  // namespace
+
+NativelyConnectableHosts::NativelyConnectableHosts() = default;
+NativelyConnectableHosts::~NativelyConnectableHosts() = default;
+
+// static
+const std::set<std::string>*
+NativelyConnectableHosts::GetConnectableNativeMessageHosts(
+    const Extension& extension) {
+  const auto* hosts = GetHosts(extension);
+  if (!hosts) {
+    return nullptr;
+  }
+  return &hosts->hosts;
+}
+
+NativelyConnectableHandler::NativelyConnectableHandler() = default;
+NativelyConnectableHandler::~NativelyConnectableHandler() = default;
+
+bool NativelyConnectableHandler::Parse(Extension* extension,
+                                       base::string16* error) {
+  const base::Value* natively_connectable_hosts = nullptr;
+  if (!extension->manifest()->GetList(manifest_keys::kNativelyConnectable,
+                                      &natively_connectable_hosts)) {
+    *error = base::ASCIIToUTF16(manifest_errors::kInvalidNativelyConnectable);
+    return false;
+  }
+
+  auto hosts = std::make_unique<NativelyConnectableHosts>();
+  for (const auto& host : natively_connectable_hosts->GetList()) {
+    if (!host.is_string()) {
+      *error =
+          base::ASCIIToUTF16(manifest_errors::kInvalidNativelyConnectableValue);
+      return false;
+    }
+    hosts->hosts.insert(host.GetString());
+  }
+
+  extension->SetManifestData(manifest_keys::kNativelyConnectable,
+                             std::move(hosts));
+  return true;
+}
+
+base::span<const char* const> NativelyConnectableHandler::Keys() const {
+  static constexpr const char* kKeys[] = {manifest_keys::kNativelyConnectable};
+  return kKeys;
+}
+
+}  // namespace extensions
diff --git a/chrome/common/extensions/manifest_handlers/natively_connectable_handler.h b/chrome/common/extensions/manifest_handlers/natively_connectable_handler.h
new file mode 100644
index 0000000..8a32c3a
--- /dev/null
+++ b/chrome/common/extensions/manifest_handlers/natively_connectable_handler.h
@@ -0,0 +1,48 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_NATIVELY_CONNECTABLE_HANDLER_H_
+#define CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_NATIVELY_CONNECTABLE_HANDLER_H_
+
+#include <set>
+#include <string>
+
+#include "base/containers/span.h"
+#include "base/macros.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/manifest_handler.h"
+
+namespace extensions {
+
+// A structure to hold the parsed list of native messaging hosts that can
+// connect to this extension.
+struct NativelyConnectableHosts : public Extension::ManifestData {
+  NativelyConnectableHosts();
+  ~NativelyConnectableHosts() override;
+
+  static const std::set<std::string>* GetConnectableNativeMessageHosts(
+      const Extension& extension);
+
+  // A set of native messaging hosts allowed to initiate connection to this
+  // extension.
+  std::set<std::string> hosts;
+};
+
+// Parses the "natively_connectable" manifest key.
+class NativelyConnectableHandler : public ManifestHandler {
+ public:
+  NativelyConnectableHandler();
+  ~NativelyConnectableHandler() override;
+
+  bool Parse(Extension* extension, base::string16* error) override;
+
+ private:
+  base::span<const char* const> Keys() const override;
+
+  DISALLOW_COPY_AND_ASSIGN(NativelyConnectableHandler);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_NATIVELY_CONNECTABLE_HANDLER_H_
diff --git a/chrome/common/extensions/manifest_handlers/natively_connectable_handler_unittest.cc b/chrome/common/extensions/manifest_handlers/natively_connectable_handler_unittest.cc
new file mode 100644
index 0000000..fa6f1f3
--- /dev/null
+++ b/chrome/common/extensions/manifest_handlers/natively_connectable_handler_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/extensions/manifest_handlers/natively_connectable_handler.h"
+
+#include "chrome/common/extensions/manifest_tests/chrome_manifest_test.h"
+#include "extensions/common/manifest_constants.h"
+#include "extensions/common/permissions/permission_message_test_util.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace extensions {
+using NativelyConnectableManifestTest = ChromeManifestTest;
+
+TEST_F(NativelyConnectableManifestTest, Basic) {
+  scoped_refptr<Extension> extension =
+      LoadAndExpectSuccess("natively_connectable.json");
+  ASSERT_TRUE(extension.get());
+
+  EXPECT_TRUE(VerifyNoPermissionMessages(extension->permissions_data()));
+
+  auto* hosts =
+      NativelyConnectableHosts::GetConnectableNativeMessageHosts(*extension);
+  ASSERT_TRUE(hosts);
+  EXPECT_EQ(*hosts, (std::set<std::string>{
+                        "com.google.chrome.test.echo",
+                        "com.google.chrome.test.host_binary_missing",
+                    }));
+}
+
+TEST_F(NativelyConnectableManifestTest, Unset) {
+  scoped_refptr<Extension> extension =
+      LoadAndExpectSuccess("natively_connectable_unset.json");
+  ASSERT_TRUE(extension.get());
+
+  EXPECT_TRUE(VerifyNoPermissionMessages(extension->permissions_data()));
+
+  EXPECT_FALSE(
+      NativelyConnectableHosts::GetConnectableNativeMessageHosts(*extension));
+}
+
+TEST_F(NativelyConnectableManifestTest, IncorrectType) {
+  LoadAndExpectError("natively_connectable_incorrect_type.json",
+                     manifest_errors::kInvalidNativelyConnectable);
+}
+
+TEST_F(NativelyConnectableManifestTest, IncorrectValuesType) {
+  LoadAndExpectError("natively_connectable_incorrect_values_type.json",
+                     manifest_errors::kInvalidNativelyConnectableValue);
+}
+
+}  // namespace extensions
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_background_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_background_unittest.cc
index e8259f16..107991130 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifests_background_unittest.cc
+++ b/chrome/common/extensions/manifest_tests/extension_manifests_background_unittest.cc
@@ -6,6 +6,7 @@
 #include <utility>
 
 #include "base/command_line.h"
+#include "base/test/values_test_util.h"
 #include "base/values.h"
 #include "chrome/common/extensions/manifest_tests/chrome_manifest_test.h"
 #include "components/version_info/version_info.h"
@@ -94,6 +95,40 @@
                      errors::kWebRequestConflictsWithLazyBackground);
 }
 
+TEST_F(ExtensionManifestBackgroundTest, BackgroundPageTransientBackground) {
+  ScopedCurrentChannel current_channel(version_info::Channel::DEV);
+
+  scoped_refptr<Extension> extension(
+      LoadAndExpectSuccess(ManifestData(base::test::ParseJson(R"(
+          {
+            "name": "test",
+            "manifest_version": 2,
+            "version": "1",
+            "background": {
+              "page": "foo.html"
+            }
+          })"),
+                                        "")));
+  ASSERT_TRUE(extension.get());
+  EXPECT_TRUE(BackgroundInfo::HasPersistentBackgroundPage(extension.get()));
+
+  LoadAndExpectError(
+      ManifestData(base::test::ParseJson(R"(
+          {
+            "name": "test",
+            "manifest_version": 2,
+            "version": "1",
+            "permissions": [
+              "transientBackground"
+            ],
+            "background": {
+              "page": "foo.html"
+            }
+          })"),
+                   ""),
+      errors::kTransientBackgroundConflictsWithPersistentBackground);
+}
+
 TEST_F(ExtensionManifestBackgroundTest, BackgroundPagePersistentPlatformApp) {
   scoped_refptr<Extension> extension =
       LoadAndExpectSuccess("background_page_persistent_app.json");
diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.cc b/chrome/common/extensions/permissions/chrome_api_permissions.cc
index 07777e2..4a29b19 100644
--- a/chrome/common/extensions/permissions/chrome_api_permissions.cc
+++ b/chrome/common/extensions/permissions/chrome_api_permissions.cc
@@ -87,6 +87,8 @@
     {APIPermission::kSignedInDevices, "signedInDevices"},
     {APIPermission::kTab, "tabs"},
     {APIPermission::kTopSites, "topSites"},
+    {APIPermission::kTransientBackground, "transientBackground",
+     APIPermissionInfo::kFlagCannotBeOptional},
     {APIPermission::kTts, "tts", APIPermissionInfo::kFlagCannotBeOptional},
     {APIPermission::kTtsEngine, "ttsEngine",
      APIPermissionInfo::kFlagCannotBeOptional},
diff --git a/chrome/common/extensions/permissions/chrome_permission_message_rules.cc b/chrome/common/extensions/permissions/chrome_permission_message_rules.cc
index d8e4032f..5afea31 100644
--- a/chrome/common/extensions/permissions/chrome_permission_message_rules.cc
+++ b/chrome/common/extensions/permissions/chrome_permission_message_rules.cc
@@ -668,6 +668,9 @@
       {IDS_EXTENSION_PROMPT_WARNING_LOGIN_SCREEN_UI,
        {APIPermission::kLoginScreenUi},
        {}},
+      {IDS_EXTENSION_PROMPT_WARNING_TRANSIENT_BACKGROUND,
+       {APIPermission::kTransientBackground},
+       {}},
   };
 
   return std::vector<ChromePermissionMessageRule>(
diff --git a/chrome/common/heap_profiler_controller_unittest.cc b/chrome/common/heap_profiler_controller_unittest.cc
index c22add8..5056f36 100644
--- a/chrome/common/heap_profiler_controller_unittest.cc
+++ b/chrome/common/heap_profiler_controller_unittest.cc
@@ -12,7 +12,14 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/metrics_proto/sampled_profile.pb.h"
 
-TEST(HeapProfilerControllerTest, EmptyProfileIsNotEmitted) {
+// TODO(crbug.com/961073): Fix memory leaks in tests and re-enable on LSAN.
+#ifdef LEAK_SANITIZER
+#define MAYBE_EmptyProfileIsNotEmitted DISABLED_EmptyProfileIsNotEmitted
+#else
+#define MAYBE_EmptyProfileIsNotEmitted EmptyProfileIsNotEmitted
+#endif
+
+TEST(HeapProfilerControllerTest, MAYBE_EmptyProfileIsNotEmitted) {
   auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
   base::TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner.get());
 
diff --git a/chrome/services/ble_scan_parser/BUILD.gn b/chrome/services/ble_scan_parser/BUILD.gn
new file mode 100644
index 0000000..07e7f7b
--- /dev/null
+++ b/chrome/services/ble_scan_parser/BUILD.gn
@@ -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.
+
+assert(is_chromeos)
+import("//testing/libfuzzer/fuzzer_test.gni")
+
+source_set("lib") {
+  sources = [
+    "ble_scan_parser_impl.cc",
+    "ble_scan_parser_impl.h",
+    "ble_scan_parser_service.cc",
+    "ble_scan_parser_service.h",
+  ]
+
+  deps = [
+    "//base",
+    "//device/bluetooth/public/mojom/",
+    "//mojo/public/cpp/bindings",
+    "//services/service_manager/public/cpp",
+  ]
+
+  public_deps = [
+    "//chrome/services/ble_scan_parser/public/mojom",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [
+    "ble_scan_parser_impl_unittest.cc",
+  ]
+
+  deps = [
+    ":lib",
+    "//base",
+    "//base/test:test_support",
+    "//services/service_manager/public/cpp/test:test_support",
+    "//testing/gtest",
+  ]
+}
+
+fuzzer_test("ble_scan_parser_fuzzer") {
+  sources = [
+    "ble_scan_parser_impl_fuzzer.cc",
+  ]
+  deps = [
+    ":lib",
+  ]
+}
diff --git a/chrome/services/ble_scan_parser/DEPS b/chrome/services/ble_scan_parser/DEPS
new file mode 100644
index 0000000..1c7539ea
--- /dev/null
+++ b/chrome/services/ble_scan_parser/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+  "+mojo",  # By definition.
+
+  # Services have to list which other services they depend on.
+  "-chrome/services",
+  "-services",
+  "+services/service_manager/public",  # Every service talks to Service Manager.
+  "+chrome/services/ble_scan_parser",
+  "+device/bluetooth/public",
+]
diff --git a/chrome/services/ble_scan_parser/OWNERS b/chrome/services/ble_scan_parser/OWNERS
new file mode 100644
index 0000000..b5fa4e4
--- /dev/null
+++ b/chrome/services/ble_scan_parser/OWNERS
@@ -0,0 +1 @@
+file://device/bluetooth/OWNERS
diff --git a/chrome/services/ble_scan_parser/ble_scan_parser_impl.cc b/chrome/services/ble_scan_parser/ble_scan_parser_impl.cc
new file mode 100644
index 0000000..fc7d649
--- /dev/null
+++ b/chrome/services/ble_scan_parser/ble_scan_parser_impl.cc
@@ -0,0 +1,199 @@
+// 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 <string>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/strings/string_number_conversions.h"
+#include "chrome/services/ble_scan_parser/ble_scan_parser_impl.h"
+
+namespace ble_scan_parser {
+
+// Definitions of the data type flags:
+// https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/
+const int kDataTypeFlags = 0x01;
+const int kDataTypeServiceUuids16BitPartial = 0x02;
+const int kDataTypeServiceUuids16BitComplete = 0x03;
+const int kDataTypeServiceUuids32BitPartial = 0x04;
+const int kDataTypeServiceUuids32BitComplete = 0x05;
+const int kDataTypeServiceUuids128BitPartial = 0x06;
+const int kDataTypeServiceUuids128BitComplete = 0x07;
+const int kDataTypeLocalNameShort = 0x08;
+const int kDataTypeLocalNameComplete = 0x09;
+const int kDataTypeTxPowerLevel = 0x0A;
+const int kDataTypeServiceData = 0x16;
+const int kDataTypeManufacturerData = 0xFF;
+
+const char kUuidPrefix[] = "0000";
+const char kUuidSuffix[] = "-0000-1000-8000-00805F9B34FB";
+
+BleScanParserImpl::BleScanParserImpl(
+    std::unique_ptr<service_manager::ServiceContextRef> service_ref)
+    : service_ref_(std::move(service_ref)) {}
+
+BleScanParserImpl::~BleScanParserImpl() = default;
+
+void BleScanParserImpl::Parse(const std::vector<uint8_t>& advertisement_data,
+                              ParseCallback callback) {
+  std::move(callback).Run(ParseBleScan(advertisement_data));
+}
+
+mojom::ScanRecordPtr BleScanParserImpl::ParseBleScan(
+    base::span<const uint8_t> advertisement_data) {
+  uint8_t tx_power;
+  std::string advertisement_name;
+  std::vector<device::BluetoothUUID> service_uuids;
+  base::flat_map<std::string, std::vector<uint8_t>> service_data_map;
+  base::flat_map<uint16_t, std::vector<uint8_t>> manufacturer_data_map;
+
+  int advertising_flags = -1;
+
+  // A reference for BLE advertising data: https://bit.ly/2DUTnsk
+  for (size_t i = 0; i < advertisement_data.size();) {
+    uint8_t length = advertisement_data[i++];
+    if (length <= 1 || length > advertisement_data.size() - i) {
+      return nullptr;
+    }
+
+    // length includes the field_type byte.
+    uint8_t data_length = length - 1;
+    uint8_t field_type = advertisement_data[i++];
+
+    switch (field_type) {
+      case kDataTypeFlags:
+        advertising_flags = advertisement_data[i];
+        break;
+      case kDataTypeServiceUuids16BitPartial:
+      case kDataTypeServiceUuids16BitComplete:
+        if (!ParseServiceUuids(advertisement_data.subspan(i, data_length),
+                               UuidFormat::kFormat16Bit, &service_uuids)) {
+          return nullptr;
+        }
+        break;
+      case kDataTypeServiceUuids32BitPartial:
+      case kDataTypeServiceUuids32BitComplete:
+        if (!ParseServiceUuids(advertisement_data.subspan(i, data_length),
+                               UuidFormat::kFormat32Bit, &service_uuids)) {
+          return nullptr;
+        }
+        break;
+      case kDataTypeServiceUuids128BitPartial:
+      case kDataTypeServiceUuids128BitComplete:
+        if (!ParseServiceUuids(advertisement_data.subspan(i, data_length),
+                               UuidFormat::kFormat128Bit, &service_uuids)) {
+          return nullptr;
+        }
+        break;
+      case kDataTypeLocalNameShort:
+      case kDataTypeLocalNameComplete: {
+        base::span<const uint8_t> s =
+            advertisement_data.subspan(i, data_length);
+        advertisement_name = std::string(s.begin(), s.end());
+        break;
+      }
+      case kDataTypeTxPowerLevel:
+        tx_power = advertisement_data[i];
+        break;
+      case kDataTypeServiceData: {
+        if (data_length < 4) {
+          return nullptr;
+        }
+
+        base::span<const uint8_t> uuid = advertisement_data.subspan(i, 2);
+        base::span<const uint8_t> data =
+            advertisement_data.subspan(i + 2, data_length - 2);
+        service_data_map[ParseUuid(uuid, UuidFormat::kFormat16Bit)] =
+            std::vector<uint8_t>(data.begin(), data.end());
+        break;
+      }
+      case kDataTypeManufacturerData: {
+        if (data_length < 4) {
+          return nullptr;
+        }
+
+        uint16_t manufacturer_key = (advertisement_data[i + 1] << 8);
+        manufacturer_key += advertisement_data[i];
+        base::span<const uint8_t> s =
+            advertisement_data.subspan(i + 2, data_length - 2);
+        manufacturer_data_map[manufacturer_key] =
+            std::vector<uint8_t>(s.begin(), s.end());
+        break;
+      }
+      default:
+        // Just ignore. We don't handle other data types.
+        break;
+    }
+
+    i += data_length;
+  }
+
+  return mojom::ScanRecord::New(advertising_flags, tx_power, advertisement_name,
+                                service_uuids, service_data_map,
+                                manufacturer_data_map);
+}
+
+std::string BleScanParserImpl::ParseUuid(base::span<const uint8_t> bytes,
+                                         UuidFormat format) {
+  size_t length = bytes.size();
+  if (!(format == UuidFormat::kFormat16Bit && length == 2) &&
+      !(format == UuidFormat::kFormat32Bit && length == 4) &&
+      !(format == UuidFormat::kFormat128Bit && length == 16)) {
+    return std::string();
+  }
+
+  std::string uuid = base::HexEncode(bytes.data(), bytes.size());
+
+  switch (format) {
+    case UuidFormat::kFormat16Bit:
+      return kUuidPrefix + uuid + kUuidSuffix;
+    case UuidFormat::kFormat32Bit:
+      return uuid + kUuidSuffix;
+    case UuidFormat::kFormat128Bit:
+      uuid.insert(8, 1, '-');
+      uuid.insert(13, 1, '-');
+      uuid.insert(18, 1, '-');
+      uuid.insert(23, 1, '-');
+      return uuid;
+    case UuidFormat::kFormatInvalid:
+      NOTREACHED();
+  }
+
+  NOTREACHED();
+  return std::string();
+}
+
+bool BleScanParserImpl::ParseServiceUuids(
+    base::span<const uint8_t> bytes,
+    UuidFormat format,
+    std::vector<device::BluetoothUUID>* service_uuids) {
+  int uuid_length = 0;
+  switch (format) {
+    case UuidFormat::kFormat16Bit:
+      uuid_length = 2;
+      break;
+    case UuidFormat::kFormat32Bit:
+      uuid_length = 4;
+      break;
+    case UuidFormat::kFormat128Bit:
+      uuid_length = 16;
+      break;
+    case UuidFormat::kFormatInvalid:
+      NOTREACHED();
+      return false;
+  }
+
+  if (bytes.size() % uuid_length != 0) {
+    return false;
+  }
+
+  for (size_t start = 0; start < bytes.size(); start += uuid_length) {
+    service_uuids->push_back(device::BluetoothUUID(
+        ParseUuid(bytes.subspan(start, uuid_length), format)));
+  }
+
+  return true;
+}
+
+}  // namespace ble_scan_parser
diff --git a/chrome/services/ble_scan_parser/ble_scan_parser_impl.h b/chrome/services/ble_scan_parser/ble_scan_parser_impl.h
new file mode 100644
index 0000000..95ee98d
--- /dev/null
+++ b/chrome/services/ble_scan_parser/ble_scan_parser_impl.h
@@ -0,0 +1,61 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_SERVICES_BLE_SCAN_PARSER_BLE_SCAN_PARSER_IMPL_H_
+#define CHROME_SERVICES_BLE_SCAN_PARSER_BLE_SCAN_PARSER_IMPL_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/containers/span.h"
+#include "base/macros.h"
+#include "chrome/services/ble_scan_parser/public/mojom/ble_scan_parser.mojom.h"
+#include "device/bluetooth/public/mojom/uuid.mojom.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+
+namespace ble_scan_parser {
+
+enum class UuidFormat {
+  // The UUID is the third and fourth bytes of a UUID with this pattern:
+  // 0000____-0000-1000-8000-00805F9B34FB
+  kFormat16Bit,
+  // The UUID is the first four bytes of a UUID with this pattern:
+  // ________-0000-1000-8000-00805F9B34FB
+  kFormat32Bit,
+  // The UUID is a standard UUID
+  kFormat128Bit,
+  kFormatInvalid
+};
+
+class BleScanParserImpl : public mojom::BleScanParser {
+ public:
+  explicit BleScanParserImpl(
+      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
+  ~BleScanParserImpl() override;
+
+  // mojom::BleScanParser:
+  void Parse(const std::vector<uint8_t>& advertisement_data,
+             ParseCallback callback) override;
+
+  static mojom::ScanRecordPtr ParseBleScan(
+      base::span<const uint8_t> advertisement_data);
+
+  static std::string ParseUuid(base::span<const uint8_t> bytes,
+                               UuidFormat format);
+
+  static bool ParseServiceUuids(
+      base::span<const uint8_t> bytes,
+      UuidFormat format,
+      std::vector<device::BluetoothUUID>* service_uuids);
+
+ private:
+  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
+
+  DISALLOW_COPY_AND_ASSIGN(BleScanParserImpl);
+};
+
+}  // namespace ble_scan_parser
+
+#endif  // CHROME_SERVICES_BLE_SCAN_PARSER_BLE_SCAN_PARSER_IMPL_H_
diff --git a/chrome/services/ble_scan_parser/ble_scan_parser_impl_fuzzer.cc b/chrome/services/ble_scan_parser/ble_scan_parser_impl_fuzzer.cc
new file mode 100644
index 0000000..84f8dfa
--- /dev/null
+++ b/chrome/services/ble_scan_parser/ble_scan_parser_impl_fuzzer.cc
@@ -0,0 +1,13 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include "chrome/services/ble_scan_parser/ble_scan_parser_impl.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  ble_scan_parser::BleScanParserImpl::ParseBleScan(base::make_span(data, size));
+  return 0;
+}
diff --git a/chrome/services/ble_scan_parser/ble_scan_parser_impl_unittest.cc b/chrome/services/ble_scan_parser/ble_scan_parser_impl_unittest.cc
new file mode 100644
index 0000000..7768fa33
--- /dev/null
+++ b/chrome/services/ble_scan_parser/ble_scan_parser_impl_unittest.cc
@@ -0,0 +1,151 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ble_scan_parser_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ble_scan_parser {
+
+TEST(BleScanParserImplTest, ParseBadUuidLengthReturnsEmptyString) {
+  std::vector<uint8_t> bad_uuid(0xab, 5);
+  EXPECT_EQ(
+      0ul,
+      BleScanParserImpl::ParseUuid(bad_uuid, UuidFormat::kFormat16Bit).size());
+  EXPECT_EQ(
+      0ul,
+      BleScanParserImpl::ParseUuid(bad_uuid, UuidFormat::kFormat32Bit).size());
+  EXPECT_EQ(
+      0ul,
+      BleScanParserImpl::ParseUuid(bad_uuid, UuidFormat::kFormat128Bit).size());
+}
+
+TEST(BleScanParserImplTest, Parse16BitUuid) {
+  const uint8_t kUuid16[] = {0xab, 0xcd};
+  const char kExpected[] = "0000ABCD-0000-1000-8000-00805F9B34FB";
+  std::string actual =
+      BleScanParserImpl::ParseUuid(kUuid16, UuidFormat::kFormat16Bit);
+  EXPECT_STREQ(kExpected, actual.c_str());
+}
+
+TEST(BleScanParserImplTest, Parse32BitUuid) {
+  const uint8_t kUuid32[] = {0xab, 0xcd, 0xef, 0x01};
+  const char kExpected[] = "ABCDEF01-0000-1000-8000-00805F9B34FB";
+  std::string actual =
+      BleScanParserImpl::ParseUuid(kUuid32, UuidFormat::kFormat32Bit);
+  EXPECT_STREQ(kExpected, actual.c_str());
+}
+
+TEST(BleScanParserImplTest, Parse128BitUuid) {
+  const uint8_t kUuid128[] = {0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89,
+                              0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89};
+  const char kExpected[] = "ABCDEF01-2345-6789-ABCD-EF0123456789";
+  std::string actual =
+      BleScanParserImpl::ParseUuid(kUuid128, UuidFormat::kFormat128Bit);
+  EXPECT_STREQ(kExpected, actual.c_str());
+}
+
+TEST(BleScanParserImplTest, Parse16BitServiceUuids) {
+  std::vector<device::BluetoothUUID> expected = {
+      device::BluetoothUUID("0000ABCD-0000-1000-8000-00805F9B34FB"),
+      device::BluetoothUUID("0000EF01-0000-1000-8000-00805F9B34FB"),
+      device::BluetoothUUID("00002345-0000-1000-8000-00805F9B34FB"),
+      device::BluetoothUUID("00006789-0000-1000-8000-00805F9B34FB"),
+      device::BluetoothUUID("0000ABCD-0000-1000-8000-00805F9B34FB"),
+      device::BluetoothUUID("0000EF01-0000-1000-8000-00805F9B34FB"),
+      device::BluetoothUUID("00002345-0000-1000-8000-00805F9B34FB"),
+      device::BluetoothUUID("00006789-0000-1000-8000-00805F9B34FB"),
+  };
+
+  const uint8_t kUuids[] = {0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89,
+                            0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89};
+
+  std::vector<device::BluetoothUUID> actual;
+  BleScanParserImpl::ParseServiceUuids(kUuids, UuidFormat::kFormat16Bit,
+                                       &actual);
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(BleScanParserImplTest, Parse32BitServiceUuids) {
+  std::vector<device::BluetoothUUID> expected = {
+      device::BluetoothUUID("ABCDEF01-0000-1000-8000-00805F9B34FB"),
+      device::BluetoothUUID("23456789-0000-1000-8000-00805F9B34FB"),
+      device::BluetoothUUID("ABCDEF01-0000-1000-8000-00805F9B34FB"),
+      device::BluetoothUUID("23456789-0000-1000-8000-00805F9B34FB"),
+  };
+
+  const uint8_t kUuids[] = {0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89,
+                            0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89};
+
+  std::vector<device::BluetoothUUID> actual;
+  BleScanParserImpl::ParseServiceUuids(kUuids, UuidFormat::kFormat32Bit,
+                                       &actual);
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(BleScanParserImplTest, Parse128BitServiceUuids) {
+  std::vector<device::BluetoothUUID> expected = {
+      device::BluetoothUUID("ABCDEF01-2345-6789-ABCD-EF0123456789"),
+      device::BluetoothUUID("23456789-ABCD-EF01-ABCD-EF0123456789"),
+  };
+
+  const uint8_t kUuids[] = {0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89,
+                            0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89,
+                            0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01,
+                            0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89};
+
+  std::vector<device::BluetoothUUID> actual;
+  BleScanParserImpl::ParseServiceUuids(kUuids, UuidFormat::kFormat128Bit,
+                                       &actual);
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(BleScanParserImplTest, ParseBleAdvertisingScan) {
+  std::vector<device::BluetoothUUID> expected_service_uuids = {
+      device::BluetoothUUID("0000ABCD-0000-1000-8000-00805F9B34FB"),
+      device::BluetoothUUID("0000EF01-0000-1000-8000-00805F9B34FB"),
+      device::BluetoothUUID("ABCDEF01-0000-1000-8000-00805F9B34FB"),
+      device::BluetoothUUID("23456789-0000-1000-8000-00805F9B34FB"),
+      device::BluetoothUUID("ABCDEF01-2345-6789-ABCD-EF0123456789"),
+  };
+
+  std::vector<uint8_t> service_data = {0xa1, 0xb2, 0xc3, 0xd4, 0xe5};
+  base::flat_map<std::string, std::vector<uint8_t>> expected_service_data_map;
+  expected_service_data_map["0000DCAB-0000-1000-8000-00805F9B34FB"] =
+      service_data;
+
+  std::vector<uint8_t> manufacturer_data = {0x1a, 0x2b, 0x3c, 0x4d};
+  base::flat_map<uint16_t, std::vector<uint8_t>> expected_manufacturer_data_map;
+  expected_manufacturer_data_map[0xd00d] = manufacturer_data;
+
+  const uint8_t kRawData[] = {
+      // Length of the rest of the section, field type, data.
+      // Advertising flag = 0x42
+      0x02, 0x01, 0x42,
+      // 16-bit service UUIDs 0000abcd-... and 0000ef01-...
+      0x05, 0x02, 0xab, 0xcd, 0xef, 0x01,
+      // TX power = 0x1b
+      0x02, 0x0a, 0x1b,
+      // 32-bit service UUIDs abcdef01-... and 23456789-...
+      0x09, 0x05, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89,
+      // Local name 'Steve'
+      0x06, 0x08, 0x53, 0x74, 0x65, 0x76, 0x65,
+      // 128-bit service UUID abcdef01-2345-6789-abcd-ef0123456789
+      0x11, 0x06, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd,
+      0xef, 0x01, 0x23, 0x45, 0x67, 0x89,
+      // Service data map 0000dcab-... => { 0xa1, 0xb2, 0xc3, 0xd4, 0xe5 }
+      0x08, 0x16, 0xdc, 0xab, 0xa1, 0xb2, 0xc3, 0xd4, 0xe5,
+      // Manufacturer data map 0xd00d => { 0x1a, 0x2b, 0x3c, 0x4d }
+      0x07, 0xff, 0x0d, 0xd0, 0x1a, 0x2b, 0x3c, 0x4d};
+
+  mojom::ScanRecordPtr actual = BleScanParserImpl::ParseBleScan(kRawData);
+  ASSERT_TRUE(actual);
+  EXPECT_EQ(0x42, actual->advertising_flags);
+  EXPECT_EQ(0x1b, actual->tx_power);
+  EXPECT_EQ("Steve", actual->advertisement_name);
+  EXPECT_EQ(expected_service_uuids, actual->service_uuids);
+  EXPECT_EQ(expected_service_data_map, actual->service_data_map);
+  EXPECT_EQ(expected_manufacturer_data_map, actual->manufacturer_data_map);
+}
+
+}  // namespace ble_scan_parser
diff --git a/chrome/services/ble_scan_parser/ble_scan_parser_service.cc b/chrome/services/ble_scan_parser/ble_scan_parser_service.cc
new file mode 100644
index 0000000..922262a
--- /dev/null
+++ b/chrome/services/ble_scan_parser/ble_scan_parser_service.cc
@@ -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.
+
+#include "chrome/services/ble_scan_parser/ble_scan_parser_service.h"
+#include "chrome/services/ble_scan_parser/ble_scan_parser_impl.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace ble_scan_parser {
+
+BleScanParserService::BleScanParserService(
+    service_manager::mojom::ServiceRequest request)
+    : binding_(this, std::move(request)),
+      keepalive_(&binding_, base::TimeDelta::FromSeconds(0)) {}
+
+BleScanParserService::~BleScanParserService() = default;
+
+void BleScanParserService::OnBindInterface(
+    const service_manager::BindSourceInfo& source_info,
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle interface_pipe) {
+  if (interface_name == mojom::BleScanParser::Name_) {
+    mojo::MakeStrongBinding(
+        std::make_unique<BleScanParserImpl>(keepalive_.CreateRef()),
+        ble_scan_parser::mojom::BleScanParserRequest(
+            std::move(interface_pipe)));
+  }
+}
+
+}  // namespace ble_scan_parser
diff --git a/chrome/services/ble_scan_parser/ble_scan_parser_service.h b/chrome/services/ble_scan_parser/ble_scan_parser_service.h
new file mode 100644
index 0000000..c8982b8
--- /dev/null
+++ b/chrome/services/ble_scan_parser/ble_scan_parser_service.h
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_SERVICES_BLE_SCAN_PARSER_BLE_SCAN_PARSER_SERVICE_H_
+#define CHROME_SERVICES_BLE_SCAN_PARSER_BLE_SCAN_PARSER_SERVICE_H_
+
+#include "services/service_manager/public/cpp/service.h"
+#include "services/service_manager/public/cpp/service_binding.h"
+#include "services/service_manager/public/cpp/service_keepalive.h"
+
+#include "base/macros.h"
+#include "chrome/services/ble_scan_parser/public/mojom/ble_scan_parser.mojom.h"
+
+namespace ble_scan_parser {
+
+class BleScanParserService : public service_manager::Service {
+ public:
+  explicit BleScanParserService(service_manager::mojom::ServiceRequest request);
+  ~BleScanParserService() override;
+
+ private:
+  // service_manager::Service:
+  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
+                       const std::string& interface_name,
+                       mojo::ScopedMessagePipeHandle interface_pipe) override;
+
+  service_manager::ServiceBinding binding_;
+  service_manager::ServiceKeepalive keepalive_;
+
+  DISALLOW_COPY_AND_ASSIGN(BleScanParserService);
+};
+
+}  // namespace ble_scan_parser
+
+#endif  // CHROME_SERVICES_BLE_SCAN_PARSER_BLE_SCAN_PARSER_SERVICE_H_
diff --git a/chrome/services/ble_scan_parser/public/cpp/BUILD.gn b/chrome/services/ble_scan_parser/public/cpp/BUILD.gn
new file mode 100644
index 0000000..7b5fba3
--- /dev/null
+++ b/chrome/services/ble_scan_parser/public/cpp/BUILD.gn
@@ -0,0 +1,20 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_chromeos)
+
+source_set("manifest") {
+  sources = [
+    "manifest.cc",
+    "manifest.h",
+  ]
+
+  deps = [
+    "//base",
+    "//chrome:strings",
+    "//chrome/services/ble_scan_parser/public/mojom",
+    "//services/preferences/public/mojom",
+    "//services/service_manager/public/cpp",
+  ]
+}
diff --git a/chrome/services/ble_scan_parser/public/cpp/OWNERS b/chrome/services/ble_scan_parser/public/cpp/OWNERS
new file mode 100644
index 0000000..dad81cc
--- /dev/null
+++ b/chrome/services/ble_scan_parser/public/cpp/OWNERS
@@ -0,0 +1,4 @@
+per-file manifest.h=set noparent
+per-file manifest.h=file://ipc/SECURITY_OWNERS
+per-file manifest.cc=set noparent
+per-file manifest.cc=file://ipc/SECURITY_OWNERS
diff --git a/chrome/services/ble_scan_parser/public/cpp/manifest.cc b/chrome/services/ble_scan_parser/public/cpp/manifest.cc
new file mode 100644
index 0000000..5a6d737
--- /dev/null
+++ b/chrome/services/ble_scan_parser/public/cpp/manifest.cc
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/services/ble_scan_parser/public/cpp/manifest.h"
+
+#include "base/no_destructor.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/services/ble_scan_parser/public/mojom/ble_scan_parser.mojom.h"
+#include "chrome/services/ble_scan_parser/public/mojom/constants.mojom.h"
+#include "services/service_manager/public/cpp/manifest_builder.h"
+
+namespace ble_scan_parser {
+
+const service_manager::Manifest GetManifest() {
+  return service_manager::ManifestBuilder()
+      .WithServiceName(mojom::kServiceName)
+      .WithDisplayName(IDS_UTILITY_PROCESS_BLE_SCAN_PARSER_NAME)
+      .ExposeCapability(
+          mojom::kParseBleScanCapability,
+          service_manager::Manifest::InterfaceList<mojom::BleScanParser>())
+      .Build();
+}
+
+}  // namespace ble_scan_parser
diff --git a/chrome/services/ble_scan_parser/public/cpp/manifest.h b/chrome/services/ble_scan_parser/public/cpp/manifest.h
new file mode 100644
index 0000000..2db3b71
--- /dev/null
+++ b/chrome/services/ble_scan_parser/public/cpp/manifest.h
@@ -0,0 +1,15 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef CHROME_SERVICES_BLE_SCAN_PARSER_PUBLIC_CPP_MANIFEST_H_
+#define CHROME_SERVICES_BLE_SCAN_PARSER_PUBLIC_CPP_MANIFEST_H_
+
+#include "services/service_manager/public/cpp/manifest.h"
+
+namespace ble_scan_parser {
+
+const service_manager::Manifest GetManifest();
+
+}  // namespace ble_scan_parser
+
+#endif  // CHROME_SERVICES_BLE_SCAN_PARSER_PUBLIC_CPP_MANIFEST_H_
diff --git a/chrome/services/ble_scan_parser/public/mojom/BUILD.gn b/chrome/services/ble_scan_parser/public/mojom/BUILD.gn
new file mode 100644
index 0000000..a246beab
--- /dev/null
+++ b/chrome/services/ble_scan_parser/public/mojom/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_chromeos)
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojom") {
+  sources = [
+    "ble_scan_parser.mojom",
+  ]
+
+  public_deps = [
+    ":constants",
+  ]
+
+  deps = [
+    "//device/bluetooth/public/mojom/",
+  ]
+}
+
+mojom("constants") {
+  sources = [
+    "constants.mojom",
+  ]
+}
diff --git a/chrome/services/ble_scan_parser/public/mojom/OWNERS b/chrome/services/ble_scan_parser/public/mojom/OWNERS
new file mode 100644
index 0000000..16d2e7a7
--- /dev/null
+++ b/chrome/services/ble_scan_parser/public/mojom/OWNERS
@@ -0,0 +1,3 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
+
diff --git a/chrome/services/ble_scan_parser/public/mojom/ble_scan_parser.mojom b/chrome/services/ble_scan_parser/public/mojom/ble_scan_parser.mojom
new file mode 100644
index 0000000..05f905f
--- /dev/null
+++ b/chrome/services/ble_scan_parser/public/mojom/ble_scan_parser.mojom
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module ble_scan_parser.mojom;
+
+import "device/bluetooth/public/mojom/uuid.mojom";
+
+// This is a parsed version of the BLE advertising packet. The data matches the
+// fields defined in the BLE specification.
+// https://bit.ly/2DUTnsk
+struct ScanRecord {
+  // Defines the discovery mode and EDR support
+  int8 advertising_flags;
+  // The transmit power in dBm
+  int8 tx_power;
+  // Device name
+  string advertisement_name;
+  // UUIDs for services offered.
+  array<bluetooth.mojom.UUID> service_uuids;
+  // Service data: 16-bit service UUID, service data
+  map<string, array<uint8>> service_data_map;
+  // Manufacturer data: company code, data
+  map<uint16, array<uint8>> manufacturer_data_map;
+};
+
+// This interface contains the raw data read from a BLE advertising packet.
+interface BleScanParser {
+  // Parses a scan record. Returns null, if the parsing fails.
+  Parse(array<uint8> advertising_data) => (ScanRecord? scan_record);
+};
diff --git a/chrome/services/ble_scan_parser/public/mojom/constants.mojom b/chrome/services/ble_scan_parser/public/mojom/constants.mojom
new file mode 100644
index 0000000..1263a28
--- /dev/null
+++ b/chrome/services/ble_scan_parser/public/mojom/constants.mojom
@@ -0,0 +1,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.
+
+module ble_scan_parser.mojom;
+
+const string kServiceName = "ble_scan_parser";
+const string kParseBleScanCapability = "parse_ble_scan";
diff --git a/chrome/services/cups_proxy/BUILD.gn b/chrome/services/cups_proxy/BUILD.gn
index fcfb01ac..21e5128 100644
--- a/chrome/services/cups_proxy/BUILD.gn
+++ b/chrome/services/cups_proxy/BUILD.gn
@@ -14,6 +14,8 @@
     "cups_proxy_service.h",
     "cups_proxy_service_delegate.cc",
     "cups_proxy_service_delegate.h",
+    "socket_manager.cc",
+    "socket_manager.h",
   ]
 
   deps = [
@@ -55,16 +57,23 @@
 source_set("unit_tests") {
   testonly = true
 
-  if (use_cups) {
-    sources = [
-      "printer_installer_unittest.cc",
-    ]
+  sources = [
+    "socket_manager_unittest.cc",
+  ]
 
-    deps = [
-      ":cups_proxy",
-      ":test_support",
-      "//base",
-      "//testing/gtest",
-    ]
+  deps = [
+    ":cups_proxy",
+    ":test_support",
+    "//base",
+    "//chrome/services/cups_proxy/public/cpp",
+    "//testing/gtest",
+  ]
+
+  if (use_cups) {
+    sources += [ "printer_installer_unittest.cc" ]
   }
+
+  data = [
+    "//chrome/test/data/cups_proxy",
+  ]
 }
diff --git a/chrome/services/cups_proxy/socket_manager.cc b/chrome/services/cups_proxy/socket_manager.cc
new file mode 100644
index 0000000..db4a11b
--- /dev/null
+++ b/chrome/services/cups_proxy/socket_manager.cc
@@ -0,0 +1,300 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/services/cups_proxy/socket_manager.h"
+
+#include <errno.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "base/task/post_task.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_checker.h"
+#include "chrome/services/cups_proxy/public/cpp/cups_util.h"
+#include "chrome/services/cups_proxy/public/cpp/type_conversions.h"
+#include "net/base/completion_repeating_callback.h"
+#include "net/base/io_buffer.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+#include "net/socket/unix_domain_client_socket_posix.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+
+namespace cups_proxy {
+namespace {
+
+// CUPS daemon socket path
+const char kCupsSocketPath[] = "/run/cups/cups.sock";
+
+// Returns true if |response_buffer| represents a full HTTP response.
+bool FinishedReadingResponse(const std::vector<uint8_t>& response_buffer) {
+  std::string response = ipp_converter::ConvertToString(response_buffer);
+  size_t end_of_headers =
+      net::HttpUtil::LocateEndOfHeaders(response.data(), response.size());
+  if (end_of_headers < 0) {
+    return false;
+  }
+
+  std::string raw_headers = net::HttpUtil::AssembleRawHeaders(
+      base::StringPiece(response.data(), end_of_headers));
+  auto parsed_headers =
+      base::MakeRefCounted<net::HttpResponseHeaders>(raw_headers);
+
+  // Check that response contains the full body.
+  size_t content_length = parsed_headers->GetContentLength();
+  if (content_length < 0) {
+    return false;
+  }
+
+  if (response.size() < end_of_headers + content_length) {
+    return false;
+  }
+
+  return true;
+}
+
+// POD representation of an in-flight socket request.
+struct SocketRequest {
+  // Explicitly declared/defined defaults since [chromium-style] flagged this as
+  // a complex struct.
+  SocketRequest();
+  SocketRequest(SocketRequest&& other);
+  ~SocketRequest();
+
+  // Used for writing/reading the IPP request/response.
+  std::vector<uint8_t> response;
+  scoped_refptr<net::DrainableIOBuffer> io_buffer;
+  SocketManagerCallback cb;
+};
+
+// All methods accessing |socket_| must be made on the IO thread.
+// TODO(luum): Consider inner IO-thread object, base::SequenceBound refactor.
+class SocketManagerImpl : public SocketManager {
+ public:
+  explicit SocketManagerImpl(
+      std::unique_ptr<net::UnixDomainClientSocket> socket);
+  ~SocketManagerImpl() override;
+
+  void ProxyToCups(std::vector<uint8_t> request,
+                   SocketManagerCallback cb) override;
+
+ private:
+  // These methods support lazily connecting to the CUPS socket.
+  void ConnectIfNeeded();
+  void Connect();
+  void OnConnect(int result);
+
+  void Write();
+  void OnWrite(int result);
+
+  void Read();
+  void OnRead(int result);
+
+  // Methods for ending a request.
+  void Finish(bool success = true);
+  void Fail(const char* error_message);
+
+  // Sequence this manager runs on, |in_flight_->cb_| is posted here.
+  scoped_refptr<base::SequencedTaskRunner> main_runner_;
+
+  // Single thread task runner the thread-affine |socket_| runs on.
+  scoped_refptr<base::SingleThreadTaskRunner> socket_runner_;
+
+  // Created in sequence but accessed and deleted on IO thread.
+  std::unique_ptr<SocketRequest> in_flight_;
+  std::unique_ptr<net::UnixDomainClientSocket> socket_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  base::WeakPtrFactory<SocketManagerImpl> weak_factory_;
+};
+
+// Defaults for SocketRequest.
+SocketRequest::SocketRequest() = default;
+SocketRequest::SocketRequest(SocketRequest&& other) = default;
+SocketRequest::~SocketRequest() = default;
+
+SocketManagerImpl::SocketManagerImpl(
+    std::unique_ptr<net::UnixDomainClientSocket> socket)
+    : socket_(std::move(socket)), weak_factory_(this) {
+  socket_runner_ =
+      base::CreateSingleThreadTaskRunnerWithTraits(base::TaskTraits());
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
+SocketManagerImpl::~SocketManagerImpl() {}
+
+void SocketManagerImpl::ProxyToCups(std::vector<uint8_t> request,
+                                    SocketManagerCallback cb) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!in_flight_);  // Only handles one request at a time.
+
+  // Lazily save the main runner, needed to post |cb| to.
+  if (!main_runner_) {
+    main_runner_ = base::SequencedTaskRunnerHandle::Get();
+  }
+
+  // Save request.
+  in_flight_ = std::make_unique<SocketRequest>();
+  in_flight_->cb = std::move(cb);
+
+  // Fill io_buffer with request to write.
+  in_flight_->io_buffer = base::MakeRefCounted<net::DrainableIOBuffer>(
+      base::MakeRefCounted<net::IOBuffer>(request.size()), request.size());
+  std::copy(request.begin(), request.end(), in_flight_->io_buffer->data());
+
+  socket_runner_->PostTask(FROM_HERE,
+                           base::BindOnce(&SocketManagerImpl::ConnectIfNeeded,
+                                          weak_factory_.GetWeakPtr()));
+}
+
+// Separate method since we need to check socket_ on the socket thread.
+void SocketManagerImpl::ConnectIfNeeded() {
+  DCHECK(socket_runner_->BelongsToCurrentThread());
+
+  // If |socket_| isn't connected yet, connect it.
+  if (!socket_->IsConnected()) {
+    return Connect();
+  }
+
+  // Write request to CUPS.
+  return Write();
+}
+
+void SocketManagerImpl::Connect() {
+  DCHECK(socket_runner_->BelongsToCurrentThread());
+
+  int result = socket_->Connect(base::BindOnce(&SocketManagerImpl::OnConnect,
+                                               weak_factory_.GetWeakPtr()));
+  if (result != net::ERR_IO_PENDING) {
+    return OnConnect(result);
+  }
+}
+
+void SocketManagerImpl::OnConnect(int result) {
+  DCHECK(socket_runner_->BelongsToCurrentThread());
+  DCHECK(in_flight_);
+
+  if (result < 0) {
+    return Fail("Failed to connect to the CUPS daemon.");
+  }
+
+  return Write();
+}
+
+void SocketManagerImpl::Write() {
+  DCHECK(socket_runner_->BelongsToCurrentThread());
+
+  int result = socket_->Write(
+      in_flight_->io_buffer.get(), in_flight_->io_buffer->BytesRemaining(),
+      base::BindOnce(&SocketManagerImpl::OnWrite, weak_factory_.GetWeakPtr()),
+      TRAFFIC_ANNOTATION_FOR_TESTS /* Unused NetworkAnnotation */);
+
+  if (result != net::ERR_IO_PENDING) {
+    return OnWrite(result);
+  }
+}
+
+void SocketManagerImpl::OnWrite(int result) {
+  DCHECK(socket_runner_->BelongsToCurrentThread());
+  DCHECK(in_flight_);
+
+  if (result < 0) {
+    return Fail("Failed to write IPP request to the CUPS daemon.");
+  }
+
+  if (result < in_flight_->io_buffer->BytesRemaining()) {
+    in_flight_->io_buffer->DidConsume(result);
+    return Write();
+  }
+
+  // Prime io_buffer for reading.
+  in_flight_->io_buffer = base::MakeRefCounted<net::DrainableIOBuffer>(
+      base::MakeRefCounted<net::IOBuffer>(kHttpMaxBufferSize),
+      kHttpMaxBufferSize);
+
+  // Start reading response from CUPS.
+  return Read();
+}
+
+void SocketManagerImpl::Read() {
+  DCHECK(socket_runner_->BelongsToCurrentThread());
+
+  int result = socket_->Read(
+      in_flight_->io_buffer.get(), kHttpMaxBufferSize,
+      base::BindOnce(&SocketManagerImpl::OnRead, weak_factory_.GetWeakPtr()));
+
+  if (result != net::ERR_IO_PENDING) {
+    return OnRead(result);
+  }
+}
+
+void SocketManagerImpl::OnRead(int num_read) {
+  DCHECK(socket_runner_->BelongsToCurrentThread());
+  DCHECK(in_flight_);
+
+  if (num_read < 0) {
+    return Fail("Failed to read IPP response from the CUPS daemon.");
+  }
+
+  // Save new response data.
+  std::copy(in_flight_->io_buffer->data(),
+            in_flight_->io_buffer->data() + num_read,
+            std::back_inserter(in_flight_->response));
+
+  // If more response left to read, read more.
+  if (!FinishedReadingResponse(in_flight_->response)) {
+    return Read();
+  }
+
+  // Finished, got response.
+  return Finish();
+}
+
+void SocketManagerImpl::Finish(bool success) {
+  DCHECK(socket_runner_->BelongsToCurrentThread());
+
+  base::OnceClosure cb;
+  if (success) {
+    cb = base::BindOnce(std::move(in_flight_->cb),
+                        std::move(in_flight_->response));
+  } else {
+    cb = base::BindOnce(std::move(in_flight_->cb), base::nullopt);
+  }
+
+  // Post callback back to main sequence.
+  main_runner_->PostTask(FROM_HERE, std::move(cb));
+
+  // Discard this request while still on IO thread.
+  in_flight_.reset();
+}
+
+void SocketManagerImpl::Fail(const char* error_message) {
+  DVLOG(1) << "SocketManager Error: " << error_message;
+  return Finish(false /* Fail this request */);
+}
+
+}  // namespace
+
+std::unique_ptr<SocketManager> SocketManager::Create() {
+  return std::make_unique<SocketManagerImpl>(
+      std::make_unique<net::UnixDomainClientSocket>(
+          kCupsSocketPath, false /* not abstract namespace */));
+}
+
+std::unique_ptr<SocketManager> SocketManager::CreateForTesting(
+    std::unique_ptr<net::UnixDomainClientSocket> socket) {
+  return std::make_unique<SocketManagerImpl>(std::move(socket));
+}
+
+}  // namespace cups_proxy
diff --git a/chrome/services/cups_proxy/socket_manager.h b/chrome/services/cups_proxy/socket_manager.h
new file mode 100644
index 0000000..04d98d80e
--- /dev/null
+++ b/chrome/services/cups_proxy/socket_manager.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 CHROME_SERVICES_CUPS_PROXY_SOCKET_MANAGER_H_
+#define CHROME_SERVICES_CUPS_PROXY_SOCKET_MANAGER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/optional.h"
+
+namespace net {
+class UnixDomainClientSocket;
+}  // namespace net
+
+namespace cups_proxy {
+
+using SocketManagerCallback =
+    base::OnceCallback<void(base::Optional<std::vector<uint8_t>>)>;
+
+// This manager proxies IPP requests to the CUPS daemon and asynchronously
+// responds with the IPP response. This class can be created anywhere but must
+// be accessed from a sequenced context.
+class SocketManager {
+ public:
+  // Factory function.
+  static std::unique_ptr<SocketManager> Create();
+
+  // Factory function that allows injected dependencies, for testing.
+  static std::unique_ptr<SocketManager> CreateForTesting(
+      std::unique_ptr<net::UnixDomainClientSocket> socket);
+
+  virtual ~SocketManager() = default;
+
+  // Attempts to send |request| to the CUPS Daemon, and return its response via
+  // |cb|. |cb| will run on the caller's sequence. Note: Can only handle 1
+  // inflight request at a time; attempts to proxy more will DCHECK.
+  virtual void ProxyToCups(std::vector<uint8_t> request,
+                           SocketManagerCallback cb) = 0;
+};
+
+}  // namespace cups_proxy
+
+#endif  // CHROME_SERVICES_CUPS_PROXY_SOCKET_MANAGER_H_
diff --git a/chrome/services/cups_proxy/socket_manager_unittest.cc b/chrome/services/cups_proxy/socket_manager_unittest.cc
new file mode 100644
index 0000000..0ef2175c
--- /dev/null
+++ b/chrome/services/cups_proxy/socket_manager_unittest.cc
@@ -0,0 +1,245 @@
+// 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 <algorithm>
+#include <string>
+#include <utility>
+
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_restrictions.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/services/cups_proxy/public/cpp/type_conversions.h"
+#include "chrome/services/cups_proxy/socket_manager.h"
+#include "net/base/io_buffer.h"
+#include "net/socket/unix_domain_client_socket_posix.h"
+#include "net/test/test_with_scoped_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cups_proxy {
+namespace {
+
+// CupsProxy testing data relative path.
+const base::FilePath::CharType kCupsProxyDataDirectory[] =
+    FILE_PATH_LITERAL("cups_proxy");
+
+// Returns base::nullopt on failure.
+base::Optional<std::string> GetTestFile(std::string test_name) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+
+  // Build file path.
+  base::FilePath path;
+  if (!base::PathService::Get(chrome::DIR_TEST_DATA, &path)) {
+    return base::nullopt;
+  }
+
+  path = path.Append(kCupsProxyDataDirectory)
+             .Append(FILE_PATH_LITERAL(test_name))
+             .AddExtension(FILE_PATH_LITERAL(".bin"));
+
+  // Read in file contents.
+  std::string contents;
+  if (!base::ReadFileToString(path, &contents)) {
+    return base::nullopt;
+  }
+
+  return contents;
+}
+
+}  // namespace
+
+// Gives full control over the "CUPS daemon" in this test.
+class FakeSocket : public net::UnixDomainClientSocket {
+ public:
+  FakeSocket() : UnixDomainClientSocket("", false) /* Dummy values */ {}
+  ~FakeSocket() override = default;
+
+  // Saves expected request and corresponding response to send back.
+  void set_request(base::StringPiece request) { request_ = request; }
+  void set_response(base::StringPiece response) { response_ = response; }
+
+  // Controls whether each method runs synchronously or asynchronously.
+  void set_connect_async() { connect_async = true; }
+  void set_read_async() { read_async = true; }
+  void set_write_async() { write_async = true; }
+
+  // net::UnixDomainClientSocket overrides.
+  bool IsConnected() const override { return is_connected; }
+
+  int Connect(net::CompletionOnceCallback callback) override {
+    if (is_connected) {
+      // Should've checked IsConnected first.
+      return net::ERR_FAILED;
+    }
+
+    is_connected = true;
+
+    // Sync
+    if (!connect_async) {
+      return net::OK;
+    }
+
+    // Async
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&FakeSocket::OnAsyncCallback, base::Unretained(this),
+                       std::move(callback), net::OK));
+    return net::ERR_IO_PENDING;
+  }
+
+  int Read(net::IOBuffer* buf,
+           int buf_len,
+           net::CompletionOnceCallback callback) override {
+    if (!is_connected) {
+      return net::ERR_FAILED;
+    }
+
+    size_t num_to_read =
+        std::min(response_.size(), static_cast<size_t>(buf_len));
+    std::copy(response_.begin(), response_.begin() + num_to_read, buf->data());
+    response_.remove_prefix(num_to_read);
+
+    // Sync
+    if (!read_async) {
+      return num_to_read;
+    }
+
+    // Async
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&FakeSocket::OnAsyncCallback, base::Unretained(this),
+                       std::move(callback), num_to_read));
+    return net::ERR_IO_PENDING;
+  }
+
+  int Write(net::IOBuffer* buf,
+            int buf_len,
+            net::CompletionOnceCallback callback,
+            const net::NetworkTrafficAnnotationTag& unused) override {
+    if (!is_connected) {
+      return net::ERR_FAILED;
+    }
+
+    // Checks that |buf| holds (part of) the expected request.
+    if (!std::equal(buf->data(), buf->data() + buf_len, request_.begin())) {
+      return net::ERR_FAILED;
+    }
+
+    // Arbitrary maximum write buffer size; just forcing partial writes.
+    const size_t kMaxWriteSize = 100;
+    size_t num_to_write = std::min(kMaxWriteSize, static_cast<size_t>(buf_len));
+    request_.remove_prefix(num_to_write);
+
+    // Sync
+    if (!write_async) {
+      return num_to_write;
+    }
+
+    // Async
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&FakeSocket::OnAsyncCallback, base::Unretained(this),
+                       std::move(callback), num_to_write));
+    return net::ERR_IO_PENDING;
+  }
+
+  // Generic callback used to force called methods to return asynchronously.
+  void OnAsyncCallback(net::CompletionOnceCallback callback, int net_code) {
+    std::move(callback).Run(net_code);
+  }
+
+ private:
+  bool is_connected = false;
+  bool connect_async = false, read_async = false, write_async = false;
+  base::StringPiece request_, response_;
+};
+
+class SocketManagerTest : public testing::Test {
+ public:
+  SocketManagerTest() : weak_factory_(this) {
+    std::unique_ptr<FakeSocket> socket = std::make_unique<FakeSocket>();
+    socket_ = socket.get();
+
+    manager_ = SocketManager::CreateForTesting(std::move(socket));
+  }
+
+  base::Optional<std::vector<uint8_t>> ProxyToCups(std::string request) {
+    std::vector<uint8_t> request_as_bytes =
+        ipp_converter::ConvertToByteBuffer(request);
+    base::Optional<std::vector<uint8_t>> response;
+
+    base::RunLoop run_loop;
+    manager_->ProxyToCups(std::move(request_as_bytes),
+                          base::BindOnce(&SocketManagerTest::OnProxyToCups,
+                                         weak_factory_.GetWeakPtr(),
+                                         run_loop.QuitClosure(), &response));
+    run_loop.Run();
+    return response;
+  }
+
+ protected:
+  // Must be first member.
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  void OnProxyToCups(base::OnceClosure finish_cb,
+                     base::Optional<std::vector<uint8_t>>* ret,
+                     base::Optional<std::vector<uint8_t>> result) {
+    *ret = std::move(result);
+    std::move(finish_cb).Run();
+  }
+
+  // Not owned.
+  FakeSocket* socket_;
+
+  std::unique_ptr<SocketManager> manager_;
+  base::WeakPtrFactory<SocketManagerTest> weak_factory_;
+};
+
+// "basic_handshake" test file contains a simple HTTP request sent by libCUPS,
+// copied below for convenience:
+//
+// POST / HTTP/1.1
+// Content-Length: 72
+// Content-Type: application/ipp
+// Date: Thu, 04 Oct 2018 20:25:59 GMT
+// Host: localhost:0
+// User-Agent: CUPS/2.3b1 (Linux 4.4.159-15303-g65f4b5a7b3d3; i686) IPP/2.0
+//
+// @Gattributes-charsetutf-8Hattributes-natural-languageen
+
+// All socket accesses are resolved synchronously.
+TEST_F(SocketManagerTest, SyncEverything) {
+  // Read request & response
+  base::Optional<std::string> http_handshake = GetTestFile("basic_handshake");
+  EXPECT_TRUE(http_handshake);
+
+  // Pre-load |socket_| with request/response.
+  // TODO(crbug.com/495409): Test with actual http response.
+  socket_->set_request(*http_handshake);
+  socket_->set_response(*http_handshake);
+
+  auto response = ProxyToCups(*http_handshake);
+  EXPECT_TRUE(response);
+  EXPECT_EQ(*response, ipp_converter::ConvertToByteBuffer(*http_handshake));
+}
+
+TEST_F(SocketManagerTest, AsyncEverything) {
+  auto http_handshake = GetTestFile("basic_handshake");
+  EXPECT_TRUE(http_handshake);
+
+  socket_->set_request(*http_handshake);
+  socket_->set_response(*http_handshake);
+
+  // Set all |socket_| calls to run asynchronously.
+  socket_->set_connect_async();
+  socket_->set_read_async();
+  socket_->set_write_async();
+
+  auto response = ProxyToCups(*http_handshake);
+  EXPECT_TRUE(response);
+  EXPECT_EQ(*response, ipp_converter::ConvertToByteBuffer(*http_handshake));
+}
+
+}  // namespace cups_proxy
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index fc059a48..e597684 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3318,7 +3318,6 @@
     "//components/sync_user_events:test_support",
     "//components/ukm/content",
     "//components/version_info:generate_version_info",
-    "//components/webdata_services:test_support",
     "//content/app/resources",
     "//content/public/app:both",
     "//content/test:test_support",
@@ -3443,6 +3442,7 @@
       "../browser/password_manager/password_accessory_controller_impl_unittest.cc",
       "../browser/password_manager/password_generation_controller_impl_unittest.cc",
       "../browser/password_manager/save_password_infobar_delegate_android_unittest.cc",
+      "../browser/password_manager/touch_to_fill_controller_unittest.cc",
       "../browser/password_manager/update_password_infobar_delegate_android_unittest.cc",
       "../browser/translate/translate_manager_render_view_host_android_unittest.cc",
     ]
@@ -3914,7 +3914,7 @@
       "../browser/ui/ash/launcher/arc_app_shelf_id_unittest.cc",
       "../browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc",
       "../browser/ui/ash/launcher/launcher_context_menu_unittest.cc",
-      "../browser/ui/ash/media_client_unittest.cc",
+      "../browser/ui/ash/media_client_impl_unittest.cc",
       "../browser/ui/ash/multi_user/multi_profile_support_unittest.cc",
       "../browser/ui/ash/multi_user/multi_user_context_menu_chromeos_unittest.cc",
       "../browser/ui/ash/multi_user/multi_user_util_chromeos_unittest.cc",
@@ -3932,6 +3932,7 @@
       "//ash/public/cpp/resources:ash_public_unscaled_resources",
       "//ash/strings",
       "//chrome/browser/resources/chromeos/zip_archiver/cpp:ziparchiver_unittests",
+      "//chrome/services/ble_scan_parser:unit_tests",
     ]
     if (use_new_tcmalloc) {
       sources += [ "../browser/metrics/perf/heap_collector_unittest.cc" ]
@@ -4239,6 +4240,7 @@
       "../common/extensions/manifest_handlers/content_scripts_manifest_unittest.cc",
       "../common/extensions/manifest_handlers/exclude_matches_manifest_unittest.cc",
       "../common/extensions/manifest_handlers/extension_action_handler_unittest.cc",
+      "../common/extensions/manifest_handlers/natively_connectable_handler_unittest.cc",
       "../common/extensions/manifest_handlers/settings_overrides_handler_unittest.cc",
       "../common/extensions/manifest_handlers/ui_overrides_handler_unittest.cc",
       "../common/extensions/manifest_tests/chrome_manifest_test.cc",
@@ -4574,6 +4576,7 @@
       "//chrome/services/cups_proxy:unit_tests",
       "//chromeos/ime:gencode",
     ]
+    data_deps += [ "//testing/buildbot/filters:chromeos_filters" ]
     sources -=
         [ "../browser/policy/cloud/user_policy_signin_service_unittest.cc" ]
   }
@@ -5418,6 +5421,7 @@
         "../browser/ui/ash/drag_to_overview_interactive_uitest.cc",
         "../browser/ui/ash/launcher_animations_interactive_uitest.cc",
         "../browser/ui/ash/launcher_drag_interactive_uitest.cc",
+        "../browser/ui/ash/launcher_page_switches_interactive_uitest.cc",
         "../browser/ui/ash/overview_animations_interactive_uitest.cc",
         "../browser/ui/ash/overview_window_drag_interactive_uitest.cc",
         "../browser/ui/ash/screen_rotation_interactive_uitest.cc",
diff --git a/chrome/test/chromedriver/BUILD.gn b/chrome/test/chromedriver/BUILD.gn
index 54d09b3..a6c410f 100644
--- a/chrome/test/chromedriver/BUILD.gn
+++ b/chrome/test/chromedriver/BUILD.gn
@@ -278,6 +278,7 @@
     "//base/third_party/dynamic_annotations",
     "//chrome/common:constants",
     "//chrome/common:version_header",
+    "//components/crx_file",
     "//components/version_info:generate_version_info",
     "//crypto",
     "//net",
diff --git a/chrome/test/chromedriver/DEPS b/chrome/test/chromedriver/DEPS
index f936602..d64f25e5c 100644
--- a/chrome/test/chromedriver/DEPS
+++ b/chrome/test/chromedriver/DEPS
@@ -10,6 +10,8 @@
 
   "+chrome/test/chromedriver",
 
+  "+components/crx_file",
+
   "+third_party/webdriver",
   "+third_party/zlib",
 ]
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.cc b/chrome/test/chromedriver/chrome/web_view_impl.cc
index 740ef9e..383a493 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl.cc
@@ -996,8 +996,8 @@
   async_args.AppendString("return (" + function + ").apply(null, arguments);");
   async_args.Append(args.CreateDeepCopy());
   async_args.AppendBoolean(is_user_supplied);
-  async_args.AppendInteger(timeout.InMilliseconds());
   std::unique_ptr<base::Value> tmp;
+  Timeout local_timeout(timeout);
   Status status = CallFunctionWithTimeout(frame, kExecuteAsyncScriptScript,
                                           async_args, timeout, &tmp);
   if (status.IsError())
@@ -1017,6 +1017,7 @@
       "}",
       kJavaScriptError,
       kDocUnloadError);
+  const base::TimeDelta kOneHundredMs = base::TimeDelta::FromMilliseconds(100);
 
   while (true) {
     base::ListValue no_args;
@@ -1046,7 +1047,12 @@
       return Status(kOk);
     }
 
-    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
+    // Since async-scripts return immediately, need to time period here instead.
+    if (local_timeout.IsExpired())
+      return Status(kTimeout);
+
+    base::PlatformThread::Sleep(
+        std::min(kOneHundredMs, local_timeout.GetRemainingTime()));
   }
 }
 
diff --git a/chrome/test/chromedriver/chrome_launcher.cc b/chrome/test/chromedriver/chrome_launcher.cc
index fff7e05..ec0e1e0e5 100644
--- a/chrome/test/chromedriver/chrome_launcher.cc
+++ b/chrome/test/chromedriver/chrome_launcher.cc
@@ -52,6 +52,7 @@
 #include "chrome/test/chromedriver/log_replay/chrome_replay_impl.h"
 #include "chrome/test/chromedriver/log_replay/replay_http_client.h"
 #include "chrome/test/chromedriver/net/net_util.h"
+#include "components/crx_file/crx_verifier.h"
 #include "crypto/rsa_private_key.h"
 #include "crypto/sha2.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
@@ -797,24 +798,34 @@
   if (!base::Base64Decode(extension_base64, &decoded_extension))
     return Status(kUnknownError, "cannot base64 decode");
 
+  base::ScopedTempDir temp_crx_dir;
+  if (!temp_crx_dir.CreateUniqueTempDir())
+    return Status(kUnknownError, "cannot create temp dir");
+  base::FilePath extension_crx = temp_crx_dir.GetPath().AppendASCII("temp.crx");
+  int size = static_cast<int>(decoded_extension.length());
+  if (base::WriteFile(extension_crx, decoded_extension.c_str(), size) != size) {
+    return Status(kUnknownError, "cannot write file");
+  }
+
   // If the file is a crx file, extract the extension's ID from its public key.
   // Otherwise generate a random public key and use its derived extension ID.
-  std::string public_key;
+  std::string public_key_base64;
   std::string magic_header = decoded_extension.substr(0, 4);
   if (magic_header.size() != 4)
     return Status(kUnknownError, "cannot extract magic number");
 
   const bool is_crx_file = magic_header == "Cr24";
+  std::string id;
 
   if (is_crx_file) {
-    // Assume a CRX v2 file - see https://developer.chrome.com/extensions/crx.
-    std::string key_len_str = decoded_extension.substr(8, 4);
-    if (key_len_str.size() != 4)
-      return Status(kUnknownError, "cannot extract public key length");
-    uint32_t key_len = *reinterpret_cast<const uint32_t*>(key_len_str.c_str());
-    public_key = decoded_extension.substr(16, key_len);
-    if (key_len != public_key.size())
-      return Status(kUnknownError, "invalid public key length");
+    crx_file::VerifierResult result =
+        crx_file::Verify(extension_crx, crx_file::VerifierFormat::CRX2_OR_CRX3,
+                         {} /** required_key_hashes */,
+                         {} /** required_file_hash */, &public_key_base64, &id);
+    if (result != crx_file::VerifierResult::OK_FULL) {
+      return Status(kUnknownError,
+                    base::StringPrintf("CRX verification failed: %d", result));
+    }
   } else {
     // Not a CRX file. Generate RSA keypair to get a valid extension id.
     std::unique_ptr<crypto::RSAPrivateKey> key_pair(
@@ -824,24 +835,14 @@
     std::vector<uint8_t> public_key_vector;
     if (!key_pair->ExportPublicKey(&public_key_vector))
       return Status(kUnknownError, "cannot extract public key");
-    public_key =
+    std::string public_key =
         std::string(reinterpret_cast<char*>(&public_key_vector.front()),
                     public_key_vector.size());
+    id = GenerateExtensionId(public_key);
+    base::Base64Encode(public_key, &public_key_base64);
   }
-  std::string public_key_base64;
-  base::Base64Encode(public_key, &public_key_base64);
-  std::string id = GenerateExtensionId(public_key);
 
   // Unzip the crx file.
-  base::ScopedTempDir temp_crx_dir;
-  if (!temp_crx_dir.CreateUniqueTempDir())
-    return Status(kUnknownError, "cannot create temp dir");
-  base::FilePath extension_crx = temp_crx_dir.GetPath().AppendASCII("temp.crx");
-  int size = static_cast<int>(decoded_extension.length());
-  if (base::WriteFile(extension_crx, decoded_extension.c_str(), size) !=
-      size) {
-    return Status(kUnknownError, "cannot write file");
-  }
   base::FilePath extension_dir = temp_dir.AppendASCII("extension_" + id);
   if (!zip::Unzip(extension_crx, extension_dir))
     return Status(kUnknownError, "cannot unzip");
diff --git a/chrome/test/chromedriver/chrome_launcher_unittest.cc b/chrome/test/chromedriver/chrome_launcher_unittest.cc
index 1f4d385..51813ae71 100644
--- a/chrome/test/chromedriver/chrome_launcher_unittest.cc
+++ b/chrome/test/chromedriver/chrome_launcher_unittest.cc
@@ -74,6 +74,27 @@
             "_generated_background_page.html", bg_pages[2]);
 }
 
+TEST(ProcessExtensions, GenerateIdCrx3) {
+  std::vector<std::string> extensions;
+  base::ScopedTempDir extension_dir;
+  Switches switches;
+  std::vector<std::string> bg_pages;
+
+  ASSERT_TRUE(AddExtensionForInstall("same_key_as_header.crx3", &extensions));
+
+  ASSERT_TRUE(extension_dir.CreateUniqueTempDir());
+
+  Status status = internal::ProcessExtensions(
+      extensions, extension_dir.GetPath(), false, &switches, &bg_pages);
+
+  ASSERT_EQ(kOk, status.code()) << status.message();
+  ASSERT_EQ(1u, bg_pages.size());
+  ASSERT_EQ(
+      "chrome-extension://dfdeoklpcichfcnoaomfpagfiibhomnh/"
+      "_generated_background_page.html",
+      bg_pages[0]);
+}
+
 TEST(ProcessExtensions, SingleExtensionWithBgPage) {
   std::vector<std::string> extensions;
   ASSERT_TRUE(AddExtensionForInstall("ext_slow_loader.crx", &extensions));
diff --git a/chrome/test/chromedriver/js/execute_async_script.js b/chrome/test/chromedriver/js/execute_async_script.js
index b9dc8ab..44a6fd8 100644
--- a/chrome/test/chromedriver/js/execute_async_script.js
+++ b/chrome/test/chromedriver/js/execute_async_script.js
@@ -45,17 +45,22 @@
 *     If not, UnknownError will be used instead of JavaScriptError if an
 *     exception occurs during the script, and an additional error callback will
 *     be supplied to the script.
-* @param {?number} opt_timeoutMillis The timeout, in milliseconds, to use.
-*     If the timeout is exceeded and the callback has not been invoked, a error
-*     result will be saved and future invocation of the callback will be
-*     ignored.
 */
-function executeAsyncScript(script, args, isUserSupplied, opt_timeoutMillis) {
-  var info = getAsyncScriptInfo();
+function executeAsyncScript(script, args, isUserSupplied) {
+  let resolveHandle;
+  let rejectHandle;
+  var promise = new Promise((resolve, reject) => {
+    resolveHandle = resolve;
+    rejectHandle = reject;
+  });
+  const info = getAsyncScriptInfo();
   info.id++;
   delete info.result;
-  var id = info.id;
+  const id = info.id;
 
+  function isThenable(value) {
+    return typeof value === 'object' && typeof value.then === 'function';
+  }
   function report(status, value) {
     if (id != info.id)
       return;
@@ -78,24 +83,23 @@
     }
     report(code, message);
   }
-  args.push(reportValue);
+  promise.then(reportValue).catch(reportScriptError);
+  args.push(resolveHandle);
   if (!isUserSupplied)
-    args.push(reportScriptError);
-
+    args.push(rejectHandle);
   try {
-    new Function(script).apply(null, args);
+    const scriptResult = new Function(script).apply(null, args);
+    // The return value is only considered if it is a promise.
+    if (isThenable(scriptResult)) {
+      const resolvedPromise = Promise.resolve(scriptResult);
+      resolvedPromise.then((value) => {
+        // Must be thenable if user-supplied.
+        if (!isUserSupplied || isThenable(value))
+          resolveHandle(value);
+      })
+      .catch(rejectHandle);
+    }
   } catch (error) {
-    reportScriptError(error);
-    return;
-  }
-
-  if (typeof(opt_timeoutMillis) != 'undefined') {
-    window.setTimeout(function() {
-      var code = isUserSupplied ? StatusCode.SCRIPT_TIMEOUT :
-                                  StatusCode.UNKNOWN_ERROR;
-      var errorMsg = 'result was not received in ' + opt_timeoutMillis / 1000 +
-                     ' seconds';
-      report(code, errorMsg);
-    }, opt_timeoutMillis);
+    rejectHandle(error);
   }
 }
diff --git a/chrome/test/chromedriver/js/execute_async_script_test.html b/chrome/test/chromedriver/js/execute_async_script_test.html
index d1ebe26..1c4ff5d 100644
--- a/chrome/test/chromedriver/js/execute_async_script_test.html
+++ b/chrome/test/chromedriver/js/execute_async_script_test.html
@@ -8,17 +8,37 @@
   delete document[ASYNC_INFO_KEY];
 }
 
-function testScriptThrows() {
-  resetAsyncScriptInfo();
-  var info = getAsyncScriptInfo();
-
-  executeAsyncScript('f(123);', [], true);
-  assertEquals(StatusCode.JAVASCRIPT_ERROR, info.result.status);
-  executeAsyncScript('f(123);', [], false);
-  assertEquals(StatusCode.UNKNOWN_ERROR, info.result.status);
+function waitForResultToPopulate(callback) {
+  const info = getAsyncScriptInfo();
+  setTimeout(() => {
+    if (info.result)
+      callback(info);
+    else
+      waitForResultToPopulate(callback);
+  }, 10);
 }
 
-function testUserScriptWithArgs() {
+function testUserScriptThrows(runner) {
+  resetAsyncScriptInfo();
+  executeAsyncScript('f(123);', [], true);
+  waitForResultToPopulate((info) => {
+    assertEquals(StatusCode.JAVASCRIPT_ERROR, info.result.status);
+    runner.continueTesting();
+  });
+  runner.waitForAsync();
+}
+
+function testScriptThrows(runner) {
+  resetAsyncScriptInfo();
+  executeAsyncScript('f(123);', [], false);
+  waitForResultToPopulate((info) => {
+    assertEquals(StatusCode.UNKNOWN_ERROR, info.result.status);
+    runner.continueTesting();
+  });
+  runner.waitForAsync();
+}
+
+function testUserScriptWithArgs(runner) {
   resetAsyncScriptInfo();
 
   var injectedArgs = null;
@@ -31,93 +51,54 @@
       'var args = arguments; args[0](args); args[args.length - 1](args[1]);';
   var script_args = [captureArguments, 1];
   executeAsyncScript(script, script_args, true);
+  waitForResultToPopulate((info) => {
+    assertEquals(3, injectedArgs.length);
+    assertEquals(captureArguments, injectedArgs[0]);
+    assertEquals(1, injectedArgs[1]);
 
-  assertEquals(3, injectedArgs.length);
-  assertEquals(captureArguments, injectedArgs[0]);
-  assertEquals(1, injectedArgs[1]);
-
-  var info = getAsyncScriptInfo();
-  assertEquals(0, info.result.status);
-  assertEquals(1, info.result.value);
-  assertEquals(2, info.id);
+    assertEquals(0, info.result.status);
+    assertEquals(1, info.result.value);
+    assertEquals(2, info.id);
+    runner.continueTesting();
+  });
+  runner.waitForAsync();
 }
 
-function testNonUserScript() {
+function testNonUserScriptCallback(runner) {
   resetAsyncScriptInfo();
-  var info = getAsyncScriptInfo();
-
   executeAsyncScript('arguments[1](arguments[0])', [33], false);
-  assertEquals(0, info.result.status);
-  assertEquals(33, info.result.value);
-
-  executeAsyncScript('arguments[2](new Error("ERR"))', [33], false);
-  assertEquals(StatusCode.UNKNOWN_ERROR, info.result.status);
-  assertEquals(0, info.result.value.indexOf('ERR'));
-
-  executeAsyncScript('var e = new Error("ERR"); e.code = 111; arguments[1](e)',
-                     [], false);
-  assertEquals(111, info.result.status);
-  assertEquals(0, info.result.value.indexOf('ERR'));
-}
-
-function testNoResultBeforeTimeout() {
-  resetAsyncScriptInfo();
-  var info = getAsyncScriptInfo();
-
-  executeAsyncScript(
-      'var a = arguments; window.setTimeout(function() {a[0](33)}, 0);',
-      [], true, 0);
-
-  assert(!info.result);
-}
-
-function testZeroTimeout(runner) {
-  resetAsyncScriptInfo();
-  var info = getAsyncScriptInfo();
-
-  executeAsyncScript(
-      'var a = arguments; window.setTimeout(function() {a[0](33)}, 0);',
-      [], true, 0);
-
-  window.setTimeout(function() {
+  waitForResultToPopulate((info) => {
     assertEquals(0, info.result.status);
     assertEquals(33, info.result.value);
     runner.continueTesting();
-  }, 0);
+  });
   runner.waitForAsync();
 }
 
-function testUserScriptTimesOut(runner) {
+function testNonUserScriptCustomError(runner) {
   resetAsyncScriptInfo();
-  var info = getAsyncScriptInfo();
-
-  executeAsyncScript('', [], true, 500);
-
-  window.setTimeout(function() {
-    assertEquals(StatusCode.SCRIPT_TIMEOUT, info.result.status);
-    assert(info.result.value.indexOf('0.5') != -1);
-    runner.continueTesting();
-  }, 500);
-
-  runner.waitForAsync();
-}
-
-function testNonUserScriptTimesOut(runner) {
-  resetAsyncScriptInfo();
-  var info = getAsyncScriptInfo();
-
-  executeAsyncScript('', [], false, 500);
-
-  window.setTimeout(function() {
+  executeAsyncScript('arguments[2](new Error("ERR"))', [33], false);
+  waitForResultToPopulate((info) => {
     assertEquals(StatusCode.UNKNOWN_ERROR, info.result.status);
-    assert(info.result.value.indexOf('0.5') != -1);
+    assertEquals(0, info.result.value.indexOf('ERR'));
     runner.continueTesting();
-  }, 500);
-
+  });
   runner.waitForAsync();
 }
 
-function testFirstScriptFinishAfterSecondScriptExecute() {
+function testNonUserScriptCustomErrorCode(runner) {
+  resetAsyncScriptInfo();
+  executeAsyncScript('var e = new Error("ERR"); e.code = 111; arguments[1](e)',
+                     [], false);
+  waitForResultToPopulate((info) => {
+    assertEquals(111, info.result.status);
+    assertEquals(0, info.result.value.indexOf('ERR'));
+    runner.continueTesting();
+  });
+  runner.waitForAsync();
+}
+
+function testFirstScriptFinishAfterSecondScriptExecute(runner) {
   resetAsyncScriptInfo();
 
   executeAsyncScript(
@@ -127,9 +108,13 @@
   assertEquals(1, info.id);
 
   executeAsyncScript('var fn = arguments[0]; fn(2);', []);
-  assertEquals(0, info.result.status);
-  assertEquals(2, info.result.value);
-  assertEquals(3, info.id);
+  waitForResultToPopulate((info) => {
+    assertEquals(0, info.result.status);
+    assertEquals(2, info.result.value);
+    assertEquals(3, info.id);
+    runner.continueTesting();
+  });
+  runner.waitForAsync();
 }
 
 </script>
diff --git a/chrome/test/chromedriver/window_commands.cc b/chrome/test/chromedriver/window_commands.cc
index a7ea4bb..acf17a8f 100644
--- a/chrome/test/chromedriver/window_commands.cc
+++ b/chrome/test/chromedriver/window_commands.cc
@@ -601,7 +601,7 @@
     script = script + "\n";
 
   Status status = web_view->CallUserAsyncFunction(
-      session->GetCurrentFrameId(), "function(){" + script + "}", *args,
+      session->GetCurrentFrameId(), "async function(){" + script + "}", *args,
       session->script_timeout, value);
   if (status.code() == kTimeout)
     return Status(kScriptTimeout);
diff --git a/chrome/test/data/chromedriver/same_key_as_header.crx3 b/chrome/test/data/chromedriver/same_key_as_header.crx3
new file mode 100644
index 0000000..14b99b3
--- /dev/null
+++ b/chrome/test/data/chromedriver/same_key_as_header.crx3
Binary files differ
diff --git a/chrome/test/data/chromeos/file_manager/plaintext b/chrome/test/data/chromeos/file_manager/plaintext
new file mode 100644
index 0000000..5368ac1d
--- /dev/null
+++ b/chrome/test/data/chromeos/file_manager/plaintext
@@ -0,0 +1 @@
+From somewhere a long time ago.
diff --git a/chrome/test/data/cups_proxy/basic_handshake.bin b/chrome/test/data/cups_proxy/basic_handshake.bin
new file mode 100644
index 0000000..79bd8ac
--- /dev/null
+++ b/chrome/test/data/cups_proxy/basic_handshake.bin
Binary files differ
diff --git a/chrome/test/data/extensions/api_test/tabs/lazy_background_on_created/manifest.json b/chrome/test/data/extensions/api_test/tabs/lazy_background_on_created/manifest.json
new file mode 100644
index 0000000..b3510f9d
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/tabs/lazy_background_on_created/manifest.json
@@ -0,0 +1,10 @@
+{
+  "name": "tabs/lazy_background_on_created",
+  "version": "0.1",
+  "manifest_version": 2,
+  "description": "Tests chrome.tabs.onCreated from event page.",
+  "background": {
+    "scripts": ["test.js"],
+    "persistent": false
+  }
+}
diff --git a/chrome/test/data/extensions/api_test/tabs/lazy_background_on_created/test.js b/chrome/test/data/extensions/api_test/tabs/lazy_background_on_created/test.js
new file mode 100644
index 0000000..b8c4683
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/tabs/lazy_background_on_created/test.js
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+chrome.test.runTests([
+  function testCreateWithActiveTrue() {
+    chrome.test.listenOnce(chrome.tabs.onCreated,
+      function(tab) {
+        chrome.test.assertEq(tab.active, true);
+      });
+    chrome.tabs.create({url: 'chrome://newtab/', active: true});
+  },
+  function testCreateWithActiveFalse() {
+    chrome.test.listenOnce(chrome.tabs.onCreated,
+      function(tab) {
+        chrome.test.assertEq(tab.active, false);
+      });
+    chrome.tabs.create({url: 'chrome://newtab/', active: false});
+  }
+]);
diff --git a/chrome/test/data/extensions/manifest_tests/natively_connectable.json b/chrome/test/data/extensions/manifest_tests/natively_connectable.json
new file mode 100644
index 0000000..ee9def9
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/natively_connectable.json
@@ -0,0 +1,10 @@
+{
+  "name": "unit_tests --gtest_filter=NativelyConnectableManifestTest.Basic",
+  "version": "1",
+  "manifest_version": 2,
+  "natively_connectable": [
+    "com.google.chrome.test.echo",
+    "com.google.chrome.test.echo",
+    "com.google.chrome.test.host_binary_missing"
+  ]
+}
diff --git a/chrome/test/data/extensions/manifest_tests/natively_connectable_incorrect_type.json b/chrome/test/data/extensions/manifest_tests/natively_connectable_incorrect_type.json
new file mode 100644
index 0000000..5cc2df4
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/natively_connectable_incorrect_type.json
@@ -0,0 +1,10 @@
+{
+  "name": "unit_tests --gtest_filter=NativelyConnectableManifestTest.IncorrectType",
+  "version": "1",
+  "manifest_version": 2,
+  "natively_connectable": {
+    "com.google.chrome.test.echo": 2,
+    "com.google.chrome.test.echo": "three",
+    "com.google.chrome.test.host_binary_missing": 4
+  }
+}
diff --git a/chrome/test/data/extensions/manifest_tests/natively_connectable_incorrect_values_type.json b/chrome/test/data/extensions/manifest_tests/natively_connectable_incorrect_values_type.json
new file mode 100644
index 0000000..2e3beb2
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/natively_connectable_incorrect_values_type.json
@@ -0,0 +1,11 @@
+{
+  "name": "unit_tests --gtest_filter=NativelyConnectableManifestTest.IncorrectValuesType",
+  "version": "1",
+  "manifest_version": 2,
+  "natively_connectable": [
+    2,
+    [
+      "com.google.chrome.test.echo"
+    ]
+  ]
+}
diff --git a/chrome/test/data/extensions/manifest_tests/natively_connectable_unset.json b/chrome/test/data/extensions/manifest_tests/natively_connectable_unset.json
new file mode 100644
index 0000000..dbc5998
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/natively_connectable_unset.json
@@ -0,0 +1,5 @@
+{
+  "name": "unit_tests --gtest_filter=NativelyConnectableManifestTest.Unset",
+  "version": "1",
+  "manifest_version": 2
+}
diff --git a/chrome/test/data/webui/settings/chromeos/smb_shares_page_tests.js b/chrome/test/data/webui/settings/chromeos/smb_shares_page_tests.js
index 83568d8..255a327 100644
--- a/chrome/test/data/webui/settings/chromeos/smb_shares_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/smb_shares_page_tests.js
@@ -258,4 +258,20 @@
     });
   });
 
+  test('LoadingBarDuringDiscovery', async function() {
+    const url = addDialog.$.address;
+    // Loading bar is shown when the page loads.
+    expectTrue(url.showLoading);
+
+    await smbBrowserProxy.whenCalled('startDiscovery');
+
+    cr.webUIListenerCallback('on-shares-found', ['smb://foo/bar'], false);
+    expectTrue(url.showLoading);
+
+    cr.webUIListenerCallback('on-shares-found', ['smb://foo/bar2'], true);
+    expectFalse(url.showLoading);
+
+    expectEquals(2, url.items.length);
+  });
+
 });
diff --git a/chrome/test/data/webui/settings/google_assistant_page_test.js b/chrome/test/data/webui/settings/google_assistant_page_test.js
index 7fa2e4e..91e27126 100644
--- a/chrome/test/data/webui/settings/google_assistant_page_test.js
+++ b/chrome/test/data/webui/settings/google_assistant_page_test.js
@@ -323,6 +323,25 @@
     assertTrue(!!dropdown);
     assertTrue(dropdown.hasAttribute('hidden'));
   });
+
+  test('assistantDisabledByPolicy', function() {
+    let button = page.$$('#google-assistant-enable');
+    assertTrue(!!button);
+    assertFalse(button.disabled);
+    assertFalse(button.checked);
+    page.setPrefValue('settings.voice_interaction.enabled', true);
+    Polymer.dom.flush();
+    button = page.$$('#google-assistant-enable');
+    assertTrue(!!button);
+    assertFalse(button.disabled);
+    assertTrue(button.checked);
+
+    page.setPrefValue('settings.assistant.disabled_by_policy', true);
+    Polymer.dom.flush();
+    assertTrue(!!button);
+    assertTrue(button.disabled);
+    assertFalse(button.checked);
+  });
 });
 
 suite('GoogleAssistantHandlerWihtNoDspHotword', function() {
diff --git a/chrome/utility/BUILD.gn b/chrome/utility/BUILD.gn
index 4142505..ab42d815 100644
--- a/chrome/utility/BUILD.gn
+++ b/chrome/utility/BUILD.gn
@@ -151,6 +151,7 @@
 
   if (is_chromeos) {
     deps += [
+      "//chrome/services/ble_scan_parser:lib",
       "//chrome/services/file_util:lib",
       "//chromeos/assistant:buildflags",
       "//chromeos/services/ime:lib",
diff --git a/chrome/utility/DEPS b/chrome/utility/DEPS
index 3d5b4500..935fe0d2 100644
--- a/chrome/utility/DEPS
+++ b/chrome/utility/DEPS
@@ -1,6 +1,8 @@
 include_rules = [
   "+chrome/grit",
   "+chrome/installer/util",
+  "+chrome/services/ble_scan_parser/ble_scan_parser_service.h",
+  "+chrome/services/ble_scan_parser/public/mojom",
   "+chrome/services/cups_ipp_parser/cups_ipp_parser_service.h",
   "+chrome/services/cups_ipp_parser/public/mojom",
   "+chrome/services/file_util/file_util_service.h",
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index 0271e4f..b2670e5 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -72,6 +72,8 @@
 #endif
 
 #if defined(OS_CHROMEOS)
+#include "chrome/services/ble_scan_parser/ble_scan_parser_service.h"
+#include "chrome/services/ble_scan_parser/public/mojom/constants.mojom.h"
 #include "chromeos/assistant/buildflags.h"  // nogncheck
 #include "chromeos/services/ime/ime_service.h"
 #include "chromeos/services/ime/public/mojom/constants.mojom.h"
@@ -325,6 +327,11 @@
   if (service_name == chromeos::ime::mojom::kServiceName)
     return std::make_unique<chromeos::ime::ImeService>(std::move(request));
 
+  if (service_name == ble_scan_parser::mojom::kServiceName) {
+    return std::make_unique<ble_scan_parser::BleScanParserService>(
+        std::move(request));
+  }
+
 #if BUILDFLAG(ENABLE_PRINTING)
   if (service_name == chrome::mojom::kCupsIppParserServiceName)
     return std::make_unique<CupsIppParserService>(std::move(request));
diff --git a/chromecast/browser/cast_web_view.h b/chromecast/browser/cast_web_view.h
index 14949aa..fd46530 100644
--- a/chromecast/browser/cast_web_view.h
+++ b/chromecast/browser/cast_web_view.h
@@ -32,15 +32,6 @@
   class Delegate : public CastWebContents::Delegate,
                    public shell::CastContentWindow::Delegate {
    public:
-    // Called when there is console log output from web_contents.
-    // Returning true indicates that the delegate handled the message.
-    // If false is returned the default logging mechanism will be used.
-    virtual bool OnAddMessageToConsoleReceived(
-        blink::mojom::ConsoleMessageLevel log_level,
-        const base::string16& message,
-        int32_t line_no,
-        const base::string16& source_id) = 0;
-
     // Invoked by CastWebView when WebContentsDelegate::RunBluetoothChooser is
     // called. Returns a BluetoothChooser, a class used to solicit bluetooth
     // device selection from the user for WebBluetooth applications. If a
@@ -91,6 +82,10 @@
     // Whether this CastWebView should be managed by web ui window manager.
     bool managed = true;
 
+    // Prefix for JS console logs. This can be used to help identify the source
+    // of console log messages.
+    std::string log_prefix = "";
+
     CreateParams();
   };
 
diff --git a/chromecast/browser/cast_web_view_default.cc b/chromecast/browser/cast_web_view_default.cc
index 180b33d..b80a761 100644
--- a/chromecast/browser/cast_web_view_default.cc
+++ b/chromecast/browser/cast_web_view_default.cc
@@ -7,6 +7,8 @@
 #include <utility>
 
 #include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chromecast/base/cast_features.h"
 #include "chromecast/base/chromecast_switches.h"
@@ -64,6 +66,7 @@
       delegate_(params.delegate),
       transparent_(params.transparent),
       allow_media_access_(params.allow_media_access),
+      log_prefix_(params.log_prefix),
       web_contents_(CreateWebContents(browser_context_, site_instance_)),
       cast_web_contents_(web_contents_.get(), params.web_contents_params),
       window_(shell::CastContentWindow::Create(params.window_params)),
@@ -182,7 +185,7 @@
 bool CastWebViewDefault::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   if (!chromecast::IsFeatureEnabled(kAllowUserMediaAccess) &&
       !allow_media_access_) {
     LOG(WARNING) << __func__ << ": media access is disabled.";
@@ -197,8 +200,14 @@
     const base::string16& message,
     int32_t line_no,
     const base::string16& source_id) {
-  return delegate_->OnAddMessageToConsoleReceived(log_level, message, line_no,
-                                                  source_id);
+  base::string16 single_line_message;
+  // Mult-line message is not friendly to dumpstate redact.
+  base::ReplaceChars(message, base::ASCIIToUTF16("\n"),
+                     base::ASCIIToUTF16("\\n "), &single_line_message);
+  logging::LogMessage("CONSOLE", line_no, ::logging::LOG_INFO).stream()
+      << log_prefix_ << ": \"" << single_line_message
+      << "\", source: " << source_id << " (" << line_no << ")";
+  return true;
 }
 
 const blink::MediaStreamDevice* GetRequestedDeviceOrDefault(
@@ -241,7 +250,8 @@
            << " video_devices=" << video_devices.size();
 
   blink::MediaStreamDevices devices;
-  if (request.audio_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE) {
+  if (request.audio_type ==
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
     const blink::MediaStreamDevice* device = GetRequestedDeviceOrDefault(
         audio_devices, request.requested_audio_device_id);
     if (device) {
@@ -251,7 +261,8 @@
     }
   }
 
-  if (request.video_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE) {
+  if (request.video_type ==
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
     const blink::MediaStreamDevice* device = GetRequestedDeviceOrDefault(
         video_devices, request.requested_video_device_id);
     if (device) {
diff --git a/chromecast/browser/cast_web_view_default.h b/chromecast/browser/cast_web_view_default.h
index cb62c792..8ace8b4 100644
--- a/chromecast/browser/cast_web_view_default.h
+++ b/chromecast/browser/cast_web_view_default.h
@@ -75,7 +75,7 @@
   void ActivateContents(content::WebContents* contents) override;
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type) override;
+                                  blink::mojom::MediaStreamType type) override;
   bool DidAddMessageToConsole(content::WebContents* source,
                               blink::mojom::ConsoleMessageLevel log_level,
                               const base::string16& message,
@@ -96,6 +96,7 @@
   Delegate* const delegate_;
   const bool transparent_;
   const bool allow_media_access_;
+  const std::string log_prefix_;
 
   std::unique_ptr<content::WebContents> web_contents_;
   CastWebContentsImpl cast_web_contents_;
diff --git a/chromecast/browser/extensions/cast_extension_host_delegate.cc b/chromecast/browser/extensions/cast_extension_host_delegate.cc
index e5121a2..3bf69fc6 100644
--- a/chromecast/browser/extensions/cast_extension_host_delegate.cc
+++ b/chromecast/browser/extensions/cast_extension_host_delegate.cc
@@ -52,7 +52,7 @@
 bool CastExtensionHostDelegate::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const Extension* extension) {
   return media_capture_util::CheckMediaAccessPermission(type, extension);
 }
diff --git a/chromecast/browser/extensions/cast_extension_host_delegate.h b/chromecast/browser/extensions/cast_extension_host_delegate.h
index 365c2ea9..a15a6ff4 100644
--- a/chromecast/browser/extensions/cast_extension_host_delegate.h
+++ b/chromecast/browser/extensions/cast_extension_host_delegate.h
@@ -31,7 +31,7 @@
                                  const Extension* extension) override;
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type,
+                                  blink::mojom::MediaStreamType type,
                                   const Extension* extension) override;
   ExtensionHostQueue* GetExtensionHostQueue() const override;
   gfx::Size EnterPictureInPicture(content::WebContents* web_contents,
diff --git a/chromecast/browser/service/cast_service_simple.cc b/chromecast/browser/service/cast_service_simple.cc
index 6c486bc..be825acd 100644
--- a/chromecast/browser/service/cast_service_simple.cc
+++ b/chromecast/browser/service/cast_service_simple.cc
@@ -100,14 +100,6 @@
 
 void CastServiceSimple::OnKeyEvent(const ui::KeyEvent& key_event) {}
 
-bool CastServiceSimple::OnAddMessageToConsoleReceived(
-    blink::mojom::ConsoleMessageLevel log_level,
-    const base::string16& message,
-    int32_t line_no,
-    const base::string16& source_id) {
-  return false;
-}
-
 bool CastServiceSimple::CanHandleGesture(GestureType gesture_type) {
   return false;
 }
diff --git a/chromecast/browser/service/cast_service_simple.h b/chromecast/browser/service/cast_service_simple.h
index f779641..512a10f 100644
--- a/chromecast/browser/service/cast_service_simple.h
+++ b/chromecast/browser/service/cast_service_simple.h
@@ -39,11 +39,6 @@
   void OnPageStopped(CastWebContents* cast_web_contents,
                      int error_code) override;
   void OnPageStateChanged(CastWebContents* cast_web_contents) override;
-  bool OnAddMessageToConsoleReceived(
-      blink::mojom::ConsoleMessageLevel log_level,
-      const base::string16& message,
-      int32_t line_no,
-      const base::string16& source_id) override;
 
   // CastContentWindow::Delegate implementation:
   void OnWindowDestroyed() override;
diff --git a/chromecast/browser/test/cast_browser_test.cc b/chromecast/browser/test/cast_browser_test.cc
index 8c0c128..a686ba311 100644
--- a/chromecast/browser/test/cast_browser_test.cc
+++ b/chromecast/browser/test/cast_browser_test.cc
@@ -107,12 +107,5 @@
   return "";
 }
 
-bool CastBrowserTest::OnAddMessageToConsoleReceived(
-    blink::mojom::ConsoleMessageLevel log_level,
-    const base::string16& message,
-    int32_t line_no,
-    const base::string16& source_id) {
-  return false;
-}
 }  // namespace shell
 }  // namespace chromecast
diff --git a/chromecast/browser/test/cast_browser_test.h b/chromecast/browser/test/cast_browser_test.h
index d7b949fc..e6b7386 100644
--- a/chromecast/browser/test/cast_browser_test.h
+++ b/chromecast/browser/test/cast_browser_test.h
@@ -49,11 +49,6 @@
                      int error_code) override;
   void OnWindowDestroyed() override;
   void OnKeyEvent(const ui::KeyEvent& key_event) override;
-  bool OnAddMessageToConsoleReceived(
-      blink::mojom::ConsoleMessageLevel log_level,
-      const base::string16& message,
-      int32_t line_no,
-      const base::string16& source_id) override;
   void OnVisibilityChange(VisibilityType visibility_type) override;
   bool CanHandleGesture(GestureType gesture_type) override;
   bool ConsumeGesture(GestureType gesture_type) override;
diff --git a/chromecast/media/cma/backend/filter_group.cc b/chromecast/media/cma/backend/filter_group.cc
index 2ab0cc9..61e25d78 100644
--- a/chromecast/media/cma/backend/filter_group.cc
+++ b/chromecast/media/cma/backend/filter_group.cc
@@ -36,24 +36,26 @@
   stream_types_.push_back(stream_type);
 }
 
-void FilterGroup::Initialize(int output_samples_per_second,
-                             int output_frames_per_write) {
-  output_samples_per_second_ = output_samples_per_second;
-  output_frames_per_write_ = output_frames_per_write;
+void FilterGroup::Initialize(const AudioPostProcessor2::Config& output_config) {
+  output_config_ = output_config;
 
-  CHECK(post_processing_pipeline_->SetOutputSampleRate(
-      output_samples_per_second_));
+  CHECK(post_processing_pipeline_->SetOutputConfig(output_config_));
   input_samples_per_second_ = post_processing_pipeline_->GetInputSampleRate();
-  input_frames_per_write_ = output_frames_per_write *
+  input_frames_per_write_ = output_config_.output_frames_per_write *
                             input_samples_per_second_ /
-                            output_samples_per_second_;
-  DCHECK_EQ(input_frames_per_write_ * output_samples_per_second_,
-            output_frames_per_write_ * input_samples_per_second_)
+                            output_config_.output_sample_rate;
+  DCHECK_EQ(input_frames_per_write_ * output_config_.output_sample_rate,
+            output_config_.output_frames_per_write * input_samples_per_second_)
       << "Unable to produce stable buffer sizes for resampling rate "
-      << input_samples_per_second_ << " : " << output_samples_per_second_;
+      << input_samples_per_second_ << " : "
+      << output_config_.output_sample_rate;
+
+  AudioPostProcessor2::Config input_config = output_config;
+  input_config.output_sample_rate = input_samples_per_second_;
+  input_config.output_frames_per_write = input_frames_per_write_;
 
   for (FilterGroup* input : mixed_inputs_) {
-    input->Initialize(input_samples_per_second_, input_frames_per_write_);
+    input->Initialize(input_config);
   }
   post_processing_pipeline_->SetContentType(content_type_);
   active_inputs_.clear();
@@ -79,8 +81,8 @@
 float FilterGroup::MixAndFilter(
     int num_output_frames,
     MediaPipelineBackend::AudioDecoder::RenderingDelay rendering_delay) {
-  DCHECK_NE(output_samples_per_second_, 0);
-  DCHECK_EQ(num_output_frames, output_frames_per_write_);
+  DCHECK_NE(output_config_.output_sample_rate, 0);
+  DCHECK_EQ(num_output_frames, output_config_.output_frames_per_write);
 
   float volume = 0.0f;
   AudioContentType content_type = static_cast<AudioContentType>(-1);
@@ -184,7 +186,7 @@
 }
 
 int64_t FilterGroup::GetRenderingDelayMicroseconds() {
-  if (output_samples_per_second_ == 0) {
+  if (output_config_.output_sample_rate == 0) {
     return 0;
   }
   return delay_seconds_ * base::Time::kMicrosecondsPerSecond;
@@ -266,8 +268,8 @@
   }
   LOG(INFO) << all_inputs << ": " << num_channels_ << "ch@"
             << input_samples_per_second_ << "hz -> [GROUP]" << name_ << " -> "
-            << GetOutputChannelCount() << "ch@" << output_samples_per_second_
-            << "hz";
+            << GetOutputChannelCount() << "ch@"
+            << output_config_.output_sample_rate << "hz";
 }
 
 }  // namespace media
diff --git a/chromecast/media/cma/backend/filter_group.h b/chromecast/media/cma/backend/filter_group.h
index 6f7010c..3c4e0ce 100644
--- a/chromecast/media/cma/backend/filter_group.h
+++ b/chromecast/media/cma/backend/filter_group.h
@@ -15,6 +15,7 @@
 #include "base/macros.h"
 #include "base/values.h"
 #include "chromecast/media/base/aligned_buffer.h"
+#include "chromecast/public/media/audio_post_processor2_shlib.h"
 #include "chromecast/public/media/media_pipeline_backend.h"
 #include "chromecast/public/volume_control.h"
 
@@ -53,13 +54,10 @@
   // Recursively sets the sample rate of the post-processors and FilterGroups.
   // This should only be called externally on the output node of the FilterGroup
   // tree.
-  // The output rate of this group will be |output_samples_per_second|.
-  // The output block size, i.e. the number of frames written in each call to
-  // MixAndFilter() of this group will be |output_frames_per_write|.
   // Groups that feed this group may receive different values due to resampling.
   // After calling Initialize(), input_samples_per_second() and
   // input_frames_per_write() may be called to determine the input rate/size.
-  void Initialize(int output_samples_per_second, int output_frames_per_write);
+  void Initialize(const AudioPostProcessor2::Config& output_config);
 
   // Adds/removes |input| from |active_inputs_|.
   void AddInput(MixerInput* input);
@@ -130,9 +128,8 @@
   base::flat_set<MixerInput*> active_inputs_;
 
   int playout_channel_selection_ = kChannelAll;
-  int output_samples_per_second_ = 0;
+  AudioPostProcessor2::Config output_config_;
   int input_samples_per_second_ = 0;
-  int output_frames_per_write_ = 0;
   int input_frames_per_write_ = 0;
   int frames_zeroed_ = 0;
   float last_volume_ = 0.0;
diff --git a/chromecast/media/cma/backend/filter_group_unittest.cc b/chromecast/media/cma/backend/filter_group_unittest.cc
index 8394c02..3966f7b 100644
--- a/chromecast/media/cma/backend/filter_group_unittest.cc
+++ b/chromecast/media/cma/backend/filter_group_unittest.cc
@@ -10,6 +10,7 @@
 #include "chromecast/media/cma/backend/mock_mixer_source.h"
 #include "chromecast/media/cma/backend/post_processing_pipeline.h"
 #include "chromecast/media/cma/backend/stream_mixer.h"
+#include "chromecast/public/media/audio_post_processor2_shlib.h"
 #include "chromecast/public/volume_control.h"
 #include "media/base/audio_bus.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -57,11 +58,8 @@
   MOCK_METHOD1(UpdatePlayoutChannel, void(int));
 
  protected:
-  float* output_buffer_;
-
- private:
-  bool SetOutputSampleRate(int sample_rate) override {
-    sample_rate_ = sample_rate;
+  bool SetOutputConfig(const AudioPostProcessor2::Config& config) override {
+    sample_rate_ = config.output_sample_rate;
     return true;
   }
   int GetInputSampleRate() const override { return sample_rate_; }
@@ -78,6 +76,7 @@
     return 0;
   }
 
+  float* output_buffer_;
   const int num_output_channels_;
   int sample_rate_;
 
@@ -88,7 +87,8 @@
 class InvertChannelPostProcessor : public MockPostProcessingPipeline {
  public:
   explicit InvertChannelPostProcessor(int channels, int channel_to_invert)
-      : channels_(channels), channel_to_invert_(channel_to_invert) {
+      : MockPostProcessingPipeline(channels),
+        channel_to_invert_(channel_to_invert) {
     ON_CALL(*this, ProcessFrames(_, _, _, _))
         .WillByDefault(testing::Invoke(
             this, &InvertChannelPostProcessor::DoInvertChannel));
@@ -111,28 +111,18 @@
                       bool is_silence) {
     output_buffer_ = data;
     for (int fr = 0; fr < num_frames; ++fr) {
-      for (int ch = 0; ch < channels_; ++ch) {
+      for (int ch = 0; ch < num_output_channels_; ++ch) {
         if (ch == channel_to_invert_) {
-          data[fr * channels_ + ch] *= -1;
+          data[fr * num_output_channels_ + ch] *= -1;
         }
       }
     }
     return 0;
   }
 
-  bool SetOutputSampleRate(int sample_rate) override {
-    sample_rate_ = sample_rate;
-    return true;
-  }
-  int GetInputSampleRate() const override { return sample_rate_; }
-
-  bool IsRinging() override { return false; }
-  int delay() { return 0; }
   std::string name() const { return "invert"; }
 
-  int channels_;
   int channel_to_invert_;
-  int sample_rate_;
 
   DISALLOW_COPY_AND_ASSIGN(InvertChannelPostProcessor);
 };
@@ -184,7 +174,11 @@
     filter_group_ = std::make_unique<FilterGroup>(
         kNumInputChannels, "test_filter", std::move(post_processor));
     input_ = std::make_unique<MixerInput>(&source_, filter_group_.get());
-    filter_group_->Initialize(kInputSampleRate, kInputFrames);
+    AudioPostProcessor2::Config config;
+    config.output_sample_rate = kInputSampleRate;
+    config.system_output_sample_rate = kInputSampleRate;
+    config.output_frames_per_write = kInputFrames;
+    filter_group_->Initialize(config);
     filter_group_->AddInput(input_.get());
     filter_group_->UpdatePlayoutChannel(kChannelAll);
   }
diff --git a/chromecast/media/cma/backend/mixer_pipeline.cc b/chromecast/media/cma/backend/mixer_pipeline.cc
index 3c377bc..c559dec 100644
--- a/chromecast/media/cma/backend/mixer_pipeline.cc
+++ b/chromecast/media/cma/backend/mixer_pipeline.cc
@@ -12,6 +12,7 @@
 #include "chromecast/media/cma/backend/filter_group.h"
 #include "chromecast/media/cma/backend/post_processing_pipeline_impl.h"
 #include "chromecast/media/cma/backend/post_processing_pipeline_parser.h"
+#include "chromecast/public/media/audio_post_processor2_shlib.h"
 #include "media/audio/audio_device_description.h"
 
 namespace chromecast {
@@ -162,7 +163,11 @@
                                int frames_per_write) {
   // The output group will recursively set the sample rate of all other
   // FilterGroups.
-  output_group_->Initialize(output_samples_per_second, frames_per_write);
+  AudioPostProcessor2::Config config;
+  config.output_sample_rate = output_samples_per_second;
+  config.system_output_sample_rate = output_samples_per_second;
+  config.output_frames_per_write = frames_per_write;
+  output_group_->Initialize(config);
   output_group_->PrintTopology();
 }
 
diff --git a/chromecast/media/cma/backend/mock_post_processor_factory.h b/chromecast/media/cma/backend/mock_post_processor_factory.h
index 22b4f7f..5b196bfb 100644
--- a/chromecast/media/cma/backend/mock_post_processor_factory.h
+++ b/chromecast/media/cma/backend/mock_post_processor_factory.h
@@ -11,6 +11,7 @@
 
 #include "base/macros.h"
 #include "chromecast/media/cma/backend/post_processing_pipeline.h"
+#include "chromecast/public/media/audio_post_processor2_shlib.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace base {
@@ -34,8 +35,8 @@
                       float current_volume,
                       bool is_silence));
   MOCK_METHOD1(SetContentType, void(AudioContentType));
-  bool SetOutputSampleRate(int sample_rate) override {
-    sample_rate_ = sample_rate;
+  bool SetOutputConfig(const AudioPostProcessor2::Config& config) override {
+    sample_rate_ = config.output_sample_rate;
     return true;
   }
   int GetInputSampleRate() const override { return sample_rate_; }
diff --git a/chromecast/media/cma/backend/post_processing_pipeline.h b/chromecast/media/cma/backend/post_processing_pipeline.h
index d0a5cbd..986bf6a 100644
--- a/chromecast/media/cma/backend/post_processing_pipeline.h
+++ b/chromecast/media/cma/backend/post_processing_pipeline.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <vector>
 
+#include "chromecast/public/media/audio_post_processor2_shlib.h"
 #include "chromecast/public/volume_control.h"
 
 namespace base {
@@ -29,7 +30,8 @@
   virtual float* GetOutputBuffer() = 0;
   virtual int NumOutputChannels() const = 0;
 
-  virtual bool SetOutputSampleRate(int sample_rate) = 0;
+  virtual bool SetOutputConfig(
+      const AudioPostProcessor2::Config& output_config) = 0;
   virtual int GetInputSampleRate() const = 0;
   virtual bool IsRinging() = 0;
   virtual void SetPostProcessorConfig(const std::string& name,
diff --git a/chromecast/media/cma/backend/post_processing_pipeline_impl.cc b/chromecast/media/cma/backend/post_processing_pipeline_impl.cc
index 90fcf65..01dffc0 100644
--- a/chromecast/media/cma/backend/post_processing_pipeline_impl.cc
+++ b/chromecast/media/cma/backend/post_processing_pipeline_impl.cc
@@ -121,6 +121,9 @@
                                                  bool is_silence) {
   DCHECK_GT(input_sample_rate_, 0);
   DCHECK(data);
+  if (processors_.size() > 0) {
+    DCHECK_EQ(processors_[0].input_frames_per_write, num_input_frames);
+  }
 
   output_buffer_ = data;
 
@@ -137,12 +140,9 @@
 
   delay_s_ = 0;
   for (auto& processor : processors_) {
-    processor.ptr->ProcessFrames(output_buffer_, num_input_frames, cast_volume_,
+    processor.ptr->ProcessFrames(output_buffer_,
+                                 processor.input_frames_per_write, cast_volume_,
                                  current_dbfs_);
-    DCHECK_EQ(
-        num_input_frames * processor.output_frames_per_input_frame,
-        std::floor(num_input_frames * processor.output_frames_per_input_frame));
-    num_input_frames *= processor.output_frames_per_input_frame;
     const auto& status = processor.ptr->GetStatus();
     delay_s_ += static_cast<double>(status.rendering_delay_frames) /
                 status.input_sample_rate;
@@ -161,26 +161,28 @@
   return output_buffer_;
 }
 
-bool PostProcessingPipelineImpl::SetOutputSampleRate(int sample_rate) {
-  output_sample_rate_ = sample_rate;
-  int processor_output_rate = sample_rate;
-  int processor_input_rate = sample_rate;
+bool PostProcessingPipelineImpl::SetOutputConfig(
+    const AudioPostProcessor2::Config& output_config) {
+  output_sample_rate_ = output_config.output_sample_rate;
+  AudioPostProcessor2::Config processor_config = output_config;
 
   // Each Processor's output rate must be the following processor's input rate.
   for (int i = static_cast<int>(processors_.size()) - 1; i >= 0; --i) {
-    AudioPostProcessor2::Config config;
-    config.output_sample_rate = processor_output_rate;
-    if (!processors_[i].ptr->SetConfig(config)) {
+    if (!processors_[i].ptr->SetConfig(processor_config)) {
       return false;
     }
-    processor_input_rate = processors_[i].ptr->GetStatus().input_sample_rate;
-    DCHECK_GT(processor_input_rate, 0) << processors_[i].name;
-    processors_[i].output_frames_per_input_frame =
-        static_cast<double>(processor_output_rate) / processor_input_rate;
-    processor_output_rate = processor_input_rate;
+    int input_sample_rate = processors_[i].ptr->GetStatus().input_sample_rate;
+    DCHECK_GT(input_sample_rate, 0)
+        << processors_[i].name << " did not set its sample rate";
+    processors_[i].input_frames_per_write =
+        processor_config.output_frames_per_write * input_sample_rate /
+        processor_config.output_sample_rate;
+    processor_config.output_sample_rate = input_sample_rate;
+    processor_config.output_frames_per_write =
+        processors_[i].input_frames_per_write;
   }
 
-  input_sample_rate_ = processor_input_rate;
+  input_sample_rate_ = processor_config.output_sample_rate;
   ringing_time_in_frames_ = GetRingingTimeInFrames();
   silence_frames_processed_ = 0;
   return true;
diff --git a/chromecast/media/cma/backend/post_processing_pipeline_impl.h b/chromecast/media/cma/backend/post_processing_pipeline_impl.h
index 31ad0e2..7babae6 100644
--- a/chromecast/media/cma/backend/post_processing_pipeline_impl.h
+++ b/chromecast/media/cma/backend/post_processing_pipeline_impl.h
@@ -40,7 +40,7 @@
   float* GetOutputBuffer() override;
   int NumOutputChannels() const override;
 
-  bool SetOutputSampleRate(int sample_rate) override;
+  bool SetOutputConfig(const AudioPostProcessor2::Config& config) override;
   int GetInputSampleRate() const override;
   bool IsRinging() override;
 
@@ -55,7 +55,7 @@
   // structs.
   typedef struct {
     std::unique_ptr<AudioPostProcessor2> ptr;
-    double output_frames_per_input_frame;
+    int input_frames_per_write;
     std::string name;
   } PostProcessorInfo;
 
diff --git a/chromecast/public/media/audio_post_processor2_shlib.h b/chromecast/public/media/audio_post_processor2_shlib.h
index dc84040f..e6692f0 100644
--- a/chromecast/public/media/audio_post_processor2_shlib.h
+++ b/chromecast/public/media/audio_post_processor2_shlib.h
@@ -35,7 +35,10 @@
 class AudioPostProcessor2 {
  public:
   struct Config {
-    int output_sample_rate;
+    int output_sample_rate;  // The output sample rate for this processor.
+    int system_output_sample_rate;  // The system (hardware) output sample rate.
+    // The number of output frames expected from ProcessFrames().
+    int output_frames_per_write;
   };
 
   struct Status {
@@ -71,9 +74,6 @@
     float* output_buffer = nullptr;
   };
 
-  // The maximum amount of data that will ever be processed in one call.
-  static constexpr int kMaxAudioWriteTimeMilliseconds = 20;
-
   virtual ~AudioPostProcessor2() = default;
 
   // Sets the Config of the processor.
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 6b70e31..fec3415d 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -47,7 +47,7 @@
 // If enabled, DriveFS will be used for Drive sync.
 const base::Feature kDriveFs{"DriveFS", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// If enabled shows the new visual signals feedback panel.
+// If enabled shows the visual signals feedback panel.
 const base::Feature kEnableFileManagerFeedbackPanel{
     "EnableFeedbackPanel", base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -80,6 +80,10 @@
 const base::Feature kFsNosymfollow{"FsNosymfollow",
                                    base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enable a D-Bus service for accessing gesture properties.
+const base::Feature kGesturePropertiesDBusService{
+    "GesturePropertiesDBusService", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enable or disable Unified Input Logic for FST decocder in the IME extension
 // on Chrome OS.
 const base::Feature kImeInputLogicFst{"ImeInputLogicFst",
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index c7fc8094..ab4cbb7c 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -44,6 +44,8 @@
 extern const base::Feature kEnableSupervisionTransitionScreens;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kFsNosymfollow;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kGesturePropertiesDBusService;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kImeInputLogicFst;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kInstantTethering;
diff --git a/chromeos/network/network_connection_handler_impl.cc b/chromeos/network/network_connection_handler_impl.cc
index a9ad926..3cc634a4 100644
--- a/chromeos/network/network_connection_handler_impl.cc
+++ b/chromeos/network/network_connection_handler_impl.cc
@@ -265,7 +265,7 @@
     }
 
     if (check_error_state) {
-      const std::string& error = network->last_error();
+      const std::string& error = network->error();
       if (error == shill::kErrorBadPassphrase) {
         InvokeConnectErrorCallback(service_path, error_callback,
                                    kErrorBadPassphrase);
diff --git a/chromeos/network/network_state.cc b/chromeos/network/network_state.cc
index 0aaa409..ec7efed 100644
--- a/chromeos/network/network_state.cc
+++ b/chromeos/network/network_state.cc
@@ -24,8 +24,6 @@
 
 namespace {
 
-const char kErrorUnknown[] = "Unknown";
-
 const char kDefaultCellularNetworkPath[] = "/cellular";
 
 // TODO(tbarzic): Add payment portal method values to shill/dbus-constants.
@@ -538,12 +536,6 @@
   guid_ = guid;
 }
 
-std::string NetworkState::GetErrorState() const {
-  if (ErrorIsValid(error()))
-    return error();
-  return last_error();
-}
-
 network_config::mojom::ActivationStateType
 NetworkState::GetMojoActivationState() const {
   using network_config::mojom::ActivationStateType;
@@ -609,10 +601,7 @@
 
 // static
 bool NetworkState::ErrorIsValid(const std::string& error) {
-  // Pre M-74 Shill uses "Unknown" to indicate an unset or cleared error state.
-  // TODO(stevenjb): Remove kErrorUnknown once 74 has shipped.
-  return !error.empty() && error != kErrorUnknown &&
-         error != shill::kErrorNoFailure;
+  return !error.empty() && error != shill::kErrorNoFailure;
 }
 
 // static
diff --git a/chromeos/network/network_state.h b/chromeos/network/network_state.h
index a77c55c8a..391fc4c 100644
--- a/chromeos/network/network_state.h
+++ b/chromeos/network/network_state.h
@@ -215,9 +215,6 @@
   // Set the GUID. Called exclusively by NetworkStateHandler.
   void SetGuid(const std::string& guid);
 
-  // Returns |error_| if valid, otherwise returns |last_error_|.
-  std::string GetErrorState() const;
-
   // Helpers for returning mojo types.
   network_config::mojom::ActivationStateType GetMojoActivationState() const;
   network_config::mojom::SecurityType GetMojoSecurity() const;
diff --git a/chromeos/network/network_state_handler.cc b/chromeos/network/network_state_handler.cc
index 0f1e584..be8f9ec 100644
--- a/chromeos/network/network_state_handler.cc
+++ b/chromeos/network/network_state_handler.cc
@@ -1171,14 +1171,14 @@
   return list.front();
 }
 
-void NetworkStateHandler::SetLastErrorForTest(const std::string& service_path,
-                                              const std::string& error) {
+void NetworkStateHandler::SetErrorForTest(const std::string& service_path,
+                                          const std::string& error) {
   NetworkState* network_state = GetModifiableNetworkState(service_path);
   if (!network_state) {
     NET_LOG(ERROR) << "No matching NetworkState for: " << service_path;
     return;
   }
-  network_state->last_error_ = error;
+  network_state->error_ = error;
 }
 
 //------------------------------------------------------------------------------
diff --git a/chromeos/network/network_state_handler.h b/chromeos/network/network_state_handler.h
index e5aba155d..0f824d1 100644
--- a/chromeos/network/network_state_handler.h
+++ b/chromeos/network/network_state_handler.h
@@ -380,9 +380,9 @@
     return default_network_path_;
   }
 
-  // Sets the |last_error_| property of the matching NetworkState for tests.
-  void SetLastErrorForTest(const std::string& service_path,
-                           const std::string& error);
+  // Sets the |error_| property of the matching NetworkState for tests.
+  void SetErrorForTest(const std::string& service_path,
+                       const std::string& error);
 
   // Sets |allow_only_policy_networks_to_connect_|,
   // |allow_only_policy_networks_to_connect_if_available_| and
diff --git a/chromeos/network/onc/onc_translator_shill_to_onc.cc b/chromeos/network/onc/onc_translator_shill_to_onc.cc
index fbfe865..29b7e60 100644
--- a/chromeos/network/onc/onc_translator_shill_to_onc.cc
+++ b/chromeos/network/onc/onc_translator_shill_to_onc.cc
@@ -536,14 +536,11 @@
     }
   }
 
-  // 'ErrorState' reflects the most recent error maintained in NetworkState
-  // (which may not match Shill's Error or PreviousError properties). Non
-  // visible networks (with null network_state_) do not set ErrorState.
+  // Non-visible networks (with null network_state_) do not set ErrorState.
   if (network_state_) {
-    std::string error_state = network_state_->GetErrorState();
-    if (!error_state.empty()) {
+    if (!network_state_->error().empty()) {
       onc_object_->SetKey(::onc::network_config::kErrorState,
-                          base::Value(error_state));
+                          base::Value(network_state_->error()));
     }
   }
 
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index 2fb8a71e..7d02a553 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -62,6 +62,8 @@
 namespace assistant {
 namespace {
 
+static bool is_first_init = true;
+
 constexpr char kWiFiDeviceSettingId[] = "WIFI";
 constexpr char kBluetoothDeviceSettingId[] = "BLUETOOTH";
 constexpr char kVolumeLevelDeviceSettingId[] = "VOLUME_LEVEL";
@@ -995,9 +997,15 @@
   std::move(post_init_callback).Run();
   assistant_settings_manager_->UpdateServerDeviceSettings();
 
-  if (base::FeatureList::IsEnabled(assistant::features::kAssistantVoiceMatch) &&
-      service_->assistant_state()->hotword_enabled().value()) {
-    assistant_settings_manager_->SyncSpeakerIdEnrollmentStatus();
+  if (is_first_init) {
+    is_first_init = false;
+    // Only sync status at the first init to prevent unexpected corner cases.
+    // This still does not handle browser restart.
+    if (base::FeatureList::IsEnabled(
+            assistant::features::kAssistantVoiceMatch) &&
+        service_->assistant_state()->hotword_enabled().value()) {
+      assistant_settings_manager_->SyncSpeakerIdEnrollmentStatus();
+    }
   }
 
   if (base::FeatureList::IsEnabled(assistant::features::kAssistantAppSupport)) {
diff --git a/chromeos/services/cellular_setup/BUILD.gn b/chromeos/services/cellular_setup/BUILD.gn
index 7f6cdf6..73b8d7b7 100644
--- a/chromeos/services/cellular_setup/BUILD.gn
+++ b/chromeos/services/cellular_setup/BUILD.gn
@@ -48,14 +48,17 @@
   testonly = true
 
   sources = [
+    "cellular_setup_impl_unittest.cc",
     "cellular_setup_service_unittest.cc",
     "ota_activator_impl_unittest.cc",
   ]
 
   deps = [
     ":cellular_setup",
+    ":test_support",
     "//base",
     "//base/test:test_support",
+    "//chromeos/dbus/shill",
     "//chromeos/network:test_support",
     "//chromeos/services/cellular_setup/public/cpp:test_support",
     "//services/service_manager/public/cpp/test:test_support",
diff --git a/chromeos/services/cellular_setup/cellular_setup_impl.cc b/chromeos/services/cellular_setup/cellular_setup_impl.cc
index c8ef02b7c..db9a3485 100644
--- a/chromeos/services/cellular_setup/cellular_setup_impl.cc
+++ b/chromeos/services/cellular_setup/cellular_setup_impl.cc
@@ -4,8 +4,13 @@
 
 #include "chromeos/services/cellular_setup/cellular_setup_impl.h"
 
+#include <utility>
+
+#include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/services/cellular_setup/ota_activator_impl.h"
 
 namespace chromeos {
 
@@ -39,9 +44,29 @@
 void CellularSetupImpl::StartActivation(
     mojom::ActivationDelegatePtr delegate,
     StartActivationCallback callback) {
-  // TODO(khorimoto): Actually return a CarrierPortalObserver instead of
-  // passing null.
-  std::move(callback).Run(nullptr /* observer */);
+  size_t request_id = next_request_id_;
+  ++next_request_id_;
+
+  NetworkHandler* network_handler = NetworkHandler::Get();
+  std::unique_ptr<OtaActivator> ota_activator =
+      OtaActivatorImpl::Factory::Create(
+          std::move(delegate),
+          base::BindOnce(&CellularSetupImpl::OnActivationAttemptFinished,
+                         base::Unretained(this), request_id),
+          network_handler->network_state_handler(),
+          network_handler->network_connection_handler(),
+          network_handler->network_activation_handler());
+
+  std::move(callback).Run(ota_activator->GenerateInterfacePtr());
+
+  // Store the OtaActivator instance in a map indexed by request ID; once the
+  // attempt has finished, the map entry will be deleted in
+  // OnActivationAttemptFinished().
+  ota_activator_map_.AddWithID(std::move(ota_activator), request_id);
+}
+
+void CellularSetupImpl::OnActivationAttemptFinished(size_t request_id) {
+  ota_activator_map_.Remove(request_id);
 }
 
 }  // namespace cellular_setup
diff --git a/chromeos/services/cellular_setup/cellular_setup_impl.h b/chromeos/services/cellular_setup/cellular_setup_impl.h
index 4590002..37862157 100644
--- a/chromeos/services/cellular_setup/cellular_setup_impl.h
+++ b/chromeos/services/cellular_setup/cellular_setup_impl.h
@@ -5,6 +5,9 @@
 #ifndef CHROMEOS_SERVICES_CELLULAR_SETUP_CELLULAR_SETUP_IMPL_H_
 #define CHROMEOS_SERVICES_CELLULAR_SETUP_CELLULAR_SETUP_IMPL_H_
 
+#include <memory>
+
+#include "base/containers/id_map.h"
 #include "base/macros.h"
 #include "chromeos/services/cellular_setup/cellular_setup_base.h"
 
@@ -12,7 +15,11 @@
 
 namespace cellular_setup {
 
-// Concrete mojom::CellularSetup implementation.
+class OtaActivator;
+
+// Concrete mojom::CellularSetup implementation. This class creates a new
+// OtaActivator instance per each StartActivation() invocation and passes a
+// pointer back to the client.
 class CellularSetupImpl : public CellularSetupBase {
  public:
   class Factory {
@@ -32,6 +39,11 @@
   void StartActivation(mojom::ActivationDelegatePtr delegate,
                        StartActivationCallback callback) override;
 
+  void OnActivationAttemptFinished(size_t request_id);
+
+  size_t next_request_id_ = 0u;
+  base::IDMap<std::unique_ptr<OtaActivator>, size_t> ota_activator_map_;
+
   DISALLOW_COPY_AND_ASSIGN(CellularSetupImpl);
 };
 
diff --git a/chromeos/services/cellular_setup/cellular_setup_impl_unittest.cc b/chromeos/services/cellular_setup/cellular_setup_impl_unittest.cc
new file mode 100644
index 0000000..a7a2e362
--- /dev/null
+++ b/chromeos/services/cellular_setup/cellular_setup_impl_unittest.cc
@@ -0,0 +1,139 @@
+// 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 "chromeos/services/cellular_setup/cellular_setup_impl.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "chromeos/dbus/shill/shill_clients.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/services/cellular_setup/cellular_setup_base.h"
+#include "chromeos/services/cellular_setup/cellular_setup_impl.h"
+#include "chromeos/services/cellular_setup/fake_ota_activator.h"
+#include "chromeos/services/cellular_setup/ota_activator_impl.h"
+#include "chromeos/services/cellular_setup/public/cpp/fake_activation_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace cellular_setup {
+
+namespace {
+
+class FakeOtaActivatorFactory : public OtaActivatorImpl::Factory {
+ public:
+  FakeOtaActivatorFactory() = default;
+  ~FakeOtaActivatorFactory() override = default;
+
+  std::vector<FakeOtaActivator*>& created_instances() {
+    return created_instances_;
+  }
+
+ private:
+  // OtaActivatorImpl::Factory:
+  std::unique_ptr<OtaActivator> BuildInstance(
+      mojom::ActivationDelegatePtr activation_delegate,
+      base::OnceClosure on_finished_callback,
+      NetworkStateHandler* network_state_handler,
+      NetworkConnectionHandler* network_connection_handler,
+      NetworkActivationHandler* network_activation_handler) override {
+    EXPECT_TRUE(activation_delegate);
+    EXPECT_TRUE(network_state_handler);
+    EXPECT_TRUE(network_connection_handler);
+    EXPECT_TRUE(network_activation_handler);
+
+    auto fake_ota_activator =
+        std::make_unique<FakeOtaActivator>(std::move(on_finished_callback));
+    created_instances_.push_back(fake_ota_activator.get());
+
+    return fake_ota_activator;
+  }
+
+  std::vector<FakeOtaActivator*> created_instances_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeOtaActivatorFactory);
+};
+
+}  // namespace
+
+class CellularSetupImplTest : public testing::Test {
+ protected:
+  CellularSetupImplTest() = default;
+  ~CellularSetupImplTest() override = default;
+
+  // testing::Test:
+  void SetUp() override {
+    OtaActivatorImpl::Factory::SetFactoryForTesting(
+        &fake_ota_activator_factory_);
+    shill_clients::InitializeFakes();
+    NetworkHandler::Initialize();
+    cellular_setup_ = CellularSetupImpl::Factory::Create();
+  }
+
+  void TearDown() override {
+    cellular_setup_.reset();
+    NetworkHandler::Shutdown();
+    shill_clients::Shutdown();
+    OtaActivatorImpl::Factory::SetFactoryForTesting(nullptr);
+  }
+
+  void CallStartActivation(FakeActivationDelegate* fake_activation_delegate) {
+    size_t num_before_call = num_carrier_portal_handlers_received_;
+    EXPECT_EQ(num_before_call,
+              fake_ota_activator_factory_.created_instances().size());
+
+    base::RunLoop run_loop;
+    cellular_setup_->StartActivation(
+        fake_activation_delegate->GenerateInterfacePtr(),
+        base::BindOnce(&CellularSetupImplTest::OnCarrierPortalHandlerReceived,
+                       base::Unretained(this), run_loop.QuitClosure()));
+    run_loop.Run();
+
+    EXPECT_EQ(num_before_call + 1u, num_carrier_portal_handlers_received_);
+    EXPECT_EQ(num_before_call + 1u,
+              fake_ota_activator_factory_.created_instances().size());
+
+    fake_ota_activator_factory_.created_instances()[num_before_call]
+        ->InvokeOnFinishedCallback();
+  }
+
+ private:
+  void OnCarrierPortalHandlerReceived(
+      base::OnceClosure quit_closure,
+      mojom::CarrierPortalHandlerPtr carrier_portal_handler) {
+    ++num_carrier_portal_handlers_received_;
+    std::move(quit_closure).Run();
+  }
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  FakeOtaActivatorFactory fake_ota_activator_factory_;
+
+  std::unique_ptr<CellularSetupBase> cellular_setup_;
+
+  size_t num_carrier_portal_handlers_received_ = 0u;
+
+  DISALLOW_COPY_AND_ASSIGN(CellularSetupImplTest);
+};
+
+TEST_F(CellularSetupImplTest, StartActivation_SingleAttempt) {
+  auto fake_activation_delegate = std::make_unique<FakeActivationDelegate>();
+  CallStartActivation(fake_activation_delegate.get());
+}
+
+TEST_F(CellularSetupImplTest, StartActivation_MultipleAttempts) {
+  auto fake_activation_delegate_1 = std::make_unique<FakeActivationDelegate>();
+  CallStartActivation(fake_activation_delegate_1.get());
+
+  auto fake_activation_delegate_2 = std::make_unique<FakeActivationDelegate>();
+  CallStartActivation(fake_activation_delegate_2.get());
+}
+
+}  // namespace cellular_setup
+
+}  // namespace chromeos
diff --git a/chromeos/services/cellular_setup/fake_ota_activator.cc b/chromeos/services/cellular_setup/fake_ota_activator.cc
index e88b84b1..e255c73f 100644
--- a/chromeos/services/cellular_setup/fake_ota_activator.cc
+++ b/chromeos/services/cellular_setup/fake_ota_activator.cc
@@ -15,6 +15,11 @@
 
 FakeOtaActivator::~FakeOtaActivator() = default;
 
+void FakeOtaActivator::OnCarrierPortalStatusChange(
+    mojom::CarrierPortalStatus status) {
+  fake_carrier_portal_handler_.OnCarrierPortalStatusChange(status);
+}
+
 }  // namespace cellular_setup
 
 }  // namespace chromeos
diff --git a/chromeos/services/cellular_setup/fake_ota_activator.h b/chromeos/services/cellular_setup/fake_ota_activator.h
index 664e57bc9..29afef8 100644
--- a/chromeos/services/cellular_setup/fake_ota_activator.h
+++ b/chromeos/services/cellular_setup/fake_ota_activator.h
@@ -15,14 +15,23 @@
 namespace cellular_setup {
 
 // Test OtaActivator implementation.
-class FakeOtaActivator : public OtaActivator, public FakeCarrierPortalHandler {
+class FakeOtaActivator : public OtaActivator {
  public:
   explicit FakeOtaActivator(base::OnceClosure on_finished_callback);
   ~FakeOtaActivator() override;
 
   using OtaActivator::InvokeOnFinishedCallback;
 
+  const std::vector<mojom::CarrierPortalStatus>& status_updates() const {
+    return fake_carrier_portal_handler_.status_updates();
+  }
+
  private:
+  // mojom::CarrierPortalHandler:
+  void OnCarrierPortalStatusChange(mojom::CarrierPortalStatus status) override;
+
+  FakeCarrierPortalHandler fake_carrier_portal_handler_;
+
   DISALLOW_COPY_AND_ASSIGN(FakeOtaActivator);
 };
 
diff --git a/chromeos/services/cellular_setup/public/cpp/fake_carrier_portal_handler.h b/chromeos/services/cellular_setup/public/cpp/fake_carrier_portal_handler.h
index 75126e0..9a3ac878 100644
--- a/chromeos/services/cellular_setup/public/cpp/fake_carrier_portal_handler.h
+++ b/chromeos/services/cellular_setup/public/cpp/fake_carrier_portal_handler.h
@@ -27,13 +27,12 @@
     return status_updates_;
   }
 
- private:
   // mojom::CarrierPortalHandler:
   void OnCarrierPortalStatusChange(
       mojom::CarrierPortalStatus carrier_portal_status) override;
 
+ private:
   std::vector<mojom::CarrierPortalStatus> status_updates_;
-
   mojo::BindingSet<mojom::CarrierPortalHandler> bindings_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeCarrierPortalHandler);
diff --git a/chromeos/services/network_config/BUILD.gn b/chromeos/services/network_config/BUILD.gn
index c08e77a..8e4a928 100644
--- a/chromeos/services/network_config/BUILD.gn
+++ b/chromeos/services/network_config/BUILD.gn
@@ -39,6 +39,7 @@
     "//base/test:test_support",
     "//chromeos/network:test_support",
     "//chromeos/services/network_config/public/cpp:test_support",
+    "//services/network/public/mojom:mojom_ip_address",
     "//testing/gtest",
   ]
 }
diff --git a/chromeos/services/network_config/cros_network_config.cc b/chromeos/services/network_config/cros_network_config.cc
index 4da800c8..491243be 100644
--- a/chromeos/services/network_config/cros_network_config.cc
+++ b/chromeos/services/network_config/cros_network_config.cc
@@ -139,8 +139,8 @@
   result->connection_state = technology_enabled
                                  ? GetMojoConnectionStateType(network)
                                  : mojom::ConnectionStateType::kNotConnected;
-  if (!network->last_error().empty())
-    result->error_state = network->last_error();
+  if (!network->error().empty())
+    result->error_state = network->error();
   result->guid = network->guid();
   result->name = network->name();
   result->priority = network->priority();
@@ -249,12 +249,12 @@
   net::IPAddress ipv4_address;
   if (ipv4_address.AssignFromIPLiteral(
           device->GetIpAddressByType(shill::kTypeIPv4))) {
-    result->ipv4_address = ipv4_address.CopyBytesToVector();
+    result->ipv4_address = ipv4_address;
   }
   net::IPAddress ipv6_address;
   if (ipv6_address.AssignFromIPLiteral(
           device->GetIpAddressByType(shill::kTypeIPv6))) {
-    result->ipv6_address = ipv6_address.CopyBytesToVector();
+    result->ipv6_address = ipv6_address;
   }
   result->mac_address =
       network_util::FormattedMacAddress(device->mac_address());
diff --git a/chromeos/services/network_config/cros_network_config_unittest.cc b/chromeos/services/network_config/cros_network_config_unittest.cc
index 5e689d1..3ffc5c7 100644
--- a/chromeos/services/network_config/cros_network_config_unittest.cc
+++ b/chromeos/services/network_config/cros_network_config_unittest.cc
@@ -10,6 +10,7 @@
 #include "chromeos/network/network_state_test_helper.h"
 #include "chromeos/network/network_type_pattern.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_test_observer.h"
+#include "net/base/ip_address.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
 
@@ -228,10 +229,11 @@
         // IP address match those set up in FakeShillManagerClient::
         // SetupDefaultEnvironment(). TODO(stevenjb): Support setting
         // expectations explicitly in NetworkStateTestHelper.
-        std::vector<uint8_t> ipv4_expected{100, 0, 0, 1};
+        net::IPAddress ipv4_expected;
+        ASSERT_TRUE(ipv4_expected.AssignFromIPLiteral("100.0.0.1"));
         EXPECT_EQ(ipv4_expected, devices[0]->ipv4_address);
-        std::vector<uint8_t> ipv6_expected{0, 0, 0, 0, 0, 0, 0, 0,
-                                           1, 0, 0, 0, 0, 0, 0, 1};
+        net::IPAddress ipv6_expected;
+        ASSERT_TRUE(ipv6_expected.AssignFromIPLiteral("0:0:0:0:100:0:0:1"));
         EXPECT_EQ(ipv6_expected, devices[0]->ipv6_address);
 
         EXPECT_EQ(mojom::NetworkType::kEthernet, devices[1]->type);
diff --git a/chromeos/services/network_config/public/mojom/BUILD.gn b/chromeos/services/network_config/public/mojom/BUILD.gn
index 36036fb..f64b33bf 100644
--- a/chromeos/services/network_config/public/mojom/BUILD.gn
+++ b/chromeos/services/network_config/public/mojom/BUILD.gn
@@ -9,4 +9,8 @@
     "constants.mojom",
     "cros_network_config.mojom",
   ]
+
+  deps = [
+    "//services/network/public/mojom:mojom_ip_address",
+  ]
 }
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 362f7c9..55d436ba 100644
--- a/chromeos/services/network_config/public/mojom/cros_network_config.mojom
+++ b/chromeos/services/network_config/public/mojom/cros_network_config.mojom
@@ -9,6 +9,8 @@
 
 module chromeos.network_config.mojom;
 
+import "services/network/public/mojom/ip_address.mojom";
+
 // Activation state for Cellular networks.
 enum ActivationStateType {
   kUnknown,
@@ -220,8 +222,8 @@
   // network is in a connected state, or the address type is not available,
   // these will be empty. When there are multiple IP addresses, only the first
   // is provided.
-  array<uint8> ipv4_address;
-  array<uint8> ipv6_address;
+  network.mojom.IPAddress ipv4_address;
+  network.mojom.IPAddress ipv6_address;
   // AA:BB formatted MAC address for the device. Use for display purposes only.
   string mac_address;
   // Set if the device is currently scanning.
diff --git a/components/arc/arc_features.cc b/components/arc/arc_features.cc
index 23e8f5c..0a12aaad 100644
--- a/components/arc/arc_features.cc
+++ b/components/arc/arc_features.cc
@@ -71,6 +71,12 @@
 const base::Feature kUsbHostFeature{"ArcUsbHost",
                                     base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Controls ARC USB Storage UI feature.
+// When enabled, chrome://settings and Files.app will ask if the user wants
+// to expose USB storage devices to ARC.
+const base::Feature kUsbStorageUIFeature{"ArcUsbStorageUI",
+                                         base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls ARC VPN integration.
 // When enabled, Chrome traffic will be routed through VPNs connected in
 // Android apps.
diff --git a/components/arc/arc_features.h b/components/arc/arc_features.h
index 56af17d..caf7edab 100644
--- a/components/arc/arc_features.h
+++ b/components/arc/arc_features.h
@@ -26,6 +26,7 @@
 extern const base::Feature kPrintSpoolerExperimentFeature;
 extern const base::Feature kSmartTextSelectionFeature;
 extern const base::Feature kUsbHostFeature;
+extern const base::Feature kUsbStorageUIFeature;
 extern const base::Feature kVpnFeature;
 
 }  // namespace arc
diff --git a/components/arc/common/policy.mojom b/components/arc/common/policy.mojom
index 35396e8..7148902 100644
--- a/components/arc/common/policy.mojom
+++ b/components/arc/common/policy.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 5
+// Next MinVersion: 6
 
 module arc.mojom;
 
@@ -59,7 +59,7 @@
   SUCCESS = 2,  // The command was successfully executed.
 };
 
-// Next Method ID: 2
+// Next Method ID: 7
 interface PolicyHost {
   // Get policies from Chrome OS, as JSON-encoded dictionary with the policies'
   // names as keys and their values as values. The list of possible policies can
@@ -73,16 +73,26 @@
   [MinVersion=1] ReportCompliance@1(string request) => (string response);
 
   // Reports that request was sent to CloudDPS for set of packages.
-  [MinVersion=3] ReportCloudDpsRequested(mojo_base.mojom.Time time,
+  [MinVersion=3] ReportCloudDpsRequested@2(mojo_base.mojom.Time time,
                                          array<string> package_names);
   // Reports that successful response was received from CloudDPS for set of
   // packages.
-  [MinVersion=3] ReportCloudDpsSucceeded(mojo_base.mojom.Time time,
+  [MinVersion=3] ReportCloudDpsSucceeded@3(mojo_base.mojom.Time time,
                                          array<string> package_names);
   // Reports that CloudDPS reports an error for packages.
-  [MinVersion=3] ReportCloudDpsFailed(mojo_base.mojom.Time time,
+  [MinVersion=3] ReportCloudDpsFailed@4(mojo_base.mojom.Time time,
                                       string package_name,
                                       InstallErrorReason reason);
+
+  // Reports that packages were scheduled for force installing
+  // via Play Store directly.
+  [MinVersion=5] ReportDirectInstall@5(mojo_base.mojom.Time time,
+                                     array<string> package_names);
+
+  // Reports that CloudDPC is giving up after several retries to
+  // force install the packages.
+  [MinVersion=5] ReportForceInstallMainLoopFailed@6(mojo_base.mojom.Time time,
+                                          array<string> package_names);
 };
 
 // Next Method ID: 4
diff --git a/components/arc/volume_mounter/arc_volume_mounter_bridge.cc b/components/arc/volume_mounter/arc_volume_mounter_bridge.cc
index 297286cd..a79e6d1 100644
--- a/components/arc/volume_mounter/arc_volume_mounter_bridge.cc
+++ b/components/arc/volume_mounter/arc_volume_mounter_bridge.cc
@@ -5,6 +5,7 @@
 #include "components/arc/volume_mounter/arc_volume_mounter_bridge.h"
 
 #include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/memory/singleton.h"
 #include "base/task/post_task.h"
@@ -12,6 +13,7 @@
 #include "chromeos/disks/disk.h"
 #include "chromeos/disks/disk_mount_manager.h"
 #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
+#include "components/arc/arc_features.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/session/arc_bridge_service.h"
 #include "components/prefs/pref_service.h"
@@ -126,6 +128,9 @@
 
 bool ArcVolumeMounterBridge::HasAccessToRemovableMedia() const {
   DCHECK(pref_service_);
+  // If the UI is not enabled, allow the access.
+  if (!base::FeatureList::IsEnabled(arc::kUsbStorageUIFeature))
+    return true;
   return pref_service_->GetBoolean(prefs::kArcHasAccessToRemovableMedia);
 }
 
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 25f0e811..bf287114 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -235,8 +235,6 @@
     "webdata/autofill_profile_sync_bridge.h",
     "webdata/autofill_profile_sync_difference_tracker.cc",
     "webdata/autofill_profile_sync_difference_tracker.h",
-    "webdata/autofill_profile_syncable_service.cc",
-    "webdata/autofill_profile_syncable_service.h",
     "webdata/autofill_sync_bridge_util.cc",
     "webdata/autofill_sync_bridge_util.h",
     "webdata/autofill_table.cc",
@@ -554,7 +552,6 @@
     "webdata/autocomplete_sync_bridge_unittest.cc",
     "webdata/autofill_profile_sync_bridge_unittest.cc",
     "webdata/autofill_profile_sync_difference_tracker_unittest.cc",
-    "webdata/autofill_profile_syncable_service_unittest.cc",
     "webdata/autofill_sync_bridge_util_unittest.cc",
     "webdata/autofill_table_unittest.cc",
     "webdata/autofill_wallet_metadata_sync_bridge_unittest.cc",
@@ -617,7 +614,6 @@
     "//components/variations/net",
     "//components/version_info:version_info",
     "//components/webdata/common",
-    "//components/webdata_services:test_support",
     "//google_apis",
     "//google_apis:test_support",
     "//net:test_support",
diff --git a/components/autofill/core/browser/autocomplete_history_manager_unittest.cc b/components/autofill/core/browser/autocomplete_history_manager_unittest.cc
index 9a4ce6d1..53f1c850 100644
--- a/components/autofill/core/browser/autocomplete_history_manager_unittest.cc
+++ b/components/autofill/core/browser/autocomplete_history_manager_unittest.cc
@@ -27,7 +27,6 @@
 #include "components/autofill/core/common/form_data.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/version_info/version_info.h"
-#include "components/webdata_services/web_data_service_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/rect.h"
diff --git a/components/autofill/core/browser/autofill_data_util.cc b/components/autofill/core/browser/autofill_data_util.cc
index 42bf11ed..111f8284 100644
--- a/components/autofill/core/browser/autofill_data_util.cc
+++ b/components/autofill/core/browser/autofill_data_util.cc
@@ -292,6 +292,12 @@
   return group_bitmask;
 }
 
+bool IsSupportedFormType(uint32_t groups) {
+  return ContainsAddress(groups) ||
+         ContainsName(groups) + ContainsEmail(groups) + ContainsPhone(groups) >=
+             2;
+}
+
 std::string TruncateUTF8(const std::string& data) {
   std::string trimmed_value;
   base::TruncateUTF8ToByteSize(data, AutofillTable::kMaxDataLength,
diff --git a/components/autofill/core/browser/autofill_data_util.h b/components/autofill/core/browser/autofill_data_util.h
index ba8f55f..f1ffed4f 100644
--- a/components/autofill/core/browser/autofill_data_util.h
+++ b/components/autofill/core/browser/autofill_data_util.h
@@ -57,6 +57,10 @@
 // PHONE_HOME FieldTypeGroups are associated with the given |field_types|.
 uint32_t DetermineGroups(const std::vector<ServerFieldType>& types);
 
+// Returns true if a form has address fields or has least two supported
+// non-address fields.
+bool IsSupportedFormType(uint32_t groups);
+
 // Truncates a string to the nearest UTF-8 character that will leave
 // the string less than or equal to the specified byte size.
 std::string TruncateUTF8(const std::string& data);
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index 99f2d48b..54ccb04 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -6445,6 +6445,7 @@
   base::HistogramTester histogram_tester;
   autofill_manager_->DidShowSuggestions(/*has_autofill_suggestions=*/true, form,
                                         form.fields[0]);
+
   histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address.AddressOnly",
                                      FORM_EVENT_SUGGESTIONS_SHOWN, 1);
   histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address.AddressOnly",
@@ -6452,13 +6453,58 @@
 
   // Logging is not done for other types of address forms.
   const std::string histograms = histogram_tester.GetAllHistogramsRecorded();
-  EXPECT_THAT(histograms,
-              Not(AnyOf("Autofill.FormEvents.Address.AddressPlusContact",
-                        "Autofill.FormEvents.Address.AddressPlusEmail",
-                        "Autofill.FormEvents.Address.AddressPlusEmailPlusPhone",
-                        "Autofill.FormEvents.Address.AddressPlusPhone",
-                        "Autofill.FormEvents.Address.ContactOnly",
-                        "Autofill.FormEvents.Address.Other")));
+  EXPECT_THAT(
+      histograms,
+      Not(AnyOf(
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusContact"),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusEmail "),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusEmailPlusPhone"),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusPhone"),
+          HasSubstr("Autofill.FormEvents.Address.ContactOnly"),
+          HasSubstr("Autofill.FormEvents.Address.Other"))));
+}
+
+TEST_F(AutofillManagerTest,
+       DidShowSuggestions_LogByType_AddressOnlyWithoutName) {
+  // Create a form with address fields.
+  FormData form;
+  form.name = ASCIIToUTF16("MyForm");
+  form.button_titles = {std::make_pair(
+      ASCIIToUTF16("Submit"), ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)};
+  form.url = GURL("http://myform.com/form.html");
+  form.action = GURL("http://myform.com/submit.html");
+  form.main_frame_origin =
+      url::Origin::Create(GURL("https://myform_root.com/form.html"));
+  form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION;
+
+  FormFieldData field;
+  test::CreateTestFormField("Address Line 1", "addr1", "", "text", &field);
+  form.fields.push_back(field);
+  test::CreateTestFormField("Address Line 2", "addr2", "", "text", &field);
+  form.fields.push_back(field);
+  test::CreateTestFormField("Postal Code", "zipcode", "", "text", &field);
+  form.fields.push_back(field);
+
+  base::HistogramTester histogram_tester;
+  autofill_manager_->DidShowSuggestions(/*has_autofill_suggestions=*/true, form,
+                                        form.fields[0]);
+
+  histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address.AddressOnly",
+                                     FORM_EVENT_SUGGESTIONS_SHOWN, 1);
+  histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address.AddressOnly",
+                                     FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+
+  // Logging is not done for other types of address forms.
+  const std::string histograms = histogram_tester.GetAllHistogramsRecorded();
+  EXPECT_THAT(
+      histograms,
+      Not(AnyOf(
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusContact"),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusEmail "),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusEmailPlusPhone"),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusPhone"),
+          HasSubstr("Autofill.FormEvents.Address.ContactOnly"),
+          HasSubstr("Autofill.FormEvents.Address.Other"))));
 }
 
 TEST_F(AutofillManagerTest, DidShowSuggestions_LogByType_ContactOnly) {
@@ -6491,13 +6537,57 @@
 
   // Logging is not done for other types of address forms.
   const std::string histograms = histogram_tester.GetAllHistogramsRecorded();
-  EXPECT_THAT(histograms,
-              Not(AnyOf("Autofill.FormEvents.Address.AddressPlusContact",
-                        "Autofill.FormEvents.Address.AddressPlusEmail",
-                        "Autofill.FormEvents.Address.AddressPlusEmailPlusPhone",
-                        "Autofill.FormEvents.Address.AddressPlusPhone",
-                        "Autofill.FormEvents.Address.AddressOnly",
-                        "Autofill.FormEvents.Address.Other")));
+  EXPECT_THAT(
+      histograms,
+      Not(AnyOf(
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusContact"),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusEmail "),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusEmailPlusPhone"),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusPhone"),
+          HasSubstr("Autofill.FormEvents.Address.AddressOnly"),
+          HasSubstr("Autofill.FormEvents.Address.Other"))));
+}
+
+TEST_F(AutofillManagerTest,
+       DidShowSuggestions_LogByType_ContactOnlyWithoutName) {
+  // Create a form with contact fields.
+  FormData form;
+  form.name = ASCIIToUTF16("MyForm");
+  form.button_titles = {std::make_pair(
+      ASCIIToUTF16("Submit"), ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)};
+  form.url = GURL("http://myform.com/form.html");
+  form.action = GURL("http://myform.com/submit.html");
+  form.main_frame_origin =
+      url::Origin::Create(GURL("https://myform_root.com/form.html"));
+  form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION;
+
+  FormFieldData field;
+  test::CreateTestFormField("Phone Number", "phonenumber", "", "tel", &field);
+  form.fields.push_back(field);
+  test::CreateTestFormField("Email", "email", "", "email", &field);
+  form.fields.push_back(field);
+  test::CreateTestFormField("Email", "email", "", "email", &field);
+  form.fields.push_back(field);
+
+  base::HistogramTester histogram_tester;
+  autofill_manager_->DidShowSuggestions(/*has_autofill_suggestions=*/true, form,
+                                        form.fields[0]);
+  histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address.ContactOnly",
+                                     FORM_EVENT_SUGGESTIONS_SHOWN, 1);
+  histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address.ContactOnly",
+                                     FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+
+  // Logging is not done for other types of address forms.
+  const std::string histograms = histogram_tester.GetAllHistogramsRecorded();
+  EXPECT_THAT(
+      histograms,
+      Not(AnyOf(
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusContact"),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusEmail "),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusEmailPlusPhone"),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusPhone"),
+          HasSubstr("Autofill.FormEvents.Address.AddressOnly"),
+          HasSubstr("Autofill.FormEvents.Address.Other"))));
 }
 
 TEST_F(AutofillManagerTest, DidShowSuggestions_LogByType_Other) {
@@ -6530,17 +6620,19 @@
 
   // Logging is not done for other types of address forms.
   const std::string histograms = histogram_tester.GetAllHistogramsRecorded();
-  EXPECT_THAT(histograms,
-              Not(AnyOf("Autofill.FormEvents.Address.AddressPlusContact",
-                        "Autofill.FormEvents.Address.AddressPlusEmail",
-                        "Autofill.FormEvents.Address.AddressPlusEmailPlusPhone",
-                        "Autofill.FormEvents.Address.AddressPlusPhone",
-                        "Autofill.FormEvents.Address.AddressOnly",
-                        "Autofill.FormEvents.Address.ContactOnly")));
+  EXPECT_THAT(
+      histograms,
+      Not(AnyOf(
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusContact"),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusEmail "),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusEmailPlusPhone"),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusPhone"),
+          HasSubstr("Autofill.FormEvents.Address.AddressOnly"),
+          HasSubstr("Autofill.FormEvents.Address.ContactOnly"))));
 }
 
 TEST_F(AutofillManagerTest, DidShowSuggestions_LogByType_AddressPlusEmail) {
-  // Create a form with name fields.
+  // Create a form with name, address, and email fields.
   FormData form;
   form.name = ASCIIToUTF16("MyForm");
   form.button_titles = {std::make_pair(
@@ -6579,12 +6671,63 @@
 
   // Logging is not done for other types of address forms.
   const std::string histograms = histogram_tester.GetAllHistogramsRecorded();
-  EXPECT_THAT(histograms,
-              Not(AnyOf("Autofill.FormEvents.Address.AddressPlusEmailPlusPhone",
-                        "Autofill.FormEvents.Address.AddressPlusPhone",
-                        "Autofill.FormEvents.Address.AddressOnly",
-                        "Autofill.FormEvents.Address.ContactOnly",
-                        "Autofill.FormEvents.Address.Other")));
+  EXPECT_THAT(
+      histograms,
+      Not(AnyOf(
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusEmailPlusPhone"),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusPhone"),
+          HasSubstr("Autofill.FormEvents.Address.AddressOnly"),
+          HasSubstr("Autofill.FormEvents.Address.ContactOnly"),
+          HasSubstr("Autofill.FormEvents.Address.Other"))));
+}
+
+TEST_F(AutofillManagerTest,
+       DidShowSuggestions_LogByType_AddressPlusEmailWithoutName) {
+  // Create a form with address and email fields.
+  FormData form;
+  form.name = ASCIIToUTF16("MyForm");
+  form.button_titles = {std::make_pair(
+      ASCIIToUTF16("Submit"), ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)};
+  form.url = GURL("http://myform.com/form.html");
+  form.action = GURL("http://myform.com/submit.html");
+  form.main_frame_origin =
+      url::Origin::Create(GURL("https://myform_root.com/form.html"));
+  form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION;
+
+  FormFieldData field;
+  test::CreateTestFormField("Address Line 1", "addr1", "", "text", &field);
+  form.fields.push_back(field);
+  test::CreateTestFormField("Address Line 2", "addr2", "", "text", &field);
+  form.fields.push_back(field);
+  test::CreateTestFormField("Email", "email", "", "email", &field);
+  form.fields.push_back(field);
+
+  base::HistogramTester histogram_tester;
+  autofill_manager_->DidShowSuggestions(/*has_autofill_suggestions=*/true, form,
+                                        form.fields[0]);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.FormEvents.Address.AddressPlusEmail",
+      FORM_EVENT_SUGGESTIONS_SHOWN, 1);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.FormEvents.Address.AddressPlusEmail",
+      FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.FormEvents.Address.AddressPlusContact",
+      FORM_EVENT_SUGGESTIONS_SHOWN, 1);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.FormEvents.Address.AddressPlusContact",
+      FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+
+  // Logging is not done for other types of address forms.
+  const std::string histograms = histogram_tester.GetAllHistogramsRecorded();
+  EXPECT_THAT(
+      histograms,
+      Not(AnyOf(
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusEmailPlusPhone"),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusPhone"),
+          HasSubstr("Autofill.FormEvents.Address.AddressOnly"),
+          HasSubstr("Autofill.FormEvents.Address.ContactOnly"),
+          HasSubstr("Autofill.FormEvents.Address.Other"))));
 }
 
 TEST_F(AutofillManagerTest, DidShowSuggestions_LogByType_AddressPlusPhone) {
@@ -6627,17 +6770,68 @@
 
   // Logging is not done for other types of address forms.
   const std::string histograms = histogram_tester.GetAllHistogramsRecorded();
-  EXPECT_THAT(histograms,
-              Not(AnyOf("Autofill.FormEvents.Address.AddressPlusEmailPlusPhone",
-                        "Autofill.FormEvents.Address.AddressPlusEmail",
-                        "Autofill.FormEvents.Address.AddressOnly",
-                        "Autofill.FormEvents.Address.ContactOnly",
-                        "Autofill.FormEvents.Address.Other")));
+  EXPECT_THAT(
+      histograms,
+      Not(AnyOf(
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusEmailPlusPhone"),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusEmail "),
+          HasSubstr("Autofill.FormEvents.Address.AddressOnly"),
+          HasSubstr("Autofill.FormEvents.Address.ContactOnly"),
+          HasSubstr("Autofill.FormEvents.Address.Other"))));
+}
+
+TEST_F(AutofillManagerTest,
+       DidShowSuggestions_LogByType_AddressPlusPhoneWithoutName) {
+  // Create a form with name, address, and phone fields.
+  FormData form;
+  form.name = ASCIIToUTF16("MyForm");
+  form.button_titles = {std::make_pair(
+      ASCIIToUTF16("Submit"), ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)};
+  form.url = GURL("http://myform.com/form.html");
+  form.action = GURL("http://myform.com/submit.html");
+  form.main_frame_origin =
+      url::Origin::Create(GURL("https://myform_root.com/form.html"));
+  form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION;
+
+  FormFieldData field;
+  test::CreateTestFormField("Address Line 1", "addr1", "", "text", &field);
+  form.fields.push_back(field);
+  test::CreateTestFormField("Address Line 2", "addr2", "", "text", &field);
+  form.fields.push_back(field);
+  test::CreateTestFormField("Phone Number", "phonenumber", "", "tel", &field);
+  form.fields.push_back(field);
+
+  base::HistogramTester histogram_tester;
+  autofill_manager_->DidShowSuggestions(/*has_autofill_suggestions=*/true, form,
+                                        form.fields[0]);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.FormEvents.Address.AddressPlusPhone",
+      FORM_EVENT_SUGGESTIONS_SHOWN, 1);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.FormEvents.Address.AddressPlusPhone",
+      FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.FormEvents.Address.AddressPlusContact",
+      FORM_EVENT_SUGGESTIONS_SHOWN, 1);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.FormEvents.Address.AddressPlusContact",
+      FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+
+  // Logging is not done for other types of address forms.
+  const std::string histograms = histogram_tester.GetAllHistogramsRecorded();
+  EXPECT_THAT(
+      histograms,
+      Not(AnyOf(
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusEmailPlusPhone"),
+          HasSubstr("Autofill.FormEvents.Address.AddressPlusEmail "),
+          HasSubstr("Autofill.FormEvents.Address.AddressOnly"),
+          HasSubstr("Autofill.FormEvents.Address.ContactOnly"),
+          HasSubstr("Autofill.FormEvents.Address.Other"))));
 }
 
 TEST_F(AutofillManagerTest,
        DidShowSuggestions_LogByType_AddressPlusEmailPlusPhone) {
-  // Create a form with name fields.
+  // Create a form with name, address, phone, and email fields.
   FormData form;
   form.name = ASCIIToUTF16("MyForm");
   form.button_titles = {std::make_pair(
@@ -6678,12 +6872,61 @@
 
   // Logging is not done for other types of address forms.
   const std::string histograms = histogram_tester.GetAllHistogramsRecorded();
-  EXPECT_THAT(histograms,
-              Not(AnyOf("Autofill.FormEvents.Address.AddressPlusPhone",
-                        "Autofill.FormEvents.Address.AddressPlusEmail",
-                        "Autofill.FormEvents.Address.AddressOnly",
-                        "Autofill.FormEvents.Address.ContactOnly",
-                        "Autofill.FormEvents.Address.Other")));
+  EXPECT_THAT(
+      histograms,
+      Not(AnyOf(HasSubstr("Autofill.FormEvents.Address.AddressPlusPhone"),
+                HasSubstr("Autofill.FormEvents.Address.AddressPlusEmail "),
+                HasSubstr("Autofill.FormEvents.Address.AddressOnly"),
+                HasSubstr("Autofill.FormEvents.Address.ContactOnly"),
+                HasSubstr("Autofill.FormEvents.Address.Other"))));
+}
+
+TEST_F(AutofillManagerTest,
+       DidShowSuggestions_LogByType_AddressPlusEmailPlusPhoneWithoutName) {
+  // Create a form with address, phone, and email fields.
+  FormData form;
+  form.name = ASCIIToUTF16("MyForm");
+  form.button_titles = {std::make_pair(
+      ASCIIToUTF16("Submit"), ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)};
+  form.url = GURL("http://myform.com/form.html");
+  form.action = GURL("http://myform.com/submit.html");
+  form.main_frame_origin =
+      url::Origin::Create(GURL("https://myform_root.com/form.html"));
+  form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION;
+
+  FormFieldData field;
+  test::CreateTestFormField("Address Line 1", "addr1", "", "text", &field);
+  form.fields.push_back(field);
+  test::CreateTestFormField("Phone Number", "phonenumber", "", "tel", &field);
+  form.fields.push_back(field);
+  test::CreateTestFormField("Email", "email", "", "email", &field);
+  form.fields.push_back(field);
+
+  base::HistogramTester histogram_tester;
+  autofill_manager_->DidShowSuggestions(/*has_autofill_suggestions=*/true, form,
+                                        form.fields[0]);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.FormEvents.Address.AddressPlusEmailPlusPhone",
+      FORM_EVENT_SUGGESTIONS_SHOWN, 1);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.FormEvents.Address.AddressPlusEmailPlusPhone",
+      FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.FormEvents.Address.AddressPlusContact",
+      FORM_EVENT_SUGGESTIONS_SHOWN, 1);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.FormEvents.Address.AddressPlusContact",
+      FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+
+  // Logging is not done for other types of address forms.
+  const std::string histograms = histogram_tester.GetAllHistogramsRecorded();
+  EXPECT_THAT(
+      histograms,
+      Not(AnyOf(HasSubstr("Autofill.FormEvents.Address.AddressPlusPhone"),
+                HasSubstr("Autofill.FormEvents.Address.AddressPlusEmail "),
+                HasSubstr("Autofill.FormEvents.Address.AddressOnly"),
+                HasSubstr("Autofill.FormEvents.Address.ContactOnly"),
+                HasSubstr("Autofill.FormEvents.Address.Other"))));
 }
 
 TEST_F(AutofillManagerTest,
diff --git a/components/autofill/core/browser/metrics/address_form_event_logger.cc b/components/autofill/core/browser/metrics/address_form_event_logger.cc
index 129918d6..228a3ca 100644
--- a/components/autofill/core/browser/metrics/address_form_event_logger.cc
+++ b/components/autofill/core/browser/metrics/address_form_event_logger.cc
@@ -27,14 +27,19 @@
 // Returns the histogram suffix corresponding to the given |bitmask|.
 std::string GetSuffixForFormType(uint32_t bitmask) {
   switch (bitmask) {
+    case kAddress | kEmail | kPhone:
     case kName | kAddress | kEmail | kPhone:
       return ".AddressPlusEmailPlusPhone";
+    case kAddress | kPhone:
     case kName | kAddress | kPhone:
       return ".AddressPlusPhone";
+    case kAddress | kEmail:
     case kName | kAddress | kEmail:
       return ".AddressPlusEmail";
+    case kAddress:
     case kName | kAddress:
       return ".AddressOnly";
+    case kEmail | kPhone:
     case kName | kEmail | kPhone:
     case kName | kEmail:
     case kName | kPhone:
@@ -120,7 +125,7 @@
   uint32_t groups = data_util::DetermineGroups(types);
   base::UmaHistogramEnumeration(name + GetSuffixForFormType(groups), event,
                                 NUM_FORM_EVENTS);
-  if (data_util::ContainsName(groups) && data_util::ContainsAddress(groups) &&
+  if (data_util::ContainsAddress(groups) &&
       (data_util::ContainsPhone(groups) || data_util::ContainsEmail(groups))) {
     base::UmaHistogramEnumeration(name + ".AddressPlusContact", event,
                                   NUM_FORM_EVENTS);
diff --git a/components/autofill/core/browser/ui/accessory_sheet_data.cc b/components/autofill/core/browser/ui/accessory_sheet_data.cc
index fd7dbd5..03593913 100644
--- a/components/autofill/core/browser/ui/accessory_sheet_data.cc
+++ b/components/autofill/core/browser/ui/accessory_sheet_data.cc
@@ -100,6 +100,8 @@
       return os << "Payments sheet";
     case AccessoryTabType::ADDRESSES:
       return os << "Address sheet";
+    case AccessoryTabType::TOUCH_TO_FILL:
+      return os << "Touch to Fill sheet";
     case AccessoryTabType::ALL:
       return os << "All sheets";
     case AccessoryTabType::COUNT:
diff --git a/components/autofill/core/browser/ui/accessory_sheet_enums.h b/components/autofill/core/browser/ui/accessory_sheet_enums.h
index ee591af..029536c3 100644
--- a/components/autofill/core/browser/ui/accessory_sheet_enums.h
+++ b/components/autofill/core/browser/ui/accessory_sheet_enums.h
@@ -18,6 +18,7 @@
   PASSWORDS = 1,
   CREDIT_CARDS = 2,
   ADDRESSES = 3,
+  TOUCH_TO_FILL = 4,
   COUNT,
 };
 
diff --git a/components/autofill/core/browser/ui/label_formatter.cc b/components/autofill/core/browser/ui/label_formatter.cc
index e08f909..5f87d68 100644
--- a/components/autofill/core/browser/ui/label_formatter.cc
+++ b/components/autofill/core/browser/ui/label_formatter.cc
@@ -30,18 +30,6 @@
 using data_util::bit_field_type_groups::kName;
 using data_util::bit_field_type_groups::kPhone;
 
-namespace {
-
-// Returns true if a form has address fields or has least two supported
-// non-address fields.
-bool IsSupportedFormType(uint32_t groups) {
-  return ContainsAddress(groups) ||
-         ContainsName(groups) + ContainsEmail(groups) + ContainsPhone(groups) >=
-             2;
-}
-
-}  // namespace
-
 LabelFormatter::LabelFormatter(const std::vector<AutofillProfile*>& profiles,
                                const std::string& app_locale,
                                ServerFieldType focused_field_type,
@@ -100,7 +88,7 @@
     ServerFieldType focused_field_type,
     const std::vector<ServerFieldType>& field_types) {
   const uint32_t groups = data_util::DetermineGroups(field_types);
-  if (!IsSupportedFormType(groups)) {
+  if (!data_util::IsSupportedFormType(groups)) {
     return nullptr;
   }
 
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc
index 7fd490e..f8dc050 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc
@@ -275,7 +275,9 @@
                      base::Unretained(web_data_backend_))));
 
   std::vector<std::unique_ptr<AutofillProfile>> profiles_to_upload_to_sync;
-  RETURN_IF_ERROR(tracker->FlushToSync(&profiles_to_upload_to_sync));
+  std::vector<std::string> profiles_to_delete_from_sync;
+  RETURN_IF_ERROR(tracker->FlushToSync(&profiles_to_upload_to_sync,
+                                       &profiles_to_delete_from_sync));
   for (const std::unique_ptr<AutofillProfile>& entry :
        profiles_to_upload_to_sync) {
     change_processor()->Put(GetStorageKeyFromAutofillProfile(*entry),
@@ -285,6 +287,9 @@
     // TODO(crbug.com/904390): Remove when the investigation is over.
     ReportAutofillProfileAddOrUpdateOrigin(origin);
   }
+  for (const std::string& storage_key : profiles_to_delete_from_sync) {
+    change_processor()->Delete(storage_key, metadata_change_list.get());
+  }
 
   return static_cast<syncer::SyncMetadataStoreChangeList*>(
              metadata_change_list.get())
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
index 8968a0f..fe32010 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
@@ -653,7 +653,8 @@
   AddAutofillProfilesToTable({local1, local2});
 
   // The synced profiles are identical to the local ones, except that the guids
-  // and use_count values are different.
+  // and use_count values are different. Remote ones have additional company
+  // name which makes them not be identical.
   AutofillProfileSpecifics remote1 =
       CreateAutofillProfileSpecifics(kGuidC, kHttpsOrigin);
   remote1.add_name_first("John");
@@ -672,10 +673,7 @@
   // should never overwrite a verified one.
   AutofillProfileSpecifics merged1(remote1);
   merged1.set_origin(kHttpOrigin);
-  // TODO(jkrcal): This is taken over from the previous test suite without any
-  // reasoning why this happens. This indeed happens, deep in
-  // AutofillProfileComparator when merging profiles both without NAME_FULL, we
-  // obtain a profile with NAME_FULL. Not sure if intended.
+  // When merging, full name gets populated.
   merged1.add_name_full("John");
   // Merging two profile takes their max use count.
   merged1.set_use_count(27);
@@ -696,11 +694,6 @@
                   WithUsageStats(CreateAutofillProfile(remote2))));
 }
 
-// TODO(jkrcal): All the MergeSimilarProfiles_* tests need some diff in Info to
-// trigger the merge similar code path (we create the diff using phone number).
-// Otherwise, we trigger the merge same code path and none of the tests pass. Is
-// it desired?
-
 // Tests that MergeSimilarProfiles keeps the most recent use date of the two
 // profiles being merged.
 TEST_F(AutofillProfileSyncBridgeTest,
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc
index 8f96bb8..44af5e0 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc
@@ -71,30 +71,51 @@
     // Look for exact duplicates, compare only profile contents (and
     // ignore origin and language code in comparison).
     if (local.Compare(*remote) == 0) {
-      // We found a duplicate, we keep the new (remote) one and delete the
-      // local one.
+      // We found a duplicate. We keep the version with the bigger storage key.
       DVLOG(2)
           << "[AUTOFILL SYNC] The profile "
           << base::UTF16ToUTF8(local.GetRawInfo(NAME_FIRST))
           << base::UTF16ToUTF8(local.GetRawInfo(NAME_LAST))
-          << " already exists with a different storage key; keep the remote key"
-          << remote_storage_key << " and delete the local key "
-          << local_storage_key;
-
-      // Ensure that a verified profile can never revert back to an unverified
-      // one. In such a case, take over the local origin for the new (remote)
-      // entry.
-      if (local.IsVerified() && !remote->IsVerified()) {
-        remote->set_origin(local.origin());
-        // Save a copy of the remote profile also to sync.
-        save_to_sync_.push_back(std::make_unique<AutofillProfile>(*remote));
+          << " already exists with a different storage key; keep the bigger key"
+          << std::max(remote_storage_key, local_storage_key)
+          << " and delete the smaller key "
+          << std::min(remote_storage_key, local_storage_key);
+      if (remote_storage_key > local_storage_key) {
+        // We keep the remote entity and delete the local one.
+        // Ensure that a verified profile can never revert back to an unverified
+        // one. In such a case, take over the old origin for the new entry.
+        if (local.IsVerified() && !remote->IsVerified()) {
+          remote->set_origin(local.origin());
+          // Save a copy of the remote profile also to sync.
+          save_to_sync_.push_back(std::make_unique<AutofillProfile>(*remote));
+        }
+        add_to_local_.push_back(std::move(remote));
+        DeleteFromLocal(local_storage_key);
+      } else {
+        // We keep the local entity and delete the remote one.
+        // Ensure that a verified profile can never revert back to an unverified
+        // one. In such a case, modify the origin and re-upload. Otherwise,
+        // there's no need to upload it: either is was already uploaded before
+        // (if this is incremental sync) or we'll upload it with all the
+        // remaining data in GetLocalOnlyEntries (if this is an initial sync).
+        if (remote->IsVerified() && !local.IsVerified()) {
+          auto modified_local = std::make_unique<AutofillProfile>(local);
+          modified_local->set_origin(remote->origin());
+          update_to_local_.push_back(
+              std::make_unique<AutofillProfile>(*modified_local));
+          save_to_sync_.push_back(std::move(modified_local));
+          // The local entity is already marked for upload so it is not local
+          // only anymore (we do not want to upload it once again while flushing
+          // if this is initial sync).
+          GetLocalOnlyEntries()->erase(local_storage_key);
+        }
+        delete_from_sync_.insert(remote_storage_key);
       }
-      // Delete the local profile that gets replaced by |remote|.
-      DeleteFromLocal(local_storage_key);
-      break;
+      return base::nullopt;
     }
   }
 
+  // If no duplicate was found, just add the remote profile.
   add_to_local_.push_back(std::move(remote));
   return base::nullopt;
 }
@@ -132,10 +153,14 @@
 }
 
 Optional<ModelError> AutofillProfileSyncDifferenceTracker::FlushToSync(
-    std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync) {
+    std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync,
+    std::vector<std::string>* profiles_to_delete_from_sync) {
   for (std::unique_ptr<AutofillProfile>& entry : save_to_sync_) {
     profiles_to_upload_to_sync->push_back(std::move(entry));
   }
+  for (const std::string& entry : delete_from_sync_) {
+    profiles_to_delete_from_sync->push_back(std::move(entry));
+  }
   return base::nullopt;
 }
 
@@ -200,9 +225,11 @@
 }
 
 Optional<ModelError> AutofillProfileInitialSyncDifferenceTracker::FlushToSync(
-    std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync) {
+    std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync,
+    std::vector<std::string>* profiles_to_delete_from_sync) {
   // First, flush standard updates to sync.
-  AutofillProfileSyncDifferenceTracker::FlushToSync(profiles_to_upload_to_sync);
+  AutofillProfileSyncDifferenceTracker::FlushToSync(
+      profiles_to_upload_to_sync, profiles_to_delete_from_sync);
 
   // For initial sync, we additionally need to upload all local only entries.
   if (!GetLocalOnlyEntries()) {
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h
index 055de2e..437d0d37 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h
@@ -50,11 +50,12 @@
       base::OnceClosure autofill_changes_callback);
 
   // Writes into |profiles_to_upload_to_sync| all autofill profiles to be sent
-  // to the sync server. After flushing, not further remote changes should get
-  // incorporated.
+  // to the sync server, and into |profiles_to_delete_from_sync| the storage
+  // keys of all profiles to be deleted from the server. After flushing, no
+  // further remote changes should get incorporated.
   virtual base::Optional<syncer::ModelError> FlushToSync(
-      std::vector<std::unique_ptr<AutofillProfile>>*
-          profiles_to_upload_to_sync);
+      std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync,
+      std::vector<std::string>* profiles_to_delete_from_sync);
 
  protected:
   // If the entry is found, |entry| will be return, otherwise base::nullopt is
@@ -98,9 +99,13 @@
   std::vector<std::unique_ptr<AutofillProfile>> add_to_local_;
   std::vector<std::unique_ptr<AutofillProfile>> update_to_local_;
 
-  // Contains merged data for entries that existed on both sync and local sides
+  // Contains data for entries that existed on both sync and local sides
   // and need to be saved back to sync.
   std::vector<std::unique_ptr<AutofillProfile>> save_to_sync_;
+  // Contains data for entries that existed on both sync and local
+  // sides and need to be deleted from sync (because the conflict resolution
+  // preferred the local copies).
+  std::set<std::string> delete_from_sync_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncDifferenceTracker);
@@ -116,8 +121,8 @@
       const std::string& storage_key) override;
 
   base::Optional<syncer::ModelError> FlushToSync(
-      std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync)
-      override;
+      std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync,
+      std::vector<std::string>* profiles_to_delete_from_sync) override;
 
   // Performs an additional pass through remote entries incorporated from sync
   // to find any similarities with local entries. Should be run after all
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc
index 799ff003..f4a1100 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc
@@ -30,13 +30,18 @@
 using testing::IsEmpty;
 
 // Some guids for testing.
-const char kGuidA[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44A";
-const char kGuidB[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44B";
+const char kSmallerGuid[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44A";
+const char kBiggerGuid[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44B";
 const char kHttpOrigin[] = "http://www.example.com/";
 const char kHttpsOrigin[] = "https://www.example.com/";
 const char kLocaleString[] = "en-US";
 const base::Time kJune2017 = base::Time::FromDoubleT(1497552271);
 
+struct UpdatesToSync {
+  std::vector<AutofillProfile> profiles_to_upload_to_sync;
+  std::vector<std::string> profiles_to_delete_from_sync;
+};
+
 }  // namespace
 
 class AutofillProfileSyncDifferenceTrackerTestBase : public testing::Test {
@@ -65,24 +70,26 @@
                                  std::make_unique<AutofillProfile>(profile)));
   }
 
-  std::vector<AutofillProfile> FlushAndReturnProfilesToUploadToSync() {
+  UpdatesToSync FlushToSync() {
     EXPECT_EQ(base::nullopt,
               tracker()->FlushToLocal(
                   /*autofill_changes_callback=*/base::DoNothing()));
 
+    UpdatesToSync updates;
     std::vector<std::unique_ptr<AutofillProfile>> vector_of_unique_ptrs;
     EXPECT_EQ(base::nullopt,
               tracker()->FlushToSync(
-                  /*profiles_to_upload_to_sync=*/&vector_of_unique_ptrs));
+                  /*profiles_to_upload_to_sync=*/&vector_of_unique_ptrs,
+                  /*profiles_to_delete_from_sync=*/&updates
+                      .profiles_to_delete_from_sync));
 
     // Copy all the elements by value so that we have a vector that is easier to
     // work with in the test.
-    std::vector<AutofillProfile> vector_of_values;
     for (const std::unique_ptr<AutofillProfile>& entry :
          vector_of_unique_ptrs) {
-      vector_of_values.push_back(*entry);
+      updates.profiles_to_upload_to_sync.push_back(*entry);
     }
-    return vector_of_values;
+    return updates;
   }
 
   std::vector<AutofillProfile> GetAllLocalData() {
@@ -130,63 +137,69 @@
 
 TEST_F(AutofillProfileSyncDifferenceTrackerTest,
        IncorporateRemoteProfileShouldOverwriteProfileWithSameKey) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin);
   local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
   AddAutofillProfilesToTable({local});
 
   // The remote profile is completely different but it has the same key.
-  AutofillProfile remote = AutofillProfile(kGuidA, kHttpsOrigin);
+  AutofillProfile remote = AutofillProfile(kSmallerGuid, kHttpsOrigin);
   remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
 
   IncorporateRemoteProfile(remote);
 
   // Nothing gets uploaded to sync and the remote profile wins.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, IsEmpty());
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(remote));
 }
 
 TEST_F(AutofillProfileSyncDifferenceTrackerTest,
        IncorporateRemoteProfileShouldOverwriteUnverifiedProfileByVerified) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpsOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpsOrigin);
   local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
   AddAutofillProfilesToTable({local});
 
   // The remote profile has the same key but it is not verified.
-  AutofillProfile remote = AutofillProfile(kGuidA, kSettingsOrigin);
+  AutofillProfile remote = AutofillProfile(kSmallerGuid, kSettingsOrigin);
   remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
 
   IncorporateRemoteProfile(remote);
 
   // Nothing gets uploaded to sync and the local profile wins.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, IsEmpty());
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(remote));
 }
 
 TEST_F(AutofillProfileSyncDifferenceTrackerTest,
        IncorporateRemoteProfileShouldNotOverwriteVerifiedProfileByUnverified) {
-  AutofillProfile local = AutofillProfile(kGuidA, kSettingsOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kSettingsOrigin);
   local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
   AddAutofillProfilesToTable({local});
 
   // The remote profile has the same key but it is not verified.
-  AutofillProfile remote = AutofillProfile(kGuidA, kHttpsOrigin);
+  AutofillProfile remote = AutofillProfile(kSmallerGuid, kHttpsOrigin);
   remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
 
   IncorporateRemoteProfile(remote);
 
   // Nothing gets uploaded to sync and the local profile wins.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, IsEmpty());
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(local));
 }
 
 TEST_F(AutofillProfileSyncDifferenceTrackerTest,
        IncorporateRemoteProfileShouldNotOverwriteFullNameByEmptyString) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin);
   local.SetRawInfo(NAME_FULL, ASCIIToUTF16("John"));
   AddAutofillProfilesToTable({local});
 
   // The remote profile has the same key.
-  AutofillProfile remote = AutofillProfile(kGuidA, kHttpsOrigin);
+  AutofillProfile remote = AutofillProfile(kSmallerGuid, kHttpsOrigin);
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2st st"));
 
   AutofillProfile merged(remote);
@@ -196,40 +209,45 @@
 
   // Nothing gets uploaded to sync and the remote profile wins except for the
   // full name.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, IsEmpty());
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(merged));
 }
 
-TEST_F(AutofillProfileSyncDifferenceTrackerTest,
-       IncorporateRemoteProfileShouldMergeIdenticalProfilesWithDifferentKeys) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+TEST_F(
+    AutofillProfileSyncDifferenceTrackerTest,
+    IncorporateRemoteProfileShouldKeepRemoteKeyWhenMergingDuplicateProfileWithBiggerKey) {
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin);
   local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
   local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   AddAutofillProfilesToTable({local});
 
   // The remote profile is identical to the local one, except that the guids and
   // origins are different.
-  AutofillProfile remote = AutofillProfile(kGuidB, kHttpsOrigin);
+  AutofillProfile remote = AutofillProfile(kBiggerGuid, kHttpsOrigin);
   remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
 
   IncorporateRemoteProfile(remote);
 
   // Nothing gets uploaded to sync and the remote profile wins.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, IsEmpty());
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(remote));
 }
 
 TEST_F(
     AutofillProfileSyncDifferenceTrackerTest,
-    IncorporateRemoteProfileShouldMergeIdenticalProfilesWithDifferentKeysButKeepVerifiedOrigin) {
-  AutofillProfile local = AutofillProfile(kGuidA, kSettingsOrigin);
+    IncorporateRemoteProfileShouldKeepRemoteKeyAndLocalOriginWhenMergingDuplicateProfileWithBiggerKey) {
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kSettingsOrigin);
   local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
   local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   AddAutofillProfilesToTable({local});
 
   // The remote profile has the same key.
-  AutofillProfile remote = AutofillProfile(kGuidB, kHttpsOrigin);
+  AutofillProfile remote = AutofillProfile(kBiggerGuid, kHttpsOrigin);
   remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
 
@@ -240,7 +258,60 @@
 
   // Nothing gets uploaded to sync and the remote profile wins except for the
   // full name.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(merged));
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, ElementsAre(merged));
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
+  EXPECT_THAT(GetAllLocalData(), ElementsAre(merged));
+}
+
+TEST_F(
+    AutofillProfileSyncDifferenceTrackerTest,
+    IncorporateRemoteProfileShouldKeepLocalKeyWhenMergingDuplicateProfileWithSmallerKey) {
+  AutofillProfile local = AutofillProfile(kBiggerGuid, kHttpOrigin);
+  local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+  local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+  AddAutofillProfilesToTable({local});
+
+  // The remote profile is identical to the local one, except that the guids and
+  // origins are different.
+  AutofillProfile remote = AutofillProfile(kSmallerGuid, kHttpsOrigin);
+  remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+  remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+
+  IncorporateRemoteProfile(remote);
+
+  // Nothing gets uploaded to sync and the remote profile wins.
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, IsEmpty());
+  EXPECT_THAT(updates.profiles_to_delete_from_sync,
+              ElementsAre(std::string(kSmallerGuid)));
+  EXPECT_THAT(GetAllLocalData(), ElementsAre(local));
+}
+
+TEST_F(
+    AutofillProfileSyncDifferenceTrackerTest,
+    IncorporateRemoteProfileShouldKeepLocalKeyAndRemoteOriginWhenMergingDuplicateProfileWithSmallerKey) {
+  AutofillProfile local = AutofillProfile(kBiggerGuid, kHttpsOrigin);
+  local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+  local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+  AddAutofillProfilesToTable({local});
+
+  // The remote profile has the same key.
+  AutofillProfile remote = AutofillProfile(kSmallerGuid, kSettingsOrigin);
+  remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+  remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+
+  AutofillProfile merged(local);
+  merged.set_origin(kSettingsOrigin);
+
+  IncorporateRemoteProfile(remote);
+
+  // Nothing gets uploaded to sync and the remote profile wins except for the
+  // full name.
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, ElementsAre(merged));
+  EXPECT_THAT(updates.profiles_to_delete_from_sync,
+              ElementsAre(std::string(kSmallerGuid)));
   EXPECT_THAT(GetAllLocalData(), ElementsAre(merged));
 }
 
@@ -255,10 +326,10 @@
 
 TEST_F(AutofillProfileSyncDifferenceTrackerTest,
        FlushToLocalShouldCallbackWhenProfileDeleted) {
-  AutofillProfile local = AutofillProfile(kGuidA, kSettingsOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kSettingsOrigin);
   AddAutofillProfilesToTable({local});
 
-  tracker()->IncorporateRemoteDelete(kGuidA);
+  tracker()->IncorporateRemoteDelete(kSmallerGuid);
 
   MockCallback<base::OnceClosure> autofill_changes_callback;
   EXPECT_CALL(autofill_changes_callback, Run()).Times(1);
@@ -271,7 +342,7 @@
 
 TEST_F(AutofillProfileSyncDifferenceTrackerTest,
        FlushToLocalShouldCallbackWhenProfileAdded) {
-  AutofillProfile remote = AutofillProfile(kGuidA, kSettingsOrigin);
+  AutofillProfile remote = AutofillProfile(kSmallerGuid, kSettingsOrigin);
   IncorporateRemoteProfile(remote);
 
   MockCallback<base::OnceClosure> autofill_changes_callback;
@@ -285,10 +356,10 @@
 
 TEST_F(AutofillProfileSyncDifferenceTrackerTest,
        FlushToLocalShouldCallbackWhenProfileUpdated) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpsOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpsOrigin);
   AddAutofillProfilesToTable({local});
 
-  AutofillProfile remote = AutofillProfile(kGuidA, kHttpsOrigin);
+  AutofillProfile remote = AutofillProfile(kSmallerGuid, kHttpsOrigin);
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   IncorporateRemoteProfile(remote);
 
@@ -297,7 +368,7 @@
   EXPECT_EQ(base::nullopt,
             tracker()->FlushToLocal(autofill_changes_callback.Get()));
 
-  // On top of that, the profile with key kGuidA should also get updated.
+  // On top of that, the profile with key kSmallerGuid should also get updated.
   EXPECT_THAT(GetAllLocalData(), ElementsAre(remote));
 }
 
@@ -324,13 +395,13 @@
 
 TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest,
        MergeSimilarEntriesForInitialSyncShouldSyncUpChanges) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin);
   local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   local.set_use_count(27);
   AddAutofillProfilesToTable({local});
 
   // The remote profile matches the local one (except for origin and use count).
-  AutofillProfile remote = AutofillProfile(kGuidB, kHttpsOrigin);
+  AutofillProfile remote = AutofillProfile(kBiggerGuid, kHttpsOrigin);
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
   remote.set_use_count(13);
@@ -346,19 +417,21 @@
   MergeSimilarEntriesForInitialSync();
 
   // The merged profile needs to get uploaded back to sync and stored locally.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(merged));
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, ElementsAre(merged));
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(merged));
 }
 
 TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest,
        MergeSimilarEntriesForInitialSyncShouldNotSyncUpWhenNotNeeded) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin);
   local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   local.set_use_count(13);
   AddAutofillProfilesToTable({local});
 
   // The remote profile matches the local one and has some additional data.
-  AutofillProfile remote = AutofillProfile(kGuidB, kHttpOrigin);
+  AutofillProfile remote = AutofillProfile(kBiggerGuid, kHttpOrigin);
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
   // Merging two profile takes their max use count, so use count of 27 is taken.
@@ -368,19 +441,21 @@
   MergeSimilarEntriesForInitialSync();
 
   // Nothing gets uploaded to sync and the remote profile wins.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, IsEmpty());
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(remote));
 }
 
 TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest,
        MergeSimilarEntriesForInitialSyncNotMatchNonsimilarEntries) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin);
   local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   local.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
   AddAutofillProfilesToTable({local});
 
   // The remote profile has a different street address.
-  AutofillProfile remote = AutofillProfile(kGuidB, kHttpOrigin);
+  AutofillProfile remote = AutofillProfile(kBiggerGuid, kHttpOrigin);
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2st st"));
   remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
 
@@ -389,19 +464,21 @@
 
   // The local profile gets uploaded (due to initial sync) and the remote
   // profile gets stored locally.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(local));
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, ElementsAre(local));
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(local, remote));
 }
 
 TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest,
        MergeSimilarEntriesForInitialSyncDoesNotMatchLocalVerifiedEntry) {
   // The local entry is verified, should not get merged.
-  AutofillProfile local = AutofillProfile(kGuidA, kSettingsOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kSettingsOrigin);
   local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   AddAutofillProfilesToTable({local});
 
   // The remote profile is similar to the local one.
-  AutofillProfile remote = AutofillProfile(kGuidB, kHttpOrigin);
+  AutofillProfile remote = AutofillProfile(kBiggerGuid, kHttpOrigin);
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
 
@@ -410,19 +487,21 @@
 
   // The local profile gets uploaded (due to initial sync) and the remote
   // profile gets stored locally.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(local));
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, ElementsAre(local));
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(local, remote));
 }
 
 TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest,
        MergeSimilarEntriesForInitialSyncDoesNotMatchRemoteVerifiedEntry) {
-  AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+  AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin);
   local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   AddAutofillProfilesToTable({local});
 
   // The remote profile is similar to the local one but is verified and thus it
   // should not get merged.
-  AutofillProfile remote = AutofillProfile(kGuidB, kSettingsOrigin);
+  AutofillProfile remote = AutofillProfile(kBiggerGuid, kSettingsOrigin);
   remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
   remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
 
@@ -431,7 +510,9 @@
 
   // The local profile gets uploaded (due to initial sync) and the remote
   // profile gets stored locally.
-  EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(local));
+  UpdatesToSync updates = FlushToSync();
+  EXPECT_THAT(updates.profiles_to_upload_to_sync, ElementsAre(local));
+  EXPECT_THAT(updates.profiles_to_delete_from_sync, IsEmpty());
   EXPECT_THAT(GetAllLocalData(), ElementsAre(local, remote));
 }
 
diff --git a/components/autofill/core/browser/webdata/autofill_profile_syncable_service.cc b/components/autofill/core/browser/webdata/autofill_profile_syncable_service.cc
deleted file mode 100644
index 440211df..0000000
--- a/components/autofill/core/browser/webdata/autofill_profile_syncable_service.cc
+++ /dev/null
@@ -1,741 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/webdata/autofill_profile_syncable_service.h"
-
-#include <stddef.h>
-
-#include <memory>
-#include <utility>
-
-#include "base/guid.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/data_model/autofill_profile_comparator.h"
-#include "components/autofill/core/browser/geo/autofill_country.h"
-// TODO(crbug.com/904390): Remove when the investigation is over.
-#include "components/autofill/core/browser/autofill_profile_sync_util.h"
-#include "components/autofill/core/browser/data_model/form_group.h"
-#include "components/autofill/core/browser/geo/country_names.h"
-#include "components/autofill/core/browser/webdata/autofill_table.h"
-#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
-#include "components/autofill/core/common/autofill_constants.h"
-#include "components/sync/model/sync_change_processor.h"
-#include "components/sync/model/sync_error.h"
-#include "components/sync/model/sync_error_factory.h"
-#include "components/sync/protocol/sync.pb.h"
-#include "components/webdata/common/web_database.h"
-
-using base::ASCIIToUTF16;
-using base::UTF8ToUTF16;
-using base::UTF16ToUTF8;
-
-namespace autofill {
-
-namespace {
-
-std::string LimitData(const std::string& data) {
-  std::string sanitized_value(data);
-  if (sanitized_value.length() > AutofillTable::kMaxDataLength)
-    sanitized_value.resize(AutofillTable::kMaxDataLength);
-  return sanitized_value;
-}
-
-void* AutofillProfileSyncableServiceUserDataKey() {
-  // Use the address of a static that COMDAT folding won't ever fold
-  // with something else.
-  static int user_data_key = 0;
-  return reinterpret_cast<void*>(&user_data_key);
-}
-
-}  // namespace
-
-const char kAutofillProfileTag[] = "google_chrome_autofill_profiles";
-
-AutofillProfileSyncableService::AutofillProfileSyncableService(
-    AutofillWebDataBackend* webdata_backend,
-    const std::string& app_locale)
-    : webdata_backend_(webdata_backend),
-      app_locale_(app_locale),
-      scoped_observer_(this) {
-  DCHECK(webdata_backend_);
-
-  scoped_observer_.Add(webdata_backend_);
-}
-
-AutofillProfileSyncableService::~AutofillProfileSyncableService() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-}
-
-// static
-void AutofillProfileSyncableService::CreateForWebDataServiceAndBackend(
-    AutofillWebDataService* web_data_service,
-    AutofillWebDataBackend* webdata_backend,
-    const std::string& app_locale) {
-  web_data_service->GetDBUserData()->SetUserData(
-      AutofillProfileSyncableServiceUserDataKey(),
-      std::make_unique<AutofillProfileSyncableService>(webdata_backend,
-                                                       app_locale));
-}
-
-// static
-AutofillProfileSyncableService*
-AutofillProfileSyncableService::FromWebDataService(
-    AutofillWebDataService* web_data_service) {
-  return static_cast<AutofillProfileSyncableService*>(
-      web_data_service->GetDBUserData()->GetUserData(
-          AutofillProfileSyncableServiceUserDataKey()));
-}
-
-AutofillProfileSyncableService::AutofillProfileSyncableService()
-    : webdata_backend_(nullptr), scoped_observer_(this) {}
-
-void AutofillProfileSyncableService::WaitUntilReadyToSync(
-    base::OnceClosure done) {
-  // Not used in the legacy directory-based architecture.
-  NOTREACHED();
-}
-
-syncer::SyncMergeResult
-AutofillProfileSyncableService::MergeDataAndStartSyncing(
-    syncer::ModelType type,
-    const syncer::SyncDataList& initial_sync_data,
-    std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
-    std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!sync_processor_);
-  DCHECK(sync_processor);
-  DCHECK(sync_error_factory);
-  DVLOG(1) << "Associating Autofill: MergeDataAndStartSyncing";
-
-  syncer::SyncMergeResult merge_result(type);
-  sync_error_factory_ = std::move(sync_error_factory);
-  if (!LoadAutofillData(&profiles_)) {
-    merge_result.set_error(sync_error_factory_->CreateAndUploadError(
-        FROM_HERE, "Could not get the autofill data from WebDatabase."));
-    return merge_result;
-  }
-
-  if (DLOG_IS_ON(INFO)) {
-    DVLOG(2) << "[AUTOFILL MIGRATION]"
-             << "Printing profiles from web db";
-
-    for (const auto& p : profiles_) {
-      DVLOG(2) << "[AUTOFILL MIGRATION]  "
-               << UTF16ToUTF8(p->GetRawInfo(NAME_FIRST))
-               << UTF16ToUTF8(p->GetRawInfo(NAME_LAST))
-               << p->guid();
-    }
-  }
-
-  sync_processor_ = std::move(sync_processor);
-
-  GUIDToProfileMap remaining_local_profiles;
-  CreateGUIDToProfileMap(profiles_, &remaining_local_profiles);
-  DataBundle bundle;
-  // For every incoming profile from sync, attempt to update a local profile or
-  // otherwise create a new one.
-  for (const auto& sync_iter : initial_sync_data) {
-    auto it =
-        CreateOrUpdateProfile(sync_iter, &remaining_local_profiles, &bundle);
-    // |it| points to created/updated profile. Add it to the |profiles_map_| and
-    // then remove it from |remaining_local_profiles|. After this loop is
-    // completed |remaining_local_profiles| will have only those profiles that
-    // are not in the sync.
-    profiles_map_[it->first] = it->second;
-    // This may be a no-op since |it| is sometimes an entirely new profile that
-    // came from sync.
-    remaining_local_profiles.erase(it);
-  }
-
-  // Check for similar unmatched profiles - they are created independently on
-  // two systems, so merge them.
-  for (const auto& sync_profile_it : bundle.candidates_to_merge) {
-    auto profile_to_merge =
-        remaining_local_profiles.find(sync_profile_it.first);
-    if (profile_to_merge != remaining_local_profiles.end()) {
-      bundle.profiles_to_delete.push_back(profile_to_merge->second->guid());
-      // For similar profile pairs, the local profile is always removed and its
-      // content merged (if applicable) in the profile that came from sync.
-      if (MergeSimilarProfiles(*(profile_to_merge->second),
-                               sync_profile_it.second, app_locale_)) {
-        // if new changes were merged into |sync_profile_it.second| from
-        // |profile_to_merge|, they will be synced back.
-        bundle.profiles_to_sync_back.push_back(sync_profile_it.second);
-      }
-      DVLOG(2) << "[AUTOFILL SYNC]"
-               << "Found similar profile in sync db but with a "
-                  "different guid: "
-               << UTF16ToUTF8(sync_profile_it.second->GetRawInfo(NAME_FIRST))
-               << UTF16ToUTF8(sync_profile_it.second->GetRawInfo(NAME_LAST))
-               << "New guid " << sync_profile_it.second->guid()
-               << ". Profile to be deleted "
-               << profile_to_merge->second->guid();
-      remaining_local_profiles.erase(profile_to_merge);
-    }
-  }
-
-  if (!SaveChangesToWebData(bundle)) {
-    merge_result.set_error(sync_error_factory_->CreateAndUploadError(
-        FROM_HERE,
-        "Failed to update webdata."));
-    return merge_result;
-  }
-
-  syncer::SyncChangeList new_changes;
-  for (const auto& it : remaining_local_profiles) {
-    new_changes.push_back(
-        syncer::SyncChange(FROM_HERE,
-                           syncer::SyncChange::ACTION_ADD,
-                           CreateData(*(it.second))));
-
-    // TODO(crbug.com/904390): Remove when the investigation is over.
-    ReportAutofillProfileAddOrUpdateOrigin(
-        AutofillProfileSyncChangeOrigin::kInitial);
-    profiles_map_[it.first] = it.second;
-  }
-
-  for (size_t i = 0; i < bundle.profiles_to_sync_back.size(); ++i) {
-    new_changes.push_back(
-        syncer::SyncChange(FROM_HERE,
-                           syncer::SyncChange::ACTION_UPDATE,
-                           CreateData(*(bundle.profiles_to_sync_back[i]))));
-
-    // TODO(crbug.com/904390): Remove when the investigation is over.
-    ReportAutofillProfileAddOrUpdateOrigin(
-        AutofillProfileSyncChangeOrigin::kInitial);
-  }
-
-  if (!new_changes.empty()) {
-    merge_result.set_error(
-        sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes));
-  }
-
-  if (webdata_backend_) {
-    webdata_backend_->NotifyOfMultipleAutofillChanges();
-    webdata_backend_->NotifyThatSyncHasStarted(type);
-  }
-
-  return merge_result;
-}
-
-void AutofillProfileSyncableService::StopSyncing(syncer::ModelType type) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK_EQ(type, syncer::AUTOFILL_PROFILE);
-
-  sync_processor_.reset();
-  sync_error_factory_.reset();
-  profiles_.clear();
-  profiles_map_.clear();
-}
-
-syncer::SyncDataList AutofillProfileSyncableService::GetAllSyncData(
-    syncer::ModelType type) const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(sync_processor_);
-  DCHECK_EQ(type, syncer::AUTOFILL_PROFILE);
-
-  syncer::SyncDataList current_data;
-  for (const auto& it : profiles_map_)
-    current_data.push_back(CreateData(*(it.second)));
-  return current_data;
-}
-
-syncer::SyncError AutofillProfileSyncableService::ProcessSyncChanges(
-    const base::Location& from_here,
-    const syncer::SyncChangeList& change_list) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!sync_processor_) {
-    syncer::SyncError error(FROM_HERE,
-                            syncer::SyncError::DATATYPE_ERROR,
-                            "Models not yet associated.",
-                            syncer::AUTOFILL_PROFILE);
-    return error;
-  }
-
-  DataBundle bundle;
-
-  for (const auto& it : change_list) {
-    DCHECK(it.IsValid());
-    switch (it.change_type()) {
-      case syncer::SyncChange::ACTION_ADD:
-      case syncer::SyncChange::ACTION_UPDATE:
-        CreateOrUpdateProfile(it.sync_data(), &profiles_map_, &bundle);
-        break;
-      case syncer::SyncChange::ACTION_DELETE: {
-        std::string guid = it.sync_data().GetSpecifics().
-             autofill_profile().guid();
-        bundle.profiles_to_delete.push_back(guid);
-        profiles_map_.erase(guid);
-      } break;
-      default:
-        NOTREACHED() << "Unexpected sync change state.";
-        return sync_error_factory_->CreateAndUploadError(
-              FROM_HERE,
-              "ProcessSyncChanges failed on ChangeType " +
-                  syncer::SyncChange::ChangeTypeToString(it.change_type()));
-    }
-  }
-
-  if (!SaveChangesToWebData(bundle)) {
-    return sync_error_factory_->CreateAndUploadError(
-        FROM_HERE,
-        "Failed to update webdata.");
-  }
-
-  if (webdata_backend_)
-    webdata_backend_->NotifyOfMultipleAutofillChanges();
-
-  return syncer::SyncError();
-}
-
-void AutofillProfileSyncableService::AutofillProfileChanged(
-    const AutofillProfileChange& change) {
-  // Check if sync is on. If we receive notification prior to the sync being set
-  // up we are going to process all when MergeData..() is called. If we receive
-  // notification after the sync exited, it will be sinced next time Chrome
-  // starts.
-  if (sync_processor_) {
-    ActOnChange(change);
-  } else if (!flare_.is_null()) {
-    flare_.Run(syncer::AUTOFILL_PROFILE);
-    flare_.Reset();
-  }
-}
-
-bool AutofillProfileSyncableService::LoadAutofillData(
-    std::vector<std::unique_ptr<AutofillProfile>>* profiles) {
-  return GetAutofillTable()->GetAutofillProfiles(profiles);
-}
-
-bool AutofillProfileSyncableService::SaveChangesToWebData(
-    const DataBundle& bundle) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  AutofillTable* autofill_table = GetAutofillTable();
-
-  bool success = true;
-  for (size_t i = 0; i< bundle.profiles_to_delete.size(); ++i) {
-    if (!autofill_table->RemoveAutofillProfile(bundle.profiles_to_delete[i]))
-      success = false;
-  }
-
-  for (size_t i = 0; i < bundle.profiles_to_add.size(); i++) {
-    if (!autofill_table->AddAutofillProfile(*bundle.profiles_to_add[i]))
-      success = false;
-  }
-
-  for (size_t i = 0; i < bundle.profiles_to_update.size(); i++) {
-    if (!autofill_table->UpdateAutofillProfile(*bundle.profiles_to_update[i]))
-      success = false;
-  }
-  return success;
-}
-
-// static
-bool AutofillProfileSyncableService::OverwriteProfileWithServerData(
-    const sync_pb::AutofillProfileSpecifics& specifics,
-    AutofillProfile* profile) {
-  bool diff = false;
-  if (specifics.has_origin() && profile->origin() != specifics.origin()) {
-    bool was_verified = profile->IsVerified();
-    // In this case, the local origin must be empty on the local |profile|, but
-    // the remote profile was verified.
-    if (specifics.origin() == kSettingsOrigin)
-      profile->set_origin(kSettingsOrigin);
-    diff = true;
-
-    // Verified profiles should never be overwritten by unverified ones.
-    DCHECK(!was_verified || profile->IsVerified());
-  }
-
-  // Update name, email, and phone fields.
-  diff = UpdateField(NAME_FIRST,
-                     specifics.name_first_size() ? specifics.name_first(0)
-                                                 : std::string(),
-                     profile) || diff;
-  diff = UpdateField(NAME_MIDDLE,
-                     specifics.name_middle_size() ? specifics.name_middle(0)
-                                                  : std::string(),
-                     profile) || diff;
-  diff =
-      UpdateField(NAME_LAST, specifics.name_last_size() ? specifics.name_last(0)
-                                                        : std::string(),
-                  profile) || diff;
-  // Older versions don't have a separate full name; don't overwrite full name
-  // in this case.
-  if (specifics.name_full_size() > 0) {
-    diff = UpdateField(NAME_FULL,
-                       specifics.name_full_size() ? specifics.name_full(0)
-                                                  : std::string(),
-                       profile) || diff;
-  }
-  diff = UpdateField(EMAIL_ADDRESS,
-                     specifics.email_address_size() ? specifics.email_address(0)
-                                                    : std::string(),
-                     profile) || diff;
-  diff = UpdateField(PHONE_HOME_WHOLE_NUMBER,
-                     specifics.phone_home_whole_number_size()
-                         ? specifics.phone_home_whole_number(0)
-                         : std::string(),
-                     profile) || diff;
-
-  // Update all simple single-valued address fields.
-  diff = UpdateField(COMPANY_NAME, specifics.company_name(), profile) || diff;
-  diff = UpdateField(ADDRESS_HOME_CITY,
-                     specifics.address_home_city(), profile) || diff;
-  diff = UpdateField(ADDRESS_HOME_STATE,
-                     specifics.address_home_state(), profile) || diff;
-  diff = UpdateField(ADDRESS_HOME_ZIP,
-                     specifics.address_home_zip(), profile) || diff;
-  diff = UpdateField(ADDRESS_HOME_SORTING_CODE,
-                     specifics.address_home_sorting_code(), profile) || diff;
-  diff = UpdateField(ADDRESS_HOME_DEPENDENT_LOCALITY,
-                     specifics.address_home_dependent_locality(),
-                     profile) || diff;
-
-  // Update the country field, which can contain either a country code (if set
-  // by a newer version of Chrome), or a country name (if set by an older
-  // version of Chrome).
-  base::string16 country_name_or_code =
-      ASCIIToUTF16(specifics.address_home_country());
-  std::string country_code =
-      CountryNames::GetInstance()->GetCountryCode(country_name_or_code);
-  diff = UpdateField(ADDRESS_HOME_COUNTRY, country_code, profile) || diff;
-
-  // Update the street address.  In newer versions of Chrome (M34+), this data
-  // is stored in the |address_home_street_address| field.  In older versions,
-  // this data is stored separated out by address line.
-  if (specifics.has_address_home_street_address()) {
-    diff = UpdateField(ADDRESS_HOME_STREET_ADDRESS,
-                       specifics.address_home_street_address(),
-                       profile) || diff;
-  } else {
-    diff = UpdateField(ADDRESS_HOME_LINE1,
-                       specifics.address_home_line1(), profile) || diff;
-    diff = UpdateField(ADDRESS_HOME_LINE2,
-                       specifics.address_home_line2(), profile) || diff;
-  }
-
-  // Update the BCP 47 language code that can be used to format the address for
-  // display.
-  if (specifics.has_address_home_language_code() &&
-      specifics.address_home_language_code() != profile->language_code()) {
-    profile->set_language_code(specifics.address_home_language_code());
-    diff = true;
-  }
-
-  // Update the validity state bitfield.
-  if (specifics.has_validity_state_bitfield() &&
-      specifics.validity_state_bitfield() !=
-          profile->GetClientValidityBitfieldValue()) {
-    profile->SetClientValidityFromBitfieldValue(
-        specifics.validity_state_bitfield());
-    diff = true;
-  }
-
-  if (static_cast<size_t>(specifics.use_count()) != profile->use_count()) {
-    profile->set_use_count(specifics.use_count());
-    diff = true;
-  }
-
-  if (specifics.use_date() != profile->use_date().ToTimeT()) {
-    profile->set_use_date(base::Time::FromTimeT(specifics.use_date()));
-    diff = true;
-  }
-
-  if (specifics.is_client_validity_states_updated() !=
-      profile->is_client_validity_states_updated()) {
-    profile->set_is_client_validity_states_updated(
-        specifics.is_client_validity_states_updated());
-    diff = true;
-  }
-  return diff;
-}
-
-// static
-void AutofillProfileSyncableService::WriteAutofillProfile(
-    const AutofillProfile& profile,
-    sync_pb::EntitySpecifics* profile_specifics) {
-  sync_pb::AutofillProfileSpecifics* specifics =
-      profile_specifics->mutable_autofill_profile();
-
-  DCHECK(base::IsValidGUID(profile.guid()));
-
-  // Reset all multi-valued fields in the protobuf.
-  specifics->clear_name_first();
-  specifics->clear_name_middle();
-  specifics->clear_name_last();
-  specifics->clear_name_full();
-  specifics->clear_email_address();
-  specifics->clear_phone_home_whole_number();
-
-  specifics->set_guid(profile.guid());
-  specifics->set_origin(profile.origin());
-  specifics->set_use_count(profile.use_count());
-  specifics->set_use_date(profile.use_date().ToTimeT());
-
-  // TODO(estade): this should be set_name_first.
-  specifics->add_name_first(
-      LimitData(UTF16ToUTF8(profile.GetRawInfo(NAME_FIRST))));
-  specifics->add_name_middle(
-      LimitData(UTF16ToUTF8(profile.GetRawInfo(NAME_MIDDLE))));
-  specifics->add_name_last(
-      LimitData(UTF16ToUTF8(profile.GetRawInfo(NAME_LAST))));
-  specifics->add_name_full(
-      LimitData(UTF16ToUTF8(profile.GetRawInfo(NAME_FULL))));
-  specifics->set_address_home_line1(
-      LimitData(UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_LINE1))));
-  specifics->set_address_home_line2(
-      LimitData(UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_LINE2))));
-  specifics->set_address_home_city(
-      LimitData(UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_CITY))));
-  specifics->set_address_home_state(
-      LimitData(UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_STATE))));
-  specifics->set_address_home_zip(
-      LimitData(UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_ZIP))));
-  specifics->set_address_home_country(
-      LimitData(UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_COUNTRY))));
-  specifics->set_address_home_street_address(
-      LimitData(UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_STREET_ADDRESS))));
-  specifics->set_address_home_sorting_code(
-      LimitData(UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_SORTING_CODE))));
-  specifics->set_address_home_dependent_locality(
-      LimitData(
-          UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY))));
-  specifics->set_address_home_language_code(LimitData(profile.language_code()));
-  specifics->set_validity_state_bitfield(
-      profile.GetClientValidityBitfieldValue());
-  specifics->set_is_client_validity_states_updated(
-      profile.is_client_validity_states_updated());
-
-  // TODO(estade): this should be set_email_address.
-  specifics->add_email_address(
-      LimitData(UTF16ToUTF8(profile.GetRawInfo(EMAIL_ADDRESS))));
-
-  specifics->set_company_name(
-      LimitData(UTF16ToUTF8(profile.GetRawInfo(COMPANY_NAME))));
-
-  // TODO(estade): this should be set_phone_home_whole_number.
-  specifics->add_phone_home_whole_number(
-      LimitData(UTF16ToUTF8(profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER))));
-}
-
-void AutofillProfileSyncableService::CreateGUIDToProfileMap(
-    const std::vector<std::unique_ptr<AutofillProfile>>& profiles,
-    GUIDToProfileMap* profile_map) {
-  DCHECK(profile_map);
-  profile_map->clear();
-  for (const auto& profile : profiles)
-    (*profile_map)[profile->guid()] = profile.get();
-}
-
-AutofillProfileSyncableService::GUIDToProfileMap::iterator
-AutofillProfileSyncableService::CreateOrUpdateProfile(
-    const syncer::SyncData& data,
-    GUIDToProfileMap* profile_map,
-    DataBundle* bundle) {
-  DCHECK(profile_map);
-  DCHECK(bundle);
-
-  DCHECK_EQ(syncer::AUTOFILL_PROFILE, data.GetDataType());
-
-  const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
-  const sync_pb::AutofillProfileSpecifics& autofill_specifics(
-      specifics.autofill_profile());
-
-  auto existing_profile = profile_map->find(autofill_specifics.guid());
-  if (existing_profile != profile_map->end()) {
-    // The synced profile already exists locally.  It might need to be updated.
-    if (OverwriteProfileWithServerData(autofill_specifics,
-                                       existing_profile->second)) {
-      bundle->profiles_to_update.push_back(existing_profile->second);
-    }
-    return existing_profile;
-  }
-
-  // New profile synced.
-  std::unique_ptr<AutofillProfile> new_profile =
-      std::make_unique<AutofillProfile>(autofill_specifics.guid(),
-                                        autofill_specifics.origin());
-  AutofillProfile* new_profile_ptr = new_profile.get();
-  OverwriteProfileWithServerData(autofill_specifics, new_profile_ptr);
-
-  // Check if profile appears under a different guid. Compares only profile
-  // contents. (Ignores origin and language code in comparison.)
-  //
-  // Unverified profiles should never overwrite verified ones.
-  AutofillProfileComparator comparator(app_locale_);
-  for (auto it = profile_map->begin(); it != profile_map->end(); ++it) {
-    AutofillProfile* local_profile = it->second;
-    if (local_profile->Compare(*new_profile) == 0) {
-      // Ensure that a verified profile can never revert back to an unverified
-      // one.
-      if (local_profile->IsVerified() && !new_profile->IsVerified()) {
-        new_profile->set_origin(local_profile->origin());
-        bundle->profiles_to_sync_back.push_back(new_profile.get());
-      }
-
-      bundle->profiles_to_delete.push_back(local_profile->guid());
-      DVLOG(2) << "[AUTOFILL SYNC]"
-               << "Found in sync db but with a different guid: "
-               << UTF16ToUTF8(local_profile->GetRawInfo(NAME_FIRST))
-               << UTF16ToUTF8(local_profile->GetRawInfo(NAME_LAST))
-               << "New guid " << new_profile->guid()
-               << ". Profile to be deleted " << local_profile->guid();
-      profile_map->erase(it);
-      break;
-    }
-    if (!local_profile->IsVerified() && !new_profile->IsVerified() &&
-        comparator.AreMergeable(*local_profile, *new_profile)) {
-      // Add it to candidates for merge - if there is no profile with this guid
-      // we will merge them.
-      bundle->candidates_to_merge.insert(
-          std::make_pair(local_profile->guid(), new_profile_ptr));
-      break;
-    }
-  }
-  profiles_.push_back(std::move(new_profile));
-  bundle->profiles_to_add.push_back(new_profile_ptr);
-  return profile_map
-      ->insert(std::make_pair(new_profile_ptr->guid(), new_profile_ptr))
-      .first;
-}
-
-void AutofillProfileSyncableService::ActOnChange(
-     const AutofillProfileChange& change) {
-  DCHECK(change.data_model());
-  DCHECK(sync_processor_);
-
-  if (change.data_model()->record_type() != AutofillProfile::LOCAL_PROFILE) {
-    return;
-  }
-
-  // TODO(crbug.com/904390): Remove when the investigation is over.
-  bool is_converted_from_server = false;
-  // |webdata_backend_|, used by GetAutofillTable() may be null in unit-tests.
-  if (webdata_backend_ != nullptr) {
-    std::vector<std::unique_ptr<AutofillProfile>> server_profiles;
-    GetAutofillTable()->GetServerProfiles(&server_profiles);
-    is_converted_from_server = IsLocalProfileEqualToServerProfile(
-        server_profiles, *change.data_model(), app_locale_);
-  }
-
-  syncer::SyncChangeList new_changes;
-  DataBundle bundle;
-  switch (change.type()) {
-    case AutofillProfileChange::ADD:
-      new_changes.push_back(
-          syncer::SyncChange(FROM_HERE,
-                             syncer::SyncChange::ACTION_ADD,
-                             CreateData(*(change.data_model()))));
-      DCHECK(profiles_map_.find(change.data_model()->guid()) ==
-             profiles_map_.end());
-      profiles_.push_back(
-          std::make_unique<AutofillProfile>(*(change.data_model())));
-      profiles_map_[change.data_model()->guid()] = profiles_.back().get();
-
-      // TODO(crbug.com/904390): Remove when the investigation is over.
-      ReportAutofillProfileAddOrUpdateOrigin(
-          is_converted_from_server
-              ? AutofillProfileSyncChangeOrigin::kConvertedLocal
-              : AutofillProfileSyncChangeOrigin::kTrulyLocal);
-      break;
-    case AutofillProfileChange::UPDATE: {
-      auto it = profiles_map_.find(change.data_model()->guid());
-      DCHECK(it != profiles_map_.end());
-      *(it->second) = *(change.data_model());
-      new_changes.push_back(
-          syncer::SyncChange(FROM_HERE,
-                             syncer::SyncChange::ACTION_UPDATE,
-                             CreateData(*(change.data_model()))));
-
-      // TODO(crbug.com/904390): Remove when the investigation is over.
-      ReportAutofillProfileAddOrUpdateOrigin(
-          is_converted_from_server
-              ? AutofillProfileSyncChangeOrigin::kConvertedLocal
-              : AutofillProfileSyncChangeOrigin::kTrulyLocal);
-      break;
-    }
-    case AutofillProfileChange::REMOVE: {
-      if (profiles_map_.find(change.key()) != profiles_map_.end()) {
-        new_changes.push_back(
-            syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_DELETE,
-                               CreateData(*(change.data_model()))));
-        profiles_map_.erase(change.key());
-        // TODO(crbug.com/904390): Remove when the investigation is over.
-        ReportAutofillProfileDeleteOrigin(
-            is_converted_from_server
-                ? AutofillProfileSyncChangeOrigin::kConvertedLocal
-                : AutofillProfileSyncChangeOrigin::kTrulyLocal);
-      }
-      break;
-    }
-    default:
-      NOTREACHED();
-  }
-  syncer::SyncError error =
-      sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
-  if (error.IsSet()) {
-    // TODO(isherman): Investigating http://crbug.com/121592
-    VLOG(1) << "[AUTOFILL SYNC] "
-            << "Failed processing change:\n"
-            << "  Error: " << error.message() << "\n"
-            << "  Guid: " << change.key();
-  }
-}
-
-void AutofillProfileSyncableService::set_sync_processor(
-    syncer::SyncChangeProcessor* sync_processor) {
-  sync_processor_.reset(sync_processor);
-}
-
-syncer::SyncData AutofillProfileSyncableService::CreateData(
-    const AutofillProfile& profile) {
-  sync_pb::EntitySpecifics specifics;
-  WriteAutofillProfile(profile, &specifics);
-  return
-      syncer::SyncData::CreateLocalData(
-          profile.guid(), profile.guid(), specifics);
-}
-
-bool AutofillProfileSyncableService::UpdateField(
-    ServerFieldType field_type,
-    const std::string& new_value,
-    AutofillProfile* autofill_profile) {
-  if (UTF16ToUTF8(autofill_profile->GetRawInfo(field_type)) == new_value)
-    return false;
-  autofill_profile->SetRawInfo(field_type, UTF8ToUTF16(new_value));
-  return true;
-}
-
-bool AutofillProfileSyncableService::MergeSimilarProfiles(
-    const AutofillProfile& merge_from,
-    AutofillProfile* merge_into,
-    const std::string& app_locale) {
-  const AutofillProfile old_merge_into = *merge_into;
-  merge_into->MergeDataFrom(merge_from, app_locale);
-  return !merge_into->EqualsForSyncPurposes(old_merge_into);
-}
-
-AutofillTable* AutofillProfileSyncableService::GetAutofillTable() const {
-  return AutofillTable::FromWebDatabase(webdata_backend_->GetDatabase());
-}
-
-void AutofillProfileSyncableService::InjectStartSyncFlare(
-    const syncer::SyncableService::StartSyncFlare& flare) {
-  flare_ = flare;
-}
-
-AutofillProfileSyncableService::DataBundle::DataBundle() {}
-
-AutofillProfileSyncableService::DataBundle::DataBundle(
-    const DataBundle& other) = default;
-
-AutofillProfileSyncableService::DataBundle::~DataBundle() {}
-
-}  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/autofill_profile_syncable_service.h b/components/autofill/core/browser/webdata/autofill_profile_syncable_service.h
deleted file mode 100644
index d963810..0000000
--- a/components/autofill/core/browser/webdata/autofill_profile_syncable_service.h
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_SYNCABLE_SERVICE_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_SYNCABLE_SERVICE_H_
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/scoped_observer.h"
-#include "base/sequence_checker.h"
-#include "base/supports_user_data.h"
-#include "base/synchronization/lock.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/webdata/autofill_change.h"
-#include "components/autofill/core/browser/webdata/autofill_entry.h"
-#include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
-#include "components/autofill/core/browser/webdata/autofill_webdata_service_observer.h"
-#include "components/sync/model/sync_change.h"
-#include "components/sync/model/sync_data.h"
-#include "components/sync/model/sync_error.h"
-#include "components/sync/model/syncable_service.h"
-#include "components/sync/protocol/autofill_specifics.pb.h"
-
-namespace browser_sync {
-class ProfileSyncServiceAutofillTest;
-}  // namespace browser_sync
-
-namespace autofill {
-
-class AutofillProfile;
-class AutofillTable;
-class AutofillWebDataService;
-
-extern const char kAutofillProfileTag[];
-
-// The sync implementation for local AutofillProfiles, which can be managed in
-// settings and can be written to the sync server. (Server profiles cannot be
-// managed in settings and can only be read from the sync server.)
-//
-// MergeDataAndStartSyncing() called first, it does cloud->local and
-// local->cloud syncs. Then for each cloud change we receive
-// ProcessSyncChanges() and for each local change Observe() is called.
-class AutofillProfileSyncableService
-    : public base::SupportsUserData::Data,
-      public syncer::SyncableService,
-      public AutofillWebDataServiceObserverOnDBSequence {
- public:
-  AutofillProfileSyncableService(AutofillWebDataBackend* webdata_backend,
-                                 const std::string& app_locale);
-
-  ~AutofillProfileSyncableService() override;
-
-  // Creates a new AutofillProfileSyncableService and hangs it off of
-  // |web_data_service|, which takes ownership. This method should only be
-  // called on |web_data_service|'s DB sequence.
-  static void CreateForWebDataServiceAndBackend(
-      AutofillWebDataService* web_data_service,
-      AutofillWebDataBackend* webdata_backend,
-      const std::string& app_locale);
-
-  // Retrieves the AutofillProfileSyncableService stored on |web_data_service|.
-  static AutofillProfileSyncableService* FromWebDataService(
-      AutofillWebDataService* web_data_service);
-
-  static syncer::ModelType model_type() { return syncer::AUTOFILL_PROFILE; }
-
-  // syncer::SyncableService implementation.
-  void WaitUntilReadyToSync(base::OnceClosure done) override;
-  syncer::SyncMergeResult MergeDataAndStartSyncing(
-      syncer::ModelType type,
-      const syncer::SyncDataList& initial_sync_data,
-      std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
-      std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory) override;
-  void StopSyncing(syncer::ModelType type) override;
-  syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const override;
-  syncer::SyncError ProcessSyncChanges(
-      const base::Location& from_here,
-      const syncer::SyncChangeList& change_list) override;
-
-  // AutofillWebDataServiceObserverOnDBSequence implementation.
-  void AutofillProfileChanged(const AutofillProfileChange& change) override;
-
-  // Provides a StartSyncFlare to the SyncableService. See
-  // sync_start_util for more.
-  void InjectStartSyncFlare(
-      const syncer::SyncableService::StartSyncFlare& flare);
-
- protected:
-  // A convenience wrapper of a bunch of state we pass around while
-  // associating models, and send to the WebDatabase for persistence.
-  // We do this so we hold the write lock for only a small period.
-  // When storing the web db we are out of the write lock.
-  struct DataBundle;
-
-  // Helper to query WebDatabase for the current autofill state.
-  // Made virtual for ease of mocking in unit tests.
-  virtual bool LoadAutofillData(
-      std::vector<std::unique_ptr<AutofillProfile>>* profiles);
-
-  // Helper to persist any changes that occured during model association to
-  // the WebDatabase.
-  // Made virtual for ease of mocking in unit tests.
-  virtual bool SaveChangesToWebData(const DataBundle& bundle);
-
-  // For unit tests.
-  AutofillProfileSyncableService();
-  void set_sync_processor(syncer::SyncChangeProcessor* sync_processor);
-
-  // Creates syncer::SyncData based on supplied |profile|.
-  // Exposed for unit tests.
-  static syncer::SyncData CreateData(const AutofillProfile& profile);
-
- private:
-  friend class browser_sync::ProfileSyncServiceAutofillTest;
-  FRIEND_TEST_ALL_PREFIXES(AutofillProfileSyncableServiceTest,
-                           UpdateField);
-  FRIEND_TEST_ALL_PREFIXES(AutofillProfileSyncableServiceTest,
-                           MergeSimilarProfiles_AdditionalInfoInBothProfiles);
-  FRIEND_TEST_ALL_PREFIXES(AutofillProfileSyncableServiceTest,
-                           MergeSimilarProfiles_DifferentUseDates);
-  FRIEND_TEST_ALL_PREFIXES(AutofillProfileSyncableServiceTest,
-                           MergeSimilarProfiles_DifferentNames);
-  FRIEND_TEST_ALL_PREFIXES(AutofillProfileSyncableServiceTest,
-                           MergeSimilarProfiles_NonZeroUseCounts);
-  FRIEND_TEST_ALL_PREFIXES(AutofillProfileSyncableServiceTest,
-                           OverwriteProfileWithServerData_NonSettingsOrigin);
-  FRIEND_TEST_ALL_PREFIXES(AutofillProfileSyncableServiceTest,
-                           OverwriteProfileWithServerData_SettingsOrigin);
-
-  // The map of the guid to profiles owned by the |profiles_| vector.
-  typedef std::map<std::string, AutofillProfile*> GUIDToProfileMap;
-
-  // Helper function that overwrites |profile| with data from proto-buffer
-  // |specifics|.
-  static bool OverwriteProfileWithServerData(
-      const sync_pb::AutofillProfileSpecifics& specifics,
-      AutofillProfile* profile);
-
-  // Writes |profile| data into supplied |profile_specifics|.
-  static void WriteAutofillProfile(const AutofillProfile& profile,
-                                   sync_pb::EntitySpecifics* profile_specifics);
-
-  // Creates |profile_map| from the supplied |profiles| vector. Necessary for
-  // fast processing of the changes.
-  void CreateGUIDToProfileMap(
-      const std::vector<std::unique_ptr<AutofillProfile>>& profiles,
-      GUIDToProfileMap* profile_map);
-
-  // Creates or updates a profile based on |data|. Looks at the guid of the data
-  // and if a profile with such guid is present in |profile_map| updates it. If
-  // not, searches through it for similar profiles. If similar profile is
-  // found substitutes it for the new one, otherwise adds a new profile. Returns
-  // iterator pointing to added/updated profile.
-  GUIDToProfileMap::iterator CreateOrUpdateProfile(
-      const syncer::SyncData& data,
-      GUIDToProfileMap* profile_map,
-      DataBundle* bundle);
-
-  // Syncs |change| to the cloud.
-  void ActOnChange(const AutofillProfileChange& change);
-
-  AutofillTable* GetAutofillTable() const;
-
-  // Helper to compare the local value and cloud value of a field, copy into
-  // the local value if they differ, and return whether the change happened.
-  static bool UpdateField(ServerFieldType field_type,
-                          const std::string& new_value,
-                          AutofillProfile* autofill_profile);
-
-  // Calls merge_into->OverwriteWith() and then checks if the
-  // |merge_into| has extra data. Returns true if the merge has made a change to
-  // |merge_into|. This should be used only for similar profiles ie. profiles
-  // where the PrimaryValue() matches.
-  static bool MergeSimilarProfiles(const AutofillProfile& merge_from,
-                                   AutofillProfile* merge_into,
-                                   const std::string& app_locale);
-
-  AutofillWebDataBackend* webdata_backend_;  // WEAK
-  std::string app_locale_;
-  ScopedObserver<AutofillWebDataBackend,
-                 AutofillProfileSyncableService> scoped_observer_;
-
-  // Cached Autofill profiles. *Warning* deleted profiles are still in the
-  // vector - use the |profiles_map_| to iterate through actual profiles.
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_;
-  GUIDToProfileMap profiles_map_;
-
-  std::unique_ptr<syncer::SyncChangeProcessor> sync_processor_;
-
-  std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory_;
-
-  syncer::SyncableService::StartSyncFlare flare_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncableService);
-};
-
-// This object is used in unit tests as well, so it defined here.
-struct AutofillProfileSyncableService::DataBundle {
-  DataBundle();
-  DataBundle(const DataBundle& other);
-  ~DataBundle();
-
-  std::vector<std::string> profiles_to_delete;
-  std::vector<AutofillProfile*> profiles_to_update;
-  std::vector<AutofillProfile*> profiles_to_add;
-
-  // When we go through sync we find profiles that are similar but unmatched.
-  // Merge such profiles.
-  GUIDToProfileMap candidates_to_merge;
-  // Profiles that have multi-valued fields that are not in sync.
-  std::vector<AutofillProfile*> profiles_to_sync_back;
-};
-
-}  // namespace autofill
-
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_SYNCABLE_SERVICE_H_
diff --git a/components/autofill/core/browser/webdata/autofill_profile_syncable_service_unittest.cc b/components/autofill/core/browser/webdata/autofill_profile_syncable_service_unittest.cc
deleted file mode 100644
index 62b8b563..0000000
--- a/components/autofill/core/browser/webdata/autofill_profile_syncable_service_unittest.cc
+++ /dev/null
@@ -1,1726 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/webdata/autofill_profile_syncable_service.h"
-
-#include <stddef.h>
-
-#include <memory>
-#include <utility>
-
-#include "base/location.h"
-#include "base/memory/ptr_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_task_environment.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/geo/country_names.h"
-#include "components/autofill/core/browser/webdata/autofill_change.h"
-#include "components/autofill/core/common/autofill_constants.h"
-#include "components/sync/model/sync_change_processor.h"
-#include "components/sync/model/sync_error_factory.h"
-#include "components/sync/model/sync_error_factory_mock.h"
-#include "components/sync/protocol/sync.pb.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace autofill {
-
-using ::testing::_;
-using ::testing::DoAll;
-using ::testing::Eq;
-using ::testing::Return;
-using ::testing::Property;
-using base::ASCIIToUTF16;
-
-namespace {
-
-// Some guids for testing.
-const char kGuid1[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44B";
-const char kGuid2[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44C";
-const char kGuid3[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44D";
-const char kGuid4[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44E";
-const char kEmptyOrigin[] = "";
-const int kValidityStateBitfield = 1984;
-
-class MockAutofillProfileSyncableService
-    : public AutofillProfileSyncableService {
- public:
-  MockAutofillProfileSyncableService() {}
-  ~MockAutofillProfileSyncableService() override {}
-
-  using AutofillProfileSyncableService::DataBundle;
-  using AutofillProfileSyncableService::set_sync_processor;
-  using AutofillProfileSyncableService::CreateData;
-
-  MOCK_METHOD1(LoadAutofillData,
-               bool(std::vector<std::unique_ptr<AutofillProfile>>*));
-  MOCK_METHOD1(SaveChangesToWebData,
-               bool(const AutofillProfileSyncableService::DataBundle&));
-};
-
-ACTION_P(LoadAutofillProfiles, datafunc) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles =
-      std::move(datafunc());
-  arg0->swap(profiles);
-}
-
-MATCHER_P(CheckSyncChanges, n_sync_changes_list, "") {
-  if (arg.size() != n_sync_changes_list.size())
-    return false;
-  syncer::SyncChangeList::const_iterator passed, expected;
-  for (passed = arg.begin(), expected = n_sync_changes_list.begin();
-       passed != arg.end() && expected != n_sync_changes_list.end();
-       ++passed, ++expected) {
-    DCHECK(passed->IsValid());
-    if (passed->change_type() != expected->change_type())
-      return false;
-    if (passed->sync_data().GetSpecifics().SerializeAsString() !=
-            expected->sync_data().GetSpecifics().SerializeAsString()) {
-      return false;
-    }
-  }
-  return true;
-}
-
-MATCHER_P(DataBundleCheck, n_bundle, "") {
-  if ((arg.profiles_to_delete.size() != n_bundle.profiles_to_delete.size()) ||
-      (arg.profiles_to_update.size() != n_bundle.profiles_to_update.size()) ||
-      (arg.profiles_to_add.size() != n_bundle.profiles_to_add.size()))
-    return false;
-  for (size_t i = 0; i < arg.profiles_to_delete.size(); ++i) {
-    if (arg.profiles_to_delete[i] != n_bundle.profiles_to_delete[i])
-      return false;
-  }
-  for (size_t i = 0; i < arg.profiles_to_update.size(); ++i) {
-    if (*arg.profiles_to_update[i] != *n_bundle.profiles_to_update[i])
-      return false;
-  }
-  for (size_t i = 0; i < arg.profiles_to_add.size(); ++i) {
-    if (*arg.profiles_to_add[i] != *n_bundle.profiles_to_add[i])
-      return false;
-  }
-  return true;
-}
-
-class MockSyncChangeProcessor : public syncer::SyncChangeProcessor {
- public:
-  MockSyncChangeProcessor() {}
-  ~MockSyncChangeProcessor() override {}
-
-  MOCK_METHOD2(ProcessSyncChanges,
-               syncer::SyncError(const base::Location&,
-                                 const syncer::SyncChangeList&));
-  syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const override {
-    return syncer::SyncDataList();
-  }
-};
-
-class TestSyncChangeProcessor : public syncer::SyncChangeProcessor {
- public:
-  TestSyncChangeProcessor() {}
-  ~TestSyncChangeProcessor() override {}
-
-  syncer::SyncError ProcessSyncChanges(
-      const base::Location& location,
-      const syncer::SyncChangeList& changes) override {
-    changes_ = changes;
-    return syncer::SyncError();
-  }
-
-  syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const override {
-    return syncer::SyncDataList();
-  }
-
-  const syncer::SyncChangeList& changes() { return changes_; }
-
- private:
-  syncer::SyncChangeList changes_;
-};
-
-// Returns a profile with all fields set.  Contains identical data to the data
-// returned from ConstructCompleteSyncData().
-std::unique_ptr<AutofillProfile> ConstructCompleteProfile() {
-  std::unique_ptr<AutofillProfile> profile(
-      new AutofillProfile(kGuid1, kSettingsOrigin));
-
-  profile->set_use_count(7);
-  profile->set_use_date(base::Time::FromTimeT(1423182152));
-
-  profile->SetRawInfo(NAME_FULL, ASCIIToUTF16("John K. Doe, Jr."));
-  profile->SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  profile->SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("K."));
-  profile->SetRawInfo(NAME_LAST, ASCIIToUTF16("Doe"));
-
-  profile->SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("user@example.com"));
-  profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("1.800.555.1234"));
-
-  profile->SetRawInfo(ADDRESS_HOME_STREET_ADDRESS,
-                      ASCIIToUTF16("123 Fake St.\n"
-                                   "Apt. 42"));
-  EXPECT_EQ(ASCIIToUTF16("123 Fake St."),
-            profile->GetRawInfo(ADDRESS_HOME_LINE1));
-  EXPECT_EQ(ASCIIToUTF16("Apt. 42"), profile->GetRawInfo(ADDRESS_HOME_LINE2));
-
-  profile->SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Google, Inc."));
-  profile->SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Mountain View"));
-  profile->SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("California"));
-  profile->SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("94043"));
-  profile->SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
-  profile->SetRawInfo(ADDRESS_HOME_SORTING_CODE, ASCIIToUTF16("CEDEX"));
-  profile->SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY,
-                      ASCIIToUTF16("Santa Clara"));
-  profile->set_language_code("en");
-  profile->SetClientValidityFromBitfieldValue(kValidityStateBitfield);
-  profile->set_is_client_validity_states_updated(true);
-  return profile;
-}
-
-// Returns SyncData with all Autofill profile fields set.  Contains identical
-// data to the data returned from ConstructCompleteProfile().
-syncer::SyncData ConstructCompleteSyncData() {
-  sync_pb::EntitySpecifics entity_specifics;
-  sync_pb::AutofillProfileSpecifics* specifics =
-      entity_specifics.mutable_autofill_profile();
-
-  specifics->set_guid(kGuid1);
-  specifics->set_origin(kSettingsOrigin);
-  specifics->set_use_count(7);
-  specifics->set_use_date(1423182152);
-
-  specifics->add_name_first("John");
-  specifics->add_name_middle("K.");
-  specifics->add_name_last("Doe");
-  specifics->add_name_full("John K. Doe, Jr.");
-
-  specifics->add_email_address("user@example.com");
-
-  specifics->add_phone_home_whole_number("1.800.555.1234");
-
-  specifics->set_address_home_line1("123 Fake St.");
-  specifics->set_address_home_line2("Apt. 42");
-  specifics->set_address_home_street_address("123 Fake St.\n"
-                                             "Apt. 42");
-
-  specifics->set_company_name("Google, Inc.");
-  specifics->set_address_home_city("Mountain View");
-  specifics->set_address_home_state("California");
-  specifics->set_address_home_zip("94043");
-  specifics->set_address_home_country("US");
-  specifics->set_address_home_sorting_code("CEDEX");
-  specifics->set_address_home_dependent_locality("Santa Clara");
-  specifics->set_address_home_language_code("en");
-  specifics->set_validity_state_bitfield(kValidityStateBitfield);
-  specifics->set_is_client_validity_states_updated(true);
-
-  return syncer::SyncData::CreateLocalData(kGuid1, kGuid1, entity_specifics);
-}
-
-}  // namespace
-
-class AutofillProfileSyncableServiceTest : public testing::Test {
- public:
-  AutofillProfileSyncableServiceTest() {
-    CountryNames::SetLocaleString("en-US");
-  }
-
-  void SetUp() override { sync_processor_.reset(new MockSyncChangeProcessor); }
-
-  // Wrapper around AutofillProfileSyncableService::MergeDataAndStartSyncing()
-  // that also verifies expectations.
-  void MergeDataAndStartSyncing(
-      std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db,
-      const syncer::SyncDataList& data_list,
-      const MockAutofillProfileSyncableService::DataBundle& expected_bundle,
-      const syncer::SyncChangeList& expected_change_list) {
-    auto profile_returner = [&profiles_from_web_db]() {
-      return std::move(profiles_from_web_db);
-    };
-    EXPECT_CALL(autofill_syncable_service_, LoadAutofillData(_))
-        .Times(1)
-        .WillOnce(DoAll(LoadAutofillProfiles(profile_returner), Return(true)));
-    EXPECT_CALL(autofill_syncable_service_,
-                SaveChangesToWebData(DataBundleCheck(expected_bundle)))
-        .Times(1)
-        .WillOnce(Return(true));
-    if (expected_change_list.empty()) {
-      EXPECT_CALL(*sync_processor_, ProcessSyncChanges(_, _)).Times(0);
-    } else {
-      ON_CALL(*sync_processor_, ProcessSyncChanges(_, _))
-          .WillByDefault(Return(syncer::SyncError()));
-      EXPECT_CALL(*sync_processor_,
-                  ProcessSyncChanges(_, CheckSyncChanges(expected_change_list)))
-          .Times(1)
-          .WillOnce(Return(syncer::SyncError()));
-    }
-
-    // Takes ownership of sync_processor_.
-    autofill_syncable_service_.MergeDataAndStartSyncing(
-        syncer::AUTOFILL_PROFILE, data_list, std::move(sync_processor_),
-        std::unique_ptr<syncer::SyncErrorFactory>(
-            new syncer::SyncErrorFactoryMock()));
-  }
-
- protected:
-  base::test::ScopedTaskEnvironment task_environment_;
-  MockAutofillProfileSyncableService autofill_syncable_service_;
-  std::unique_ptr<MockSyncChangeProcessor> sync_processor_;
-};
-
-TEST_F(AutofillProfileSyncableServiceTest, MergeDataAndStartSyncing) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-  std::string guid_present1 = kGuid1;
-  std::string guid_present2 = kGuid2;
-  std::string guid_synced1 = kGuid3;
-  std::string guid_synced2 = kGuid4;
-  std::string origin_present1 = kEmptyOrigin;
-  std::string origin_present2 = kEmptyOrigin;
-  std::string origin_synced1 = kEmptyOrigin;
-  std::string origin_synced2 = kSettingsOrigin;
-
-  profiles_from_web_db.push_back(
-      std::make_unique<AutofillProfile>(guid_present1, origin_present1));
-  profiles_from_web_db.back()->SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  profiles_from_web_db.back()->SetRawInfo(ADDRESS_HOME_LINE1,
-                                          ASCIIToUTF16("1 1st st"));
-  profiles_from_web_db.push_back(
-      std::make_unique<AutofillProfile>(guid_present2, origin_present2));
-  profiles_from_web_db.back()->SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
-  profiles_from_web_db.back()->SetRawInfo(ADDRESS_HOME_LINE1,
-                                          ASCIIToUTF16("2 2nd st"));
-
-  syncer::SyncDataList data_list;
-  AutofillProfile profile1(guid_synced1, origin_synced1);
-  profile1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane"));
-  data_list.push_back(autofill_syncable_service_.CreateData(profile1));
-  AutofillProfile profile2(guid_synced2, origin_synced2);
-  profile2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Harry"));
-  data_list.push_back(autofill_syncable_service_.CreateData(profile2));
-  // This one will have the name and origin updated.
-  AutofillProfile profile3(guid_present2, origin_synced2);
-  profile3.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom Doe"));
-  data_list.push_back(autofill_syncable_service_.CreateData(profile3));
-
-  syncer::SyncChangeList expected_change_list;
-  expected_change_list.push_back(
-      syncer::SyncChange(FROM_HERE,
-                         syncer::SyncChange::ACTION_ADD,
-                         MockAutofillProfileSyncableService::CreateData(
-                             *profiles_from_web_db.front())));
-
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  expected_bundle.profiles_to_add.push_back(&profile1);
-  expected_bundle.profiles_to_add.push_back(&profile2);
-  expected_bundle.profiles_to_update.push_back(&profile3);
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-TEST_F(AutofillProfileSyncableServiceTest, MergeIdenticalProfiles) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-  std::string guid_present1 = kGuid1;
-  std::string guid_present2 = kGuid2;
-  std::string guid_synced1 = kGuid3;
-  std::string guid_synced2 = kGuid4;
-  std::string origin_present1 = kEmptyOrigin;
-  std::string origin_present2 = kSettingsOrigin;
-  std::string origin_synced1 = kEmptyOrigin;
-  std::string origin_synced2 = kEmptyOrigin;
-
-  profiles_from_web_db.push_back(
-      std::make_unique<AutofillProfile>(guid_present1, origin_present1));
-  profiles_from_web_db.back()->SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  profiles_from_web_db.back()->SetRawInfo(ADDRESS_HOME_LINE1,
-                                          ASCIIToUTF16("1 1st st"));
-  profiles_from_web_db.push_back(
-      std::make_unique<AutofillProfile>(guid_present2, origin_present2));
-  profiles_from_web_db.back()->SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
-  profiles_from_web_db.back()->SetRawInfo(ADDRESS_HOME_LINE1,
-                                          ASCIIToUTF16("2 2nd st"));
-
-  // The synced profiles are identical to the local ones, except that the guids
-  // are different.
-  syncer::SyncDataList data_list;
-  AutofillProfile profile1(guid_synced1, origin_synced1);
-  profile1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  profile1.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1 1st st"));
-  data_list.push_back(autofill_syncable_service_.CreateData(profile1));
-  AutofillProfile profile2(guid_synced2, origin_synced2);
-  profile2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
-  profile2.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("2 2nd st"));
-  data_list.push_back(autofill_syncable_service_.CreateData(profile2));
-
-  AutofillProfile expected_profile(profile2);
-  expected_profile.set_origin(kSettingsOrigin);
-  syncer::SyncChangeList expected_change_list;
-  expected_change_list.push_back(
-      syncer::SyncChange(FROM_HERE,
-                         syncer::SyncChange::ACTION_UPDATE,
-                         MockAutofillProfileSyncableService::CreateData(
-                             expected_profile)));
-
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  expected_bundle.profiles_to_delete.push_back(guid_present1);
-  expected_bundle.profiles_to_delete.push_back(guid_present2);
-  expected_bundle.profiles_to_add.push_back(&profile1);
-  expected_bundle.profiles_to_add.push_back(&expected_profile);
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-TEST_F(AutofillProfileSyncableServiceTest, MergeSimilarProfiles) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-  std::string guid_present1 = kGuid1;
-  std::string guid_present2 = kGuid2;
-  std::string guid_synced1 = kGuid3;
-  std::string guid_synced2 = kGuid4;
-  std::string origin_present1 = kEmptyOrigin;
-  std::string origin_present2 = kSettingsOrigin;
-  std::string origin_synced1 = kEmptyOrigin;
-  std::string origin_synced2 = kEmptyOrigin;
-
-  profiles_from_web_db.push_back(
-      std::make_unique<AutofillProfile>(guid_present1, origin_present1));
-  profiles_from_web_db.back()->SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  profiles_from_web_db.back()->SetRawInfo(ADDRESS_HOME_LINE1,
-                                          ASCIIToUTF16("1 1st st"));
-  profiles_from_web_db.back()->set_use_count(27);
-  profiles_from_web_db.push_back(
-      std::make_unique<AutofillProfile>(guid_present2, origin_present2));
-  profiles_from_web_db.back()->SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
-  profiles_from_web_db.back()->SetRawInfo(ADDRESS_HOME_LINE1,
-                                          ASCIIToUTF16("2 2nd st"));
-
-  // The synced profiles are identical to the local ones, except that the guids
-  // and use_count values are different.
-  syncer::SyncDataList data_list;
-  AutofillProfile profile1(guid_synced1, origin_synced1);
-  profile1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  profile1.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1 1st st"));
-  profile1.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
-  profile1.set_use_count(13);
-  data_list.push_back(autofill_syncable_service_.CreateData(profile1));
-  AutofillProfile profile2(guid_synced2, origin_synced2);
-  profile2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
-  profile2.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("2 2nd st"));
-  profile2.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Fizzbang, LLC."));
-  profile1.set_use_count(4);
-  data_list.push_back(autofill_syncable_service_.CreateData(profile2));
-
-  // The first profile should have its origin updated.
-  // The second profile should remain as-is, because an unverified profile
-  // should never overwrite a verified one.
-  AutofillProfile expected_profile(profile1);
-  expected_profile.set_origin(origin_present1);
-  expected_profile.SetRawInfo(NAME_FULL, ASCIIToUTF16("John"));
-  // Merging two profile takes their max use count.
-  expected_profile.set_use_count(27);
-  syncer::SyncChangeList expected_change_list;
-  expected_change_list.push_back(
-      syncer::SyncChange(FROM_HERE,
-                         syncer::SyncChange::ACTION_ADD,
-                         MockAutofillProfileSyncableService::CreateData(
-                             *profiles_from_web_db.back())));
-  expected_change_list.push_back(
-      syncer::SyncChange(FROM_HERE,
-                         syncer::SyncChange::ACTION_UPDATE,
-                         MockAutofillProfileSyncableService::CreateData(
-                             expected_profile)));
-
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  expected_bundle.profiles_to_delete.push_back(guid_present1);
-  expected_bundle.profiles_to_add.push_back(&expected_profile);
-  expected_bundle.profiles_to_add.push_back(&profile2);
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Ensure that no Sync events are generated to fill in missing origins from Sync
-// with explicitly present empty ones.  This ensures that the migration to add
-// origins to profiles does not generate lots of needless Sync updates.
-TEST_F(AutofillProfileSyncableServiceTest, MergeDataEmptyOrigins) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  // Create a profile with an empty origin.
-  AutofillProfile profile(kGuid1, std::string());
-  profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1 1st st"));
-
-  profiles_from_web_db.push_back(std::make_unique<AutofillProfile>(profile));
-
-  // Create a Sync profile identical to |profile|, except with no origin set.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->add_name_first("John");
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  autofill_specifics->set_address_home_line1("1 1st st");
-  autofill_specifics->set_use_count(profile.use_count());
-  autofill_specifics->set_use_date(profile.use_date().ToTimeT());
-  EXPECT_FALSE(autofill_specifics->has_origin());
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(
-      syncer::SyncData::CreateLocalData(
-          profile.guid(), profile.guid(), specifics));
-
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  syncer::SyncChangeList expected_change_list;
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-TEST_F(AutofillProfileSyncableServiceTest, GetAllSyncData) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-  std::string guid_present1 = kGuid1;
-  std::string guid_present2 = kGuid2;
-
-  profiles_from_web_db.push_back(
-      std::make_unique<AutofillProfile>(guid_present1, kEmptyOrigin));
-  profiles_from_web_db.back()->SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  profiles_from_web_db.push_back(
-      std::make_unique<AutofillProfile>(guid_present2, kEmptyOrigin));
-  profiles_from_web_db.back()->SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane"));
-
-  syncer::SyncChangeList expected_change_list;
-  expected_change_list.push_back(
-      syncer::SyncChange(FROM_HERE,
-                         syncer::SyncChange::ACTION_ADD,
-                         MockAutofillProfileSyncableService::CreateData(
-                             *profiles_from_web_db.front())));
-  expected_change_list.push_back(
-      syncer::SyncChange(FROM_HERE,
-                         syncer::SyncChange::ACTION_ADD,
-                         MockAutofillProfileSyncableService::CreateData(
-                             *profiles_from_web_db.back())));
-
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  syncer::SyncDataList data_list;
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_change_list);
-
-  syncer::SyncDataList data =
-      autofill_syncable_service_.GetAllSyncData(syncer::AUTOFILL_PROFILE);
-
-  ASSERT_EQ(2U, data.size());
-  EXPECT_EQ(guid_present1, data[0].GetSpecifics().autofill_profile().guid());
-  EXPECT_EQ(guid_present2, data[1].GetSpecifics().autofill_profile().guid());
-  EXPECT_EQ(kEmptyOrigin, data[0].GetSpecifics().autofill_profile().origin());
-  EXPECT_EQ(kEmptyOrigin, data[1].GetSpecifics().autofill_profile().origin());
-
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-TEST_F(AutofillProfileSyncableServiceTest, ProcessSyncChanges) {
-  std::vector<AutofillProfile *> profiles_from_web_db;
-  std::string guid_present = kGuid1;
-  std::string guid_synced = kGuid2;
-
-  syncer::SyncChangeList change_list;
-  AutofillProfile profile(guid_synced, kEmptyOrigin);
-  profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane"));
-  change_list.push_back(
-      syncer::SyncChange(
-          FROM_HERE,
-          syncer::SyncChange::ACTION_ADD,
-          MockAutofillProfileSyncableService::CreateData(profile)));
-  AutofillProfile empty_profile(guid_present, kEmptyOrigin);
-  change_list.push_back(
-      syncer::SyncChange(
-          FROM_HERE,
-          syncer::SyncChange::ACTION_DELETE,
-          MockAutofillProfileSyncableService::CreateData(empty_profile)));
-
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  expected_bundle.profiles_to_delete.push_back(guid_present);
-  expected_bundle.profiles_to_add.push_back(&profile);
-
-  EXPECT_CALL(autofill_syncable_service_, SaveChangesToWebData(
-              DataBundleCheck(expected_bundle)))
-      .Times(1)
-      .WillOnce(Return(true));
-
-  autofill_syncable_service_.set_sync_processor(sync_processor_.release());
-  syncer::SyncError error = autofill_syncable_service_.ProcessSyncChanges(
-      FROM_HERE, change_list);
-
-  EXPECT_FALSE(error.IsSet());
-}
-
-TEST_F(AutofillProfileSyncableServiceTest, AutofillProfileAdded) {
-  // Will be owned by the syncable service.  Keep a reference available here for
-  // verifying test expectations.
-  TestSyncChangeProcessor* sync_change_processor = new TestSyncChangeProcessor;
-  autofill_syncable_service_.set_sync_processor(sync_change_processor);
-
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane"));
-  AutofillProfileChange change(AutofillProfileChange::ADD, kGuid1, &profile);
-  autofill_syncable_service_.AutofillProfileChanged(change);
-
-  ASSERT_EQ(1U, sync_change_processor->changes().size());
-  syncer::SyncChange result = sync_change_processor->changes()[0];
-  EXPECT_EQ(syncer::SyncChange::ACTION_ADD, result.change_type());
-
-  sync_pb::AutofillProfileSpecifics specifics =
-      result.sync_data().GetSpecifics().autofill_profile();
-  EXPECT_EQ(kGuid1, specifics.guid());
-  EXPECT_EQ(kEmptyOrigin, specifics.origin());
-  EXPECT_THAT(specifics.name_first(), testing::ElementsAre("Jane"));
-}
-
-TEST_F(AutofillProfileSyncableServiceTest, AutofillProfileDeleted) {
-  // Will be owned by the syncable service.  Keep a reference available here for
-  // verifying test expectations.
-  TestSyncChangeProcessor* sync_change_processor = new TestSyncChangeProcessor;
-  autofill_syncable_service_.set_sync_processor(sync_change_processor);
-
-  // First add the profile so we have something to delete.
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane"));
-  AutofillProfileChange change1(AutofillProfileChange::ADD, kGuid1, &profile);
-  autofill_syncable_service_.AutofillProfileChanged(change1);
-
-  AutofillProfileChange change2(AutofillProfileChange::REMOVE, kGuid1,
-                                &profile);
-  autofill_syncable_service_.AutofillProfileChanged(change2);
-
-  ASSERT_EQ(1U, sync_change_processor->changes().size());
-  syncer::SyncChange result = sync_change_processor->changes()[0];
-  EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, result.change_type());
-  sync_pb::AutofillProfileSpecifics specifics =
-      result.sync_data().GetSpecifics().autofill_profile();
-  EXPECT_EQ(kGuid1, specifics.guid());
-}
-
-TEST_F(AutofillProfileSyncableServiceTest,
-       AutofillProfileDeletedIgnoresUnknown) {
-  // Will be owned by the syncable service.  Keep a reference available here for
-  // verifying test expectations.
-  TestSyncChangeProcessor* sync_change_processor = new TestSyncChangeProcessor;
-  autofill_syncable_service_.set_sync_processor(sync_change_processor);
-
-  AutofillProfile profile(kGuid2, kEmptyOrigin);
-  profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane"));
-  AutofillProfileChange change(AutofillProfileChange::REMOVE, kGuid2, &profile);
-  autofill_syncable_service_.AutofillProfileChanged(change);
-
-  ASSERT_EQ(0U, sync_change_processor->changes().size());
-}
-
-TEST_F(AutofillProfileSyncableServiceTest, UpdateField) {
-  AutofillProfile profile(kGuid1, kSettingsOrigin);
-  std::string company1 = "A Company";
-  std::string company2 = "Another Company";
-  profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16(company1));
-  EXPECT_FALSE(AutofillProfileSyncableService::UpdateField(
-      COMPANY_NAME, company1, &profile));
-  EXPECT_EQ(profile.GetRawInfo(COMPANY_NAME), ASCIIToUTF16(company1));
-  EXPECT_TRUE(AutofillProfileSyncableService::UpdateField(
-      COMPANY_NAME, company2, &profile));
-  EXPECT_EQ(profile.GetRawInfo(COMPANY_NAME), ASCIIToUTF16(company2));
-  EXPECT_FALSE(AutofillProfileSyncableService::UpdateField(
-      COMPANY_NAME, company2, &profile));
-  EXPECT_EQ(profile.GetRawInfo(COMPANY_NAME), ASCIIToUTF16(company2));
-}
-
-// Tests that MergeSimilarProfiles adds the additional information of
-// |from_profile| into |into_profile| but not the other way around.
-TEST_F(AutofillProfileSyncableServiceTest,
-       MergeSimilarProfiles_AdditionalInfoInBothProfiles) {
-  AutofillProfile into_profile(kGuid1, kEmptyOrigin);
-  into_profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("111 First St."));
-
-  AutofillProfile from_profile(kGuid2, kEmptyOrigin);
-  from_profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("111 First St."));
-
-  from_profile.set_use_count(0);
-  into_profile.set_use_count(0);
-
-  into_profile.set_use_date(base::Time::FromTimeT(1234));
-  from_profile.set_use_date(base::Time::FromTimeT(1234));
-
-  into_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  from_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-
-  into_profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Doe"));
-  from_profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Doe"));
-
-  from_profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("650234567"));
-
-  into_profile.set_language_code("en");
-
-  // Expect true because the phone number and origin of |from_profile| were
-  // saved in |into_profile|.
-  EXPECT_TRUE(AutofillProfileSyncableService::MergeSimilarProfiles(
-      from_profile, &into_profile, "en-US"));
-  EXPECT_EQ(ASCIIToUTF16("650234567"),
-            into_profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
-  EXPECT_EQ(kEmptyOrigin, into_profile.origin());
-
-  // Make sure that the language code of |into_profile| was not added to
-  // |from_profile|.
-  EXPECT_EQ("", from_profile.language_code());
-}
-
-// Tests that MergeSimilarProfiles keeps the most recent use date of the two
-// profiles being merged.
-TEST_F(AutofillProfileSyncableServiceTest,
-       MergeSimilarProfiles_DifferentUseDates) {
-  // Different guids, same origin.
-  AutofillProfile into_profile(kGuid1, kEmptyOrigin);
-  AutofillProfile from_profile(kGuid2, kEmptyOrigin);
-
-  from_profile.set_use_count(0);
-  into_profile.set_use_count(0);
-
-  // |from_profile| has a more recent use date.
-  from_profile.set_use_date(base::Time::FromTimeT(30));
-  into_profile.set_use_date(base::Time::FromTimeT(25));
-
-  // Expect true because the use date of |from_profile| replaced the use date of
-  // |into_profile|.
-  EXPECT_TRUE(AutofillProfileSyncableService::MergeSimilarProfiles(
-      from_profile, &into_profile, "en-US"));
-  EXPECT_EQ(base::Time::FromTimeT(30), into_profile.use_date());
-
-  // |into_profile| has a more recent use date.
-  into_profile.set_use_date(base::Time::FromTimeT(35));
-
-  // Expect false because |from_profile| was not updated in any way by
-  // |into_profile|.
-  EXPECT_FALSE(AutofillProfileSyncableService::MergeSimilarProfiles(
-      from_profile, &into_profile, "en-US"));
-  EXPECT_EQ(base::Time::FromTimeT(35), into_profile.use_date());
-}
-
-// Tests that MergeSimilarProfiles saves the max of the use counts of the two
-// profiles in |into_profile|.
-TEST_F(AutofillProfileSyncableServiceTest,
-       MergeSimilarProfiles_NonZeroUseCounts) {
-  // Different guids, same origin, same use date.
-  AutofillProfile into_profile(kGuid1, kEmptyOrigin);
-  AutofillProfile from_profile(kGuid2, kEmptyOrigin);
-  from_profile.set_use_date(base::Time::FromTimeT(1234));
-  into_profile.set_use_date(base::Time::FromTimeT(1234));
-
-  from_profile.set_use_count(12);
-  into_profile.set_use_count(5);
-
-  // Expect true because the use count of |from_profile| was added to the use
-  // count of |into_profile|.
-  EXPECT_TRUE(AutofillProfileSyncableService::MergeSimilarProfiles(
-      from_profile, &into_profile, "en-US"));
-  EXPECT_EQ(12U, into_profile.use_count());
-}
-
-// Ensure that all profile fields are able to be synced up from the client to
-// the server.
-TEST_F(AutofillProfileSyncableServiceTest, SyncAllFieldsToServer) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  // Create a profile with all fields set.
-  profiles_from_web_db.push_back(ConstructCompleteProfile());
-
-  // Set up expectations: No changes to the WebDB, and all fields correctly
-  // copied to Sync.
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  syncer::SyncChangeList expected_change_list;
-  expected_change_list.push_back(
-      syncer::SyncChange(FROM_HERE,
-                         syncer::SyncChange::ACTION_ADD,
-                         ConstructCompleteSyncData()));
-
-  // Verify the expectations.
-  syncer::SyncDataList data_list;
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Ensure that all profile fields are able to be synced down from the server to
-// the client.
-TEST_F(AutofillProfileSyncableServiceTest, SyncAllFieldsToClient) {
-  // Create a profile with all fields set.
-  syncer::SyncDataList data_list;
-  data_list.push_back(ConstructCompleteSyncData());
-
-  // Set up expectations: All fields correctly copied to the WebDB, and no
-  // changes propagated to Sync.
-  syncer::SyncChangeList expected_change_list;
-  std::unique_ptr<AutofillProfile> expected_profile =
-      ConstructCompleteProfile();
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  expected_bundle.profiles_to_add.push_back(expected_profile.get());
-
-  // Verify the expectations.
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Ensure that the street address field takes precedence over the address line 1
-// and line 2 fields, even though these are expected to always be in sync in
-// practice.
-TEST_F(AutofillProfileSyncableServiceTest,
-       StreetAddressTakesPrecedenceOverAddressLines) {
-  // Create a Sync profile with conflicting address data in the street address
-  // field vs. the address line 1 and address line 2 fields.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(kGuid1);
-  autofill_specifics->set_origin(kEmptyOrigin);
-  autofill_specifics->add_name_first(std::string());
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  autofill_specifics->set_address_home_line1("123 Example St.");
-  autofill_specifics->set_address_home_line2("Apt. 42");
-  autofill_specifics->set_address_home_street_address("456 El Camino Real\n"
-                                                      "Suite #1337");
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(
-      syncer::SyncData::CreateLocalData(kGuid1, kGuid1, specifics));
-
-  // Set up expectations: Full street address takes precedence over address
-  // lines.
-  syncer::SyncChangeList expected_change_list;
-  AutofillProfile expected_profile(kGuid1, kEmptyOrigin);
-  expected_profile.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS,
-                              ASCIIToUTF16("456 El Camino Real\n"
-                                           "Suite #1337"));
-  EXPECT_EQ(ASCIIToUTF16("456 El Camino Real"),
-            expected_profile.GetRawInfo(ADDRESS_HOME_LINE1));
-  EXPECT_EQ(ASCIIToUTF16("Suite #1337"),
-            expected_profile.GetRawInfo(ADDRESS_HOME_LINE2));
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  expected_bundle.profiles_to_add.push_back(&expected_profile);
-
-  // Verify the expectations.
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Ensure that no Sync events are generated to fill in missing street address
-// fields from Sync with explicitly present ones identical to the data stored in
-// the line1 and line2 fields.  This ensures that the migration to add the
-// street address field to profiles does not generate lots of needless Sync
-// updates.
-TEST_F(AutofillProfileSyncableServiceTest, MergeDataEmptyStreetAddress) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  // Create a profile with the street address set.
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  profile.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS,
-                     ASCIIToUTF16("123 Example St.\n"
-                                  "Apt. 42"));
-  EXPECT_EQ(ASCIIToUTF16("123 Example St."),
-            profile.GetRawInfo(ADDRESS_HOME_LINE1));
-  EXPECT_EQ(ASCIIToUTF16("Apt. 42"), profile.GetRawInfo(ADDRESS_HOME_LINE2));
-
-  profiles_from_web_db.push_back(std::make_unique<AutofillProfile>(profile));
-
-  // Create a Sync profile identical to |profile|, except without street address
-  // explicitly set.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->set_origin(profile.origin());
-  autofill_specifics->add_name_first(std::string());
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  autofill_specifics->set_address_home_line1("123 Example St.");
-  autofill_specifics->set_address_home_line2("Apt. 42");
-  autofill_specifics->set_use_count(profile.use_count());
-  autofill_specifics->set_use_date(profile.use_date().ToTimeT());
-  EXPECT_FALSE(autofill_specifics->has_address_home_street_address());
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(
-      syncer::SyncData::CreateLocalData(
-          profile.guid(), profile.guid(), specifics));
-
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  syncer::SyncChangeList expected_change_list;
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Sync data without origin should not overwrite existing origin in local
-// autofill profile.
-TEST_F(AutofillProfileSyncableServiceTest, EmptySyncPreservesOrigin) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  // Local autofill profile has an origin.
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  profiles_from_web_db.push_back(std::make_unique<AutofillProfile>(profile));
-
-  // Remote data does not have an origin value.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->add_name_first("John");
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  EXPECT_FALSE(autofill_specifics->has_origin());
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(
-      syncer::SyncData::CreateLocalData(
-          profile.guid(), profile.guid(), specifics));
-
-  // Expect the local autofill profile to still have an origin after sync.
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  AutofillProfile expected_profile(profile.guid(), profile.origin());
-  expected_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  expected_bundle.profiles_to_update.push_back(&expected_profile);
-
-  // Expect no sync events to add origin to the remote data.
-  syncer::SyncChangeList expected_empty_change_list;
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_empty_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Sync data without origin should not overwrite existing origin in local
-// autofill profile.
-TEST_F(AutofillProfileSyncableServiceTest,
-       NonSettingsOriginFromSyncIsIgnored_Merge) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  // Remote data has no origin value.
-  AutofillProfile profile(kGuid1, std::string());
-  profiles_from_web_db.push_back(std::make_unique<AutofillProfile>(profile));
-
-  // Remote data has a non-settings origin value.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->set_origin("www.example.com");
-  autofill_specifics->add_name_first("John");
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(syncer::SyncData::CreateLocalData(
-      profile.guid(), profile.guid(), specifics));
-
-  // Expect the local autofill profile to still have an origin after sync.
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  AutofillProfile expected_profile(profile.guid(), profile.origin());
-  expected_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  expected_bundle.profiles_to_update.push_back(&expected_profile);
-
-  // Expect no sync events to add origin to the remote data.
-  syncer::SyncChangeList expected_empty_change_list;
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_empty_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Missing language code field should not generate sync events.
-TEST_F(AutofillProfileSyncableServiceTest, NoLanguageCodeNoSync) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  // Local autofill profile has an empty language code.
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  EXPECT_TRUE(profile.language_code().empty());
-  profiles_from_web_db.push_back(std::make_unique<AutofillProfile>(profile));
-
-  // Remote data does not have a language code value.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->set_origin(profile.origin());
-  autofill_specifics->add_name_first(std::string());
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  autofill_specifics->set_use_count(profile.use_count());
-  autofill_specifics->set_use_date(profile.use_date().ToTimeT());
-  EXPECT_FALSE(autofill_specifics->has_address_home_language_code());
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(
-      syncer::SyncData::CreateLocalData(
-          profile.guid(), profile.guid(), specifics));
-
-  // Expect no changes to local and remote data.
-  MockAutofillProfileSyncableService::DataBundle expected_empty_bundle;
-  syncer::SyncChangeList expected_empty_change_list;
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_empty_bundle, expected_empty_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Empty language code should be overwritten by sync.
-TEST_F(AutofillProfileSyncableServiceTest, SyncUpdatesEmptyLanguageCode) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  // Local autofill profile has an empty language code.
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  EXPECT_TRUE(profile.language_code().empty());
-  profiles_from_web_db.push_back(std::make_unique<AutofillProfile>(profile));
-
-  // Remote data has "en" language code.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->set_origin(profile.origin());
-  autofill_specifics->add_name_first(std::string());
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  autofill_specifics->set_address_home_language_code("en");
-  EXPECT_TRUE(autofill_specifics->has_address_home_language_code());
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(
-      syncer::SyncData::CreateLocalData(
-          profile.guid(), profile.guid(), specifics));
-
-  // Expect the local autofill profile to have "en" language code after sync.
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  AutofillProfile expected_profile(kGuid1, kEmptyOrigin);
-  expected_profile.set_language_code("en");
-  expected_bundle.profiles_to_update.push_back(&expected_profile);
-
-  // Expect no changes to remote data.
-  syncer::SyncChangeList expected_empty_change_list;
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_empty_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Incorrect language code should be overwritten by sync.
-TEST_F(AutofillProfileSyncableServiceTest, SyncUpdatesIncorrectLanguageCode) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  // Local autofill profile has "de" language code.
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  profile.set_language_code("de");
-  profiles_from_web_db.push_back(std::make_unique<AutofillProfile>(profile));
-
-  // Remote data has "en" language code.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->set_origin(profile.origin());
-  autofill_specifics->add_name_first(std::string());
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  autofill_specifics->set_address_home_language_code("en");
-  EXPECT_TRUE(autofill_specifics->has_address_home_language_code());
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(
-      syncer::SyncData::CreateLocalData(
-          profile.guid(), profile.guid(), specifics));
-
-  // Expect the local autofill profile to have "en" language code after sync.
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  AutofillProfile expected_profile(kGuid1, kEmptyOrigin);
-  expected_profile.set_language_code("en");
-  expected_bundle.profiles_to_update.push_back(&expected_profile);
-
-  // Expect no changes to remote data.
-  syncer::SyncChangeList expected_empty_change_list;
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_empty_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Sync data without language code should not overwrite existing language code
-// in local autofill profile.
-TEST_F(AutofillProfileSyncableServiceTest, EmptySyncPreservesLanguageCode) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  // Local autofill profile has "en" language code.
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  profile.set_language_code("en");
-  profiles_from_web_db.push_back(std::make_unique<AutofillProfile>(profile));
-
-  // Remote data does not have a language code value.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->set_origin(profile.origin());
-  autofill_specifics->add_name_first("John");
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  EXPECT_FALSE(autofill_specifics->has_address_home_language_code());
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(
-      syncer::SyncData::CreateLocalData(
-          profile.guid(), profile.guid(), specifics));
-
-  // Expect local autofill profile to still have "en" language code after sync.
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  AutofillProfile expected_profile(profile.guid(), profile.origin());
-  expected_profile.set_language_code("en");
-  expected_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  expected_bundle.profiles_to_update.push_back(&expected_profile);
-
-  // Expect no changes to remote data.
-  syncer::SyncChangeList expected_empty_change_list;
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_empty_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Language code in autofill profiles should be synced to the server.
-TEST_F(AutofillProfileSyncableServiceTest, LanguageCodePropagates) {
-  TestSyncChangeProcessor* sync_change_processor = new TestSyncChangeProcessor;
-  autofill_syncable_service_.set_sync_processor(sync_change_processor);
-
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  profile.set_language_code("en");
-  AutofillProfileChange change(AutofillProfileChange::ADD, kGuid1, &profile);
-  autofill_syncable_service_.AutofillProfileChanged(change);
-
-  ASSERT_EQ(1U, sync_change_processor->changes().size());
-  syncer::SyncChange result = sync_change_processor->changes()[0];
-  EXPECT_EQ(syncer::SyncChange::ACTION_ADD, result.change_type());
-
-  sync_pb::AutofillProfileSpecifics specifics =
-      result.sync_data().GetSpecifics().autofill_profile();
-  EXPECT_EQ(kGuid1, specifics.guid());
-  EXPECT_EQ(kEmptyOrigin, specifics.origin());
-  EXPECT_EQ("en", specifics.address_home_language_code());
-}
-
-// Missing validity state bitifield should not generate sync events.
-TEST_F(AutofillProfileSyncableServiceTest, DefaultValidityStateNoSync) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  // Local autofill profile has a default validity state bitfield.
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  EXPECT_EQ(0, profile.GetClientValidityBitfieldValue());
-  profiles_from_web_db.push_back(std::make_unique<AutofillProfile>(profile));
-
-  // Remote data does not have a validity state bitfield value.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->set_origin(profile.origin());
-  autofill_specifics->add_name_first(std::string());
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  autofill_specifics->set_use_count(profile.use_count());
-  autofill_specifics->set_use_date(profile.use_date().ToTimeT());
-  EXPECT_FALSE(autofill_specifics->has_validity_state_bitfield());
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(syncer::SyncData::CreateLocalData(
-      profile.guid(), profile.guid(), specifics));
-
-  // Expect no changes to local and remote data.
-  MockAutofillProfileSyncableService::DataBundle expected_empty_bundle;
-  syncer::SyncChangeList expected_empty_change_list;
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_empty_bundle, expected_empty_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Default validity state bitfield should be overwritten by sync.
-TEST_F(AutofillProfileSyncableServiceTest,
-       SyncUpdatesDefaultValidityBitfieldAndFlag) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  // Local autofill profile has a default validity state.
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  EXPECT_EQ(0, profile.GetClientValidityBitfieldValue());
-  profiles_from_web_db.push_back(std::make_unique<AutofillProfile>(profile));
-
-  // Remote data has a non default validity state bitfield value.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->set_origin(profile.origin());
-  autofill_specifics->add_name_first(std::string());
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  autofill_specifics->set_validity_state_bitfield(kValidityStateBitfield);
-  autofill_specifics->set_is_client_validity_states_updated(true);
-  EXPECT_TRUE(autofill_specifics->has_validity_state_bitfield());
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(syncer::SyncData::CreateLocalData(
-      profile.guid(), profile.guid(), specifics));
-
-  // Expect the local autofill profile to have the non default validity state
-  // bitfield after sync.
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  AutofillProfile expected_profile(kGuid1, kEmptyOrigin);
-  expected_profile.SetClientValidityFromBitfieldValue(kValidityStateBitfield);
-  expected_profile.set_is_client_validity_states_updated(true);
-  expected_bundle.profiles_to_update.push_back(&expected_profile);
-
-  // Expect no changes to remote data.
-  syncer::SyncChangeList expected_empty_change_list;
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_empty_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Local validity state bitfield should be overwritten by sync.
-TEST_F(AutofillProfileSyncableServiceTest, SyncUpdatesLocalValidityBitfield) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  // Local autofill profile has a non default validity state bitfield value.
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  profile.SetClientValidityFromBitfieldValue(kValidityStateBitfield + 1);
-  profiles_from_web_db.push_back(std::make_unique<AutofillProfile>(profile));
-
-  // Remote data has a different non default validity state bitfield value.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->set_origin(profile.origin());
-  autofill_specifics->add_name_first(std::string());
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  autofill_specifics->set_validity_state_bitfield(kValidityStateBitfield);
-  EXPECT_TRUE(autofill_specifics->has_validity_state_bitfield());
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(syncer::SyncData::CreateLocalData(
-      profile.guid(), profile.guid(), specifics));
-
-  // Expect the local autofill profile to have the remote validity state
-  // bitfield value after sync.
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  AutofillProfile expected_profile(kGuid1, kEmptyOrigin);
-  expected_profile.SetClientValidityFromBitfieldValue(kValidityStateBitfield);
-  expected_bundle.profiles_to_update.push_back(&expected_profile);
-
-  // Expect no changes to remote data.
-  syncer::SyncChangeList expected_empty_change_list;
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_empty_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Sync data without a default validity state bitfield should not overwrite
-// an existing validity state bitfield in local autofill profile.
-TEST_F(AutofillProfileSyncableServiceTest,
-       DefaultSyncPreservesLocalValidityBitfield) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  // Local autofill profile has a non default validity state bitfield value.
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  profile.SetClientValidityFromBitfieldValue(kValidityStateBitfield);
-  profiles_from_web_db.push_back(std::make_unique<AutofillProfile>(profile));
-
-  // Remote data does not has no validity state bitfield value.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->set_origin(profile.origin());
-  autofill_specifics->add_name_first("John");
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  EXPECT_FALSE(autofill_specifics->has_validity_state_bitfield());
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(syncer::SyncData::CreateLocalData(
-      profile.guid(), profile.guid(), specifics));
-
-  // Expect local autofill profile to still have the kValidityStateBitfield
-  // language code after sync.
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  AutofillProfile expected_profile(profile.guid(), profile.origin());
-  expected_profile.SetClientValidityFromBitfieldValue(kValidityStateBitfield);
-  expected_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  expected_bundle.profiles_to_update.push_back(&expected_profile);
-
-  // Expect no changes to remote data.
-  syncer::SyncChangeList expected_empty_change_list;
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_empty_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Validity state bitfield in autofill profiles should be synced to the server.
-TEST_F(AutofillProfileSyncableServiceTest, LocalValidityBitfieldPropagates) {
-  TestSyncChangeProcessor* sync_change_processor = new TestSyncChangeProcessor;
-  autofill_syncable_service_.set_sync_processor(sync_change_processor);
-
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  profile.SetClientValidityFromBitfieldValue(kValidityStateBitfield);
-  AutofillProfileChange change(AutofillProfileChange::ADD, kGuid1, &profile);
-  autofill_syncable_service_.AutofillProfileChanged(change);
-
-  ASSERT_EQ(1U, sync_change_processor->changes().size());
-  syncer::SyncChange result = sync_change_processor->changes()[0];
-  EXPECT_EQ(syncer::SyncChange::ACTION_ADD, result.change_type());
-
-  sync_pb::AutofillProfileSpecifics specifics =
-      result.sync_data().GetSpecifics().autofill_profile();
-  EXPECT_EQ(kGuid1, specifics.guid());
-  EXPECT_EQ(kEmptyOrigin, specifics.origin());
-  EXPECT_EQ(kValidityStateBitfield, specifics.validity_state_bitfield());
-}
-
-// Missing full name field should not generate sync events.
-TEST_F(AutofillProfileSyncableServiceTest, NoFullNameNoSync) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  // Local autofill profile has an empty full name.
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  profiles_from_web_db.push_back(std::make_unique<AutofillProfile>(profile));
-
-  // Remote data does not have a full name.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->set_origin(profile.origin());
-  autofill_specifics->add_name_first(std::string("John"));
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->set_use_count(profile.use_count());
-  autofill_specifics->set_use_date(profile.use_date().ToTimeT());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(
-      syncer::SyncData::CreateLocalData(
-          profile.guid(), profile.guid(), specifics));
-
-  // Expect no changes to local and remote data.
-  MockAutofillProfileSyncableService::DataBundle expected_empty_bundle;
-  syncer::SyncChangeList expected_empty_change_list;
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_empty_bundle, expected_empty_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-TEST_F(AutofillProfileSyncableServiceTest, EmptySyncPreservesFullName) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  // Local autofill profile has a full name.
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  profile.SetRawInfo(NAME_FULL, ASCIIToUTF16("John Jacob Smith, Jr"));
-  profiles_from_web_db.push_back(std::make_unique<AutofillProfile>(profile));
-
-  // Remote data does not have a full name value.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->set_origin(profile.origin());
-  autofill_specifics->add_name_first(std::string("John"));
-  autofill_specifics->add_name_middle(std::string("Jacob"));
-  autofill_specifics->add_name_last(std::string("Smith"));
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(
-      syncer::SyncData::CreateLocalData(
-          profile.guid(), profile.guid(), specifics));
-
-  // Expect local autofill profile to still have the same full name after sync.
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  AutofillProfile expected_profile(profile.guid(), profile.origin());
-  expected_profile.SetInfo(AutofillType(NAME_FULL),
-                           ASCIIToUTF16("John Jacob Smith, Jr"),
-                           "en-US");
-  expected_bundle.profiles_to_update.push_back(&expected_profile);
-
-  // Expect no changes to remote data.
-  syncer::SyncChangeList expected_empty_change_list;
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_empty_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Missing use_count/use_date fields should not generate sync events.
-TEST_F(AutofillProfileSyncableServiceTest, NoUsageStatsNoSync) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  // Local autofill profile has 0 for use_count/use_date.
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  profile.set_language_code("en");
-  profile.set_use_count(0);
-  profile.set_use_date(base::Time());
-  EXPECT_EQ(0U, profile.use_count());
-  EXPECT_EQ(base::Time(), profile.use_date());
-  profiles_from_web_db.push_back(std::make_unique<AutofillProfile>(profile));
-
-  // Remote data does not have use_count/use_date.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->set_origin(profile.origin());
-  autofill_specifics->add_name_first(std::string());
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  autofill_specifics->set_address_home_language_code("en");
-  EXPECT_FALSE(autofill_specifics->has_use_count());
-  EXPECT_FALSE(autofill_specifics->has_use_date());
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(
-      syncer::SyncData::CreateLocalData(
-          profile.guid(), profile.guid(), specifics));
-
-  // Expect no changes to local and remote data.
-  MockAutofillProfileSyncableService::DataBundle expected_empty_bundle;
-  syncer::SyncChangeList expected_empty_change_list;
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_empty_bundle, expected_empty_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-struct SyncUpdatesUsageStatsTestCase {
-  size_t local_use_count;
-  base::Time local_use_date;
-  size_t remote_use_count;
-  int remote_use_date;
-  size_t synced_use_count;
-  base::Time synced_use_date;
-};
-
-class SyncUpdatesUsageStatsTest
-    : public testing::TestWithParam<SyncUpdatesUsageStatsTestCase> {
- public:
-  SyncUpdatesUsageStatsTest() { CountryNames::SetLocaleString("en-US"); }
-
-  void SetUp() override { sync_processor_.reset(new MockSyncChangeProcessor); }
-
-  // Wrapper around AutofillProfileSyncableService::MergeDataAndStartSyncing()
-  // that also verifies expectations.
-  void MergeDataAndStartSyncing(
-      std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db,
-      const syncer::SyncDataList& data_list,
-      const MockAutofillProfileSyncableService::DataBundle& expected_bundle,
-      const syncer::SyncChangeList& expected_change_list) {
-    auto profile_returner = [&profiles_from_web_db]() {
-      return std::move(profiles_from_web_db);
-    };
-    EXPECT_CALL(autofill_syncable_service_, LoadAutofillData(_))
-        .Times(1)
-        .WillOnce(DoAll(LoadAutofillProfiles(profile_returner), Return(true)));
-    EXPECT_CALL(autofill_syncable_service_,
-                SaveChangesToWebData(DataBundleCheck(expected_bundle)))
-        .Times(1)
-        .WillOnce(Return(true));
-    if (expected_change_list.empty()) {
-      EXPECT_CALL(*sync_processor_, ProcessSyncChanges(_, _)).Times(0);
-    } else {
-      ON_CALL(*sync_processor_, ProcessSyncChanges(_, _))
-          .WillByDefault(Return(syncer::SyncError()));
-      EXPECT_CALL(*sync_processor_,
-                  ProcessSyncChanges(_, CheckSyncChanges(expected_change_list)))
-          .Times(1)
-          .WillOnce(Return(syncer::SyncError()));
-    }
-
-    // Takes ownership of sync_processor_.
-    autofill_syncable_service_.MergeDataAndStartSyncing(
-        syncer::AUTOFILL_PROFILE, data_list, std::move(sync_processor_),
-        std::unique_ptr<syncer::SyncErrorFactory>(
-            new syncer::SyncErrorFactoryMock()));
-  }
-
- protected:
-  base::test::ScopedTaskEnvironment task_environment_;
-  MockAutofillProfileSyncableService autofill_syncable_service_;
-  std::unique_ptr<MockSyncChangeProcessor> sync_processor_;
-};
-
-TEST_P(SyncUpdatesUsageStatsTest, SyncUpdatesUsageStats) {
-  auto test_case = GetParam();
-  SetUp();
-  std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db;
-
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  profile.set_language_code("en");
-  profile.set_use_count(test_case.local_use_count);
-  profile.set_use_date(test_case.local_use_date);
-  EXPECT_EQ(test_case.local_use_count, profile.use_count());
-  EXPECT_EQ(test_case.local_use_date, profile.use_date());
-  profiles_from_web_db.push_back(std::make_unique<AutofillProfile>(profile));
-
-  // Remote data has usage stats.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->set_origin(profile.origin());
-  autofill_specifics->add_name_first(std::string());
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  autofill_specifics->set_address_home_language_code("en");
-  autofill_specifics->set_use_count(test_case.remote_use_count);
-  autofill_specifics->set_use_date(test_case.remote_use_date);
-  EXPECT_TRUE(autofill_specifics->has_use_count());
-  EXPECT_TRUE(autofill_specifics->has_use_date());
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(syncer::SyncData::CreateLocalData(
-      profile.guid(), profile.guid(), specifics));
-
-  // Expect the local autofill profile to have usage stats after sync.
-  MockAutofillProfileSyncableService::DataBundle expected_bundle;
-  AutofillProfile expected_profile = profile;
-  expected_profile.set_use_count(test_case.synced_use_count);
-  expected_profile.set_use_date(test_case.synced_use_date);
-  expected_bundle.profiles_to_update.push_back(&expected_profile);
-
-  // Expect no changes to remote data.
-  syncer::SyncChangeList expected_empty_change_list;
-
-  MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list,
-                           expected_bundle, expected_empty_change_list);
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    AutofillProfileSyncableServiceTest,
-    SyncUpdatesUsageStatsTest,
-    testing::Values(
-        // Local profile with default stats.
-        SyncUpdatesUsageStatsTestCase{0U, base::Time(), 9U, 4321, 9U,
-                                      base::Time::FromTimeT(4321)},
-        // Local profile has older stats than the server.
-        SyncUpdatesUsageStatsTestCase{3U, base::Time::FromTimeT(1234), 9U, 4321,
-                                      9U, base::Time::FromTimeT(4321)},
-        // Local profile has newer stats than the server
-        SyncUpdatesUsageStatsTestCase{10U, base::Time::FromTimeT(9999), 9U,
-                                      4321, 9U, base::Time::FromTimeT(4321)}));
-
-// Usage stats should be updated by the client.
-TEST_F(AutofillProfileSyncableServiceTest, ClientOverwritesUsageStats) {
-  TestSyncChangeProcessor* sync_change_processor = new TestSyncChangeProcessor;
-
-  // Remote data has a profile with usage stats.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(kGuid1);
-  autofill_specifics->set_origin(kEmptyOrigin);
-  autofill_specifics->add_name_first(std::string());
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  autofill_specifics->set_address_home_language_code("en");
-  autofill_specifics->set_use_count(9);
-  autofill_specifics->set_use_date(25);
-
-  syncer::SyncDataList data_list;
-  data_list.push_back(
-      syncer::SyncData::CreateLocalData(kGuid1, kEmptyOrigin, specifics));
-
-  EXPECT_CALL(autofill_syncable_service_, LoadAutofillData(_))
-      .Times(1)
-      .WillOnce(Return(true));
-  EXPECT_CALL(autofill_syncable_service_,
-              SaveChangesToWebData(_))
-      .Times(1)
-      .WillOnce(Return(true));
-  autofill_syncable_service_.MergeDataAndStartSyncing(
-      syncer::AUTOFILL_PROFILE, data_list,
-      std::unique_ptr<TestSyncChangeProcessor>(sync_change_processor),
-      std::unique_ptr<syncer::SyncErrorFactory>(
-          new syncer::SyncErrorFactoryMock()));
-
-  // Update to the usage stats for that profile.
-  AutofillProfile profile(kGuid1, kEmptyOrigin);
-  profile.set_language_code("en");
-  profile.set_use_count(10U);
-  profile.set_use_date(base::Time::FromTimeT(30));
-  AutofillProfileChange change(AutofillProfileChange::UPDATE, kGuid1, &profile);
-  autofill_syncable_service_.AutofillProfileChanged(change);
-  std::vector<AutofillProfile*> profiles;
-  profiles.push_back(&profile);
-
-  ASSERT_EQ(1U, sync_change_processor->changes().size());
-  syncer::SyncChange result = sync_change_processor->changes()[0];
-  EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, result.change_type());
-
-  sync_pb::AutofillProfileSpecifics result_specifics =
-      result.sync_data().GetSpecifics().autofill_profile();
-  EXPECT_EQ(10U, result_specifics.use_count());
-  EXPECT_EQ(30, result_specifics.use_date());
-
-  autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE);
-}
-
-// Server profile updates should be ignored.
-TEST_F(AutofillProfileSyncableServiceTest, IgnoreServerProfileUpdate) {
-  EXPECT_CALL(autofill_syncable_service_, LoadAutofillData(_))
-      .Times(1)
-      .WillOnce(Return(true));
-  EXPECT_CALL(autofill_syncable_service_, SaveChangesToWebData(_))
-      .Times(1)
-      .WillOnce(Return(true));
-  autofill_syncable_service_.MergeDataAndStartSyncing(
-      syncer::AUTOFILL_PROFILE, syncer::SyncDataList(),
-      std::make_unique<TestSyncChangeProcessor>(),
-      std::unique_ptr<syncer::SyncErrorFactory>(
-          new syncer::SyncErrorFactoryMock()));
-  AutofillProfile server_profile(AutofillProfile::SERVER_PROFILE, "server-id");
-
-  // Should not crash:
-  autofill_syncable_service_.AutofillProfileChanged(AutofillProfileChange(
-      AutofillProfileChange::UPDATE, server_profile.guid(), &server_profile));
-}
-
-// Tests that a non-settings origin from the server is never set to the local
-// profile.
-TEST_F(AutofillProfileSyncableServiceTest,
-       OverwriteProfileWithServerData_NonSettingsOrigin) {
-  // Create a profile with an empty origin.
-  AutofillProfile profile(kGuid1, std::string());
-  profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1 1st st"));
-
-  // Create a Sync profile with a non-settings origin.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->set_origin("https://www.example.com");
-  autofill_specifics->add_name_first("John");
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  autofill_specifics->set_address_home_line1("1 1st st");
-  autofill_specifics->set_use_count(profile.use_count());
-  autofill_specifics->set_use_date(profile.use_date().ToTimeT());
-
-  // Expect that the empty origin is not overwritten.
-  autofill_syncable_service_.OverwriteProfileWithServerData(*autofill_specifics,
-                                                            &profile);
-  EXPECT_TRUE(profile.origin().empty());
-
-  // Set the local origin to settings.
-  profile.set_origin(kSettingsOrigin);
-
-  // Expect that the settings origin is not overwritten.
-  autofill_syncable_service_.OverwriteProfileWithServerData(*autofill_specifics,
-                                                            &profile);
-  EXPECT_EQ(kSettingsOrigin, profile.origin());
-}
-
-// Tests that a non-settings origin from the server is not set to the local
-// profile.
-TEST_F(AutofillProfileSyncableServiceTest,
-       OverwriteProfileWithServerData_SettingsOrigin) {
-  // Create a profile with an empty origin.
-  AutofillProfile profile(kGuid1, std::string());
-  profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1 1st st"));
-
-  // Create a Sync profile with a non-settings origin.
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::AutofillProfileSpecifics* autofill_specifics =
-      specifics.mutable_autofill_profile();
-  autofill_specifics->set_guid(profile.guid());
-  autofill_specifics->set_origin(kSettingsOrigin);
-  autofill_specifics->add_name_first("John");
-  autofill_specifics->add_name_middle(std::string());
-  autofill_specifics->add_name_last(std::string());
-  autofill_specifics->add_name_full(std::string());
-  autofill_specifics->add_email_address(std::string());
-  autofill_specifics->add_phone_home_whole_number(std::string());
-  autofill_specifics->set_address_home_line1("1 1st st");
-  autofill_specifics->set_use_count(profile.use_count());
-  autofill_specifics->set_use_date(profile.use_date().ToTimeT());
-
-  // Expect that the settings origin replaced the empty origin.
-  autofill_syncable_service_.OverwriteProfileWithServerData(*autofill_specifics,
-                                                            &profile);
-  EXPECT_EQ(kSettingsOrigin, profile.origin());
-}
-
-}  // namespace autofill
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index 855e5a3..c17f8d8 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -174,7 +174,6 @@
     "element_precondition_unittest.cc",
     "fake_script_executor_delegate.cc",
     "fake_script_executor_delegate.h",
-    "mock_run_once_callback.h",
     "mock_service.cc",
     "mock_service.h",
     "mock_ui_controller.cc",
diff --git a/components/autofill_assistant/browser/actions/autofill_action_unittest.cc b/components/autofill_assistant/browser/actions/autofill_action_unittest.cc
index 7614d11..691bc07 100644
--- a/components/autofill_assistant/browser/actions/autofill_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/autofill_action_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/guid.h"
+#include "base/test/gmock_callback_support.h"
 #include "build/build_config.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
@@ -15,7 +16,6 @@
 #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_run_once_callback.h"
 #include "components/autofill_assistant/browser/mock_web_controller.h"
 #include "components/autofill_assistant/browser/service.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -23,6 +23,7 @@
 namespace autofill_assistant {
 namespace {
 
+using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::Eq;
 using ::testing::InSequence;
diff --git a/components/autofill_assistant/browser/actions/configure_bottom_sheet_action_unittest.cc b/components/autofill_assistant/browser/actions/configure_bottom_sheet_action_unittest.cc
index b96af79..2a3edb7d 100644
--- a/components/autofill_assistant/browser/actions/configure_bottom_sheet_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/configure_bottom_sheet_action_unittest.cc
@@ -11,7 +11,6 @@
 #include "base/test/bind_test_util.h"
 #include "base/test/scoped_task_environment.h"
 #include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
-#include "components/autofill_assistant/browser/mock_run_once_callback.h"
 #include "components/autofill_assistant/browser/mock_web_controller.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.cc b/components/autofill_assistant/browser/actions/mock_action_delegate.cc
index 627b30a3..3c4daf0 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.cc
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.cc
@@ -4,11 +4,6 @@
 
 #include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
 
-#include "components/autofill_assistant/browser/mock_run_once_callback.h"
-#include "url/gurl.h"
-
-using ::testing::_;
-
 namespace autofill_assistant {
 
 MockActionDelegate::MockActionDelegate() = default;
diff --git a/components/autofill_assistant/browser/actions/prompt_action_unittest.cc b/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
index 2bb8a5e..76d7c3e0 100644
--- a/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
@@ -8,16 +8,17 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
 #include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
-#include "components/autofill_assistant/browser/mock_run_once_callback.h"
 #include "components/autofill_assistant/browser/mock_web_controller.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace autofill_assistant {
 namespace {
 
+using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::Eq;
 using ::testing::Invoke;
diff --git a/components/autofill_assistant/browser/actions/wait_for_dom_action_unittest.cc b/components/autofill_assistant/browser/actions/wait_for_dom_action_unittest.cc
index d574bbe..8fbe51e 100644
--- a/components/autofill_assistant/browser/actions/wait_for_dom_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/wait_for_dom_action_unittest.cc
@@ -8,15 +8,16 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#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/mock_run_once_callback.h"
 #include "components/autofill_assistant/browser/mock_web_controller.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace autofill_assistant {
 namespace {
 
+using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::ElementsAre;
 using ::testing::Invoke;
diff --git a/components/autofill_assistant/browser/batch_element_checker_unittest.cc b/components/autofill_assistant/browser/batch_element_checker_unittest.cc
index 4fb0a40..5523c71 100644
--- a/components/autofill_assistant/browser/batch_element_checker_unittest.cc
+++ b/components/autofill_assistant/browser/batch_element_checker_unittest.cc
@@ -9,12 +9,13 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
-#include "components/autofill_assistant/browser/mock_run_once_callback.h"
 #include "components/autofill_assistant/browser/mock_web_controller.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
+using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::Contains;
 using ::testing::ElementsAre;
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index 22e7bc0..60338d4 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -7,10 +7,10 @@
 #include <memory>
 #include <utility>
 
+#include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
 #include "components/autofill_assistant/browser/features.h"
-#include "components/autofill_assistant/browser/mock_run_once_callback.h"
 #include "components/autofill_assistant/browser/mock_service.h"
 #include "components/autofill_assistant/browser/mock_ui_controller.h"
 #include "components/autofill_assistant/browser/mock_web_controller.h"
@@ -25,6 +25,7 @@
 
 namespace autofill_assistant {
 
+using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::AnyNumber;
 using ::testing::AtLeast;
diff --git a/components/autofill_assistant/browser/element_area_unittest.cc b/components/autofill_assistant/browser/element_area_unittest.cc
index e928994..d6686ac 100644
--- a/components/autofill_assistant/browser/element_area_unittest.cc
+++ b/components/autofill_assistant/browser/element_area_unittest.cc
@@ -10,14 +10,15 @@
 
 #include "base/bind.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
 #include "components/autofill_assistant/browser/fake_script_executor_delegate.h"
-#include "components/autofill_assistant/browser/mock_run_once_callback.h"
 #include "components/autofill_assistant/browser/mock_web_controller.h"
 #include "components/autofill_assistant/browser/script_executor_delegate.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
+using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::ElementsAre;
 using ::testing::Eq;
diff --git a/components/autofill_assistant/browser/element_precondition_unittest.cc b/components/autofill_assistant/browser/element_precondition_unittest.cc
index 40ab14a0..5850608 100644
--- a/components/autofill_assistant/browser/element_precondition_unittest.cc
+++ b/components/autofill_assistant/browser/element_precondition_unittest.cc
@@ -9,9 +9,9 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
 #include "components/autofill_assistant/browser/batch_element_checker.h"
-#include "components/autofill_assistant/browser/mock_run_once_callback.h"
 #include "components/autofill_assistant/browser/mock_web_controller.h"
 #include "components/autofill_assistant/browser/service.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -20,6 +20,7 @@
 namespace autofill_assistant {
 namespace {
 
+using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::Eq;
 
diff --git a/components/autofill_assistant/browser/mock_run_once_callback.h b/components/autofill_assistant/browser/mock_run_once_callback.h
deleted file mode 100644
index a4721f0..0000000
--- a/components/autofill_assistant/browser/mock_run_once_callback.h
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_RUN_ONCE_CALLBACK_H_
-#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_RUN_ONCE_CALLBACK_H_
-
-#include <utility>
-
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace autofill_assistant {
-
-// Templates for calling base::OnceCallback from gmock actions.
-//
-// To work around the fact that OnceCallback can't be copied, the method
-// to be mocked needs to take the callback as a reference. To do it without
-// changing the original interface, follow this pattern:
-//
-//   void DoSomething(..., base::OnceCallback<void(bool)> callback) override {
-//     OnDoSomething(..., callback);
-//   }
-//   MOCK_METHOD2(OnDoSomething,
-//       void(..., base::OnceCallback<void(bool)>& callback));
-//
-//
-
-ACTION_TEMPLATE(RunOnceCallback,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_0_VALUE_PARAMS()) {
-  return std::move(std::get<k>(args)).Run();
-}
-
-ACTION_TEMPLATE(RunOnceCallback,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_1_VALUE_PARAMS(p0)) {
-  return std::move(std::get<k>(args)).Run(p0);
-}
-
-ACTION_TEMPLATE(RunOnceCallback,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_2_VALUE_PARAMS(p0, p1)) {
-  return std::move(std::get<k>(args)).Run(p0, p1);
-}
-
-ACTION_TEMPLATE(RunOnceCallback,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_3_VALUE_PARAMS(p0, p1, p2)) {
-  return std::move(std::get<k>(args)).Run(p0, p1, p2);
-}
-
-ACTION_TEMPLATE(RunOnceCallback,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_4_VALUE_PARAMS(p0, p1, p2, p3)) {
-  return std::move(std::get<k>(args)).Run(p0, p1, p2, p3);
-}
-
-ACTION_TEMPLATE(RunOnceCallback,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) {
-  return std::move(std::get<k>(args)).Run(p0, p1, p2, p3, p4);
-}
-
-ACTION_TEMPLATE(RunOnceCallback,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) {
-  return std::move(std::get<k>(args)).Run(p0, p1, p2, p3, p4, p5);
-}
-
-ACTION_TEMPLATE(RunOnceCallback,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) {
-  return std::move(std::get<k>(args)).Run(p0, p1, p2, p3, p4, p5, p6);
-}
-
-// Template for capturing a base::OnceCallback passed to a mocked method
-//
-// This is useful to run the callback later on, at an appropriate time.
-//
-// base::OnceCallback<void(bool)> captured_callback;
-//   EXPECT_CALL(my_mock_, MyMethod(_))
-//     .WillOnce(CaptureOnceCallback<0>(&captured_callback));
-// [...]
-// std::move(captured_callback).Run();
-//
-
-ACTION_TEMPLATE(CaptureOnceCallback,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_1_VALUE_PARAMS(p0)) {
-  *p0 = std::move(std::get<k>(args));
-}
-
-}  // namespace autofill_assistant
-
-#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_RUN_ONCE_CALLBACK_H_
diff --git a/components/autofill_assistant/browser/script_executor_unittest.cc b/components/autofill_assistant/browser/script_executor_unittest.cc
index 819580b5..3d8db2d 100644
--- a/components/autofill_assistant/browser/script_executor_unittest.cc
+++ b/components/autofill_assistant/browser/script_executor_unittest.cc
@@ -8,11 +8,11 @@
 #include <utility>
 
 #include "base/strings/strcat.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
 #include "components/autofill_assistant/browser/client_memory.h"
 #include "components/autofill_assistant/browser/fake_script_executor_delegate.h"
-#include "components/autofill_assistant/browser/mock_run_once_callback.h"
 #include "components/autofill_assistant/browser/mock_service.h"
 #include "components/autofill_assistant/browser/mock_ui_controller.h"
 #include "components/autofill_assistant/browser/mock_web_controller.h"
@@ -23,6 +23,7 @@
 
 namespace {
 
+using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::AllOf;
 using ::testing::Contains;
diff --git a/components/autofill_assistant/browser/script_precondition_unittest.cc b/components/autofill_assistant/browser/script_precondition_unittest.cc
index bd5ac588..3ccf22d 100644
--- a/components/autofill_assistant/browser/script_precondition_unittest.cc
+++ b/components/autofill_assistant/browser/script_precondition_unittest.cc
@@ -9,8 +9,8 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
 #include "components/autofill_assistant/browser/batch_element_checker.h"
-#include "components/autofill_assistant/browser/mock_run_once_callback.h"
 #include "components/autofill_assistant/browser/mock_web_controller.h"
 #include "components/autofill_assistant/browser/service.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -19,6 +19,7 @@
 namespace autofill_assistant {
 namespace {
 
+using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::Eq;
 using ::testing::Invoke;
diff --git a/components/autofill_assistant/browser/script_tracker_unittest.cc b/components/autofill_assistant/browser/script_tracker_unittest.cc
index 1d0d1d5..a1391fa 100644
--- a/components/autofill_assistant/browser/script_tracker_unittest.cc
+++ b/components/autofill_assistant/browser/script_tracker_unittest.cc
@@ -6,9 +6,9 @@
 
 #include <utility>
 
+#include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
 #include "components/autofill_assistant/browser/fake_script_executor_delegate.h"
-#include "components/autofill_assistant/browser/mock_run_once_callback.h"
 #include "components/autofill_assistant/browser/mock_service.h"
 #include "components/autofill_assistant/browser/mock_ui_controller.h"
 #include "components/autofill_assistant/browser/mock_web_controller.h"
@@ -18,6 +18,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace autofill_assistant {
+using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::ElementsAre;
 using ::testing::Eq;
diff --git a/components/bookmarks/browser/base_bookmark_model_observer.cc b/components/bookmarks/browser/base_bookmark_model_observer.cc
index 7d40d0f..657a3c9 100644
--- a/components/bookmarks/browser/base_bookmark_model_observer.cc
+++ b/components/bookmarks/browser/base_bookmark_model_observer.cc
@@ -17,22 +17,22 @@
 void BaseBookmarkModelObserver::BookmarkNodeMoved(
     BookmarkModel* model,
     const BookmarkNode* old_parent,
-    int old_index,
+    size_t old_index,
     const BookmarkNode* new_parent,
-    int new_index) {
+    size_t new_index) {
   BookmarkModelChanged();
 }
 
 void BaseBookmarkModelObserver::BookmarkNodeAdded(BookmarkModel* model,
                                                   const BookmarkNode* parent,
-                                                  int index) {
+                                                  size_t index) {
   BookmarkModelChanged();
 }
 
 void BaseBookmarkModelObserver::BookmarkNodeRemoved(
     BookmarkModel* model,
     const BookmarkNode* parent,
-    int old_index,
+    size_t old_index,
     const BookmarkNode* node,
     const std::set<GURL>& removed_urls) {
   BookmarkModelChanged();
diff --git a/components/bookmarks/browser/base_bookmark_model_observer.h b/components/bookmarks/browser/base_bookmark_model_observer.h
index 14a184e..f2b93075 100644
--- a/components/bookmarks/browser/base_bookmark_model_observer.h
+++ b/components/bookmarks/browser/base_bookmark_model_observer.h
@@ -23,15 +23,15 @@
   void BookmarkModelBeingDeleted(BookmarkModel* model) override;
   void BookmarkNodeMoved(BookmarkModel* model,
                          const BookmarkNode* old_parent,
-                         int old_index,
+                         size_t old_index,
                          const BookmarkNode* new_parent,
-                         int new_index) override;
+                         size_t new_index) override;
   void BookmarkNodeAdded(BookmarkModel* model,
                          const BookmarkNode* parent,
-                         int index) override;
+                         size_t index) override;
   void BookmarkNodeRemoved(BookmarkModel* model,
                            const BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override;
   void BookmarkAllUserNodesRemoved(BookmarkModel* model,
diff --git a/components/bookmarks/browser/bookmark_codec_unittest.cc b/components/bookmarks/browser/bookmark_codec_unittest.cc
index 4accc279..6ca33211 100644
--- a/components/bookmarks/browser/bookmark_codec_unittest.cc
+++ b/components/bookmarks/browser/bookmark_codec_unittest.cc
@@ -357,12 +357,11 @@
   // Add a couple of more items to the decoded bookmark model and make sure
   // ID persistence is working properly.
   const BookmarkNode* bookmark_bar = decoded_model->bookmark_bar_node();
-  decoded_model->AddURL(bookmark_bar,
-                        bookmark_bar->child_count(),
-                        ASCIIToUTF16(kUrl3Title),
-                        GURL(kUrl3Url));
-  const BookmarkNode* folder2_node = decoded_model->AddFolder(
-      bookmark_bar, bookmark_bar->child_count(), ASCIIToUTF16(kFolder2Title));
+  decoded_model->AddURL(bookmark_bar, bookmark_bar->children().size(),
+                        ASCIIToUTF16(kUrl3Title), GURL(kUrl3Url));
+  const BookmarkNode* folder2_node =
+      decoded_model->AddFolder(bookmark_bar, bookmark_bar->children().size(),
+                               ASCIIToUTF16(kFolder2Title));
   decoded_model->AddURL(
       folder2_node, 0, ASCIIToUTF16(kUrl4Title), GURL(kUrl4Url));
 
diff --git a/components/bookmarks/browser/bookmark_expanded_state_tracker.cc b/components/bookmarks/browser/bookmark_expanded_state_tracker.cc
index 40cdbaa..4ad2afa1 100644
--- a/components/bookmarks/browser/bookmark_expanded_state_tracker.cc
+++ b/components/bookmarks/browser/bookmark_expanded_state_tracker.cc
@@ -85,7 +85,7 @@
 void BookmarkExpandedStateTracker::BookmarkNodeRemoved(
     BookmarkModel* model,
     const BookmarkNode* parent,
-    int old_index,
+    size_t old_index,
     const BookmarkNode* node,
     const std::set<GURL>& removed_urls) {
   if (!node->is_folder())
diff --git a/components/bookmarks/browser/bookmark_expanded_state_tracker.h b/components/bookmarks/browser/bookmark_expanded_state_tracker.h
index 92f8d5f..a73244f 100644
--- a/components/bookmarks/browser/bookmark_expanded_state_tracker.h
+++ b/components/bookmarks/browser/bookmark_expanded_state_tracker.h
@@ -39,7 +39,7 @@
   void BookmarkModelBeingDeleted(BookmarkModel* model) override;
   void BookmarkNodeRemoved(BookmarkModel* model,
                            const BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override;
   void BookmarkAllUserNodesRemoved(BookmarkModel* model,
diff --git a/components/bookmarks/browser/bookmark_index_unittest.cc b/components/bookmarks/browser/bookmark_index_unittest.cc
index 06766f48..db35599 100644
--- a/components/bookmarks/browser/bookmark_index_unittest.cc
+++ b/components/bookmarks/browser/bookmark_index_unittest.cc
@@ -72,8 +72,7 @@
 
   void AddBookmarks(const std::vector<TitleAndURL>& bookmarks) {
     for (size_t i = 0; i < bookmarks.size(); ++i) {
-      model_->AddURL(model_->other_node(), static_cast<int>(i),
-                     ASCIIToUTF16(bookmarks[i].first),
+      model_->AddURL(model_->other_node(), i, ASCIIToUTF16(bookmarks[i].first),
                      GURL(bookmarks[i].second));
     }
   }
diff --git a/components/bookmarks/browser/bookmark_model.cc b/components/bookmarks/browser/bookmark_model.cc
index 1578f2e5..c6cbe917 100644
--- a/components/bookmarks/browser/bookmark_model.cc
+++ b/components/bookmarks/browser/bookmark_model.cc
@@ -108,7 +108,7 @@
   void SetUndoProvider(BookmarkUndoProvider* provider) override {}
   void OnBookmarkNodeRemoved(BookmarkModel* model,
                              const BookmarkNode* parent,
-                             int index,
+                             size_t index,
                              std::unique_ptr<BookmarkNode> node) override {}
 
   DISALLOW_COPY_AND_ASSIGN(EmptyUndoDelegate);
@@ -200,8 +200,8 @@
   DCHECK(!is_root_node(node));
   const BookmarkNode* parent = node->parent();
   DCHECK(parent);
-  int index = parent->GetIndexOf(node);
-  DCHECK_NE(-1, index);
+  size_t index = size_t{parent->GetIndexOf(node)};
+  DCHECK_NE(size_t{-1}, index);
 
   for (BookmarkModelObserver& observer : observers_)
     observer.OnWillRemoveBookmarks(this, parent, index, node);
@@ -270,7 +270,7 @@
 
 void BookmarkModel::Move(const BookmarkNode* node,
                          const BookmarkNode* new_parent,
-                         int index) {
+                         size_t index) {
   DCHECK(loaded_);
   DCHECK(node);
   DCHECK(IsValidIndex(new_parent, index, true));
@@ -279,7 +279,7 @@
   DCHECK(!new_parent->HasAncestor(node));
 
   const BookmarkNode* old_parent = node->parent();
-  int old_index = old_parent->GetIndexOf(node);
+  size_t old_index = old_parent->GetIndexOf(node);
 
   if (old_parent == new_parent &&
       (index == old_index || index == old_index + 1)) {
@@ -307,7 +307,7 @@
 
 void BookmarkModel::Copy(const BookmarkNode* node,
                          const BookmarkNode* new_parent,
-                         int index) {
+                         size_t index) {
   DCHECK(loaded_);
   DCHECK(node);
   DCHECK(IsValidIndex(new_parent, index, true));
@@ -554,13 +554,13 @@
 }
 
 const BookmarkNode* BookmarkModel::AddFolder(const BookmarkNode* parent,
-                                             int index,
+                                             size_t index,
                                              const base::string16& title) {
   return AddFolderWithMetaInfo(parent, index, title, nullptr);
 }
 const BookmarkNode* BookmarkModel::AddFolderWithMetaInfo(
     const BookmarkNode* parent,
-    int index,
+    size_t index,
     const base::string16& title,
     const BookmarkNode::MetaInfoMap* meta_info) {
   DCHECK(loaded_);
@@ -580,7 +580,7 @@
 }
 
 const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent,
-                                          int index,
+                                          size_t index,
                                           const base::string16& title,
                                           const GURL& url) {
   return AddURLWithCreationTimeAndMetaInfo(parent, index, title, url,
@@ -589,7 +589,7 @@
 
 const BookmarkNode* BookmarkModel::AddURLWithCreationTimeAndMetaInfo(
     const BookmarkNode* parent,
-    int index,
+    size_t index,
     const base::string16& title,
     const GURL& url,
     const Time& creation_time,
@@ -733,7 +733,7 @@
 }
 
 void BookmarkModel::RestoreRemovedNode(const BookmarkNode* parent,
-                                       int index,
+                                       size_t index,
                                        std::unique_ptr<BookmarkNode> node) {
   BookmarkNode* node_ptr = node.get();
   AddNode(AsMutable(parent), index, std::move(node));
@@ -744,7 +744,7 @@
 }
 
 void BookmarkModel::NotifyNodeAddedForAllDescendents(const BookmarkNode* node) {
-  for (int i = 0; i < node->child_count(); ++i) {
+  for (size_t i = 0; i < node->children().size(); ++i) {
     for (BookmarkModelObserver& observer : observers_)
       observer.BookmarkNodeAdded(this, node, i);
     NotifyNodeAddedForAllDescendents(node->GetChild(i));
@@ -812,7 +812,7 @@
 }
 
 BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
-                                     int index,
+                                     size_t index,
                                      std::unique_ptr<BookmarkNode> node) {
   BookmarkNode* node_ptr = node.get();
   url_index_->Add(parent, index, std::move(node));
@@ -836,11 +836,11 @@
 }
 
 bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
-                                 int index,
+                                 size_t index,
                                  bool allow_end) {
-  return (parent && parent->is_folder() &&
-          (index >= 0 && (index < parent->child_count() ||
-                          (allow_end && index == parent->child_count()))));
+  return parent && parent->is_folder() &&
+         (index < parent->children().size() ||
+          (allow_end && index == parent->children().size()));
 }
 
 void BookmarkModel::OnFaviconDataAvailable(
diff --git a/components/bookmarks/browser/bookmark_model.h b/components/bookmarks/browser/bookmark_model.h
index 2f48a65e..f4b6c87 100644
--- a/components/bookmarks/browser/bookmark_model.h
+++ b/components/bookmarks/browser/bookmark_model.h
@@ -137,12 +137,12 @@
   // Moves |node| to |new_parent| and inserts it at the given |index|.
   void Move(const BookmarkNode* node,
             const BookmarkNode* new_parent,
-            int index);
+            size_t index);
 
   // Inserts a copy of |node| into |new_parent| at |index|.
   void Copy(const BookmarkNode* node,
             const BookmarkNode* new_parent,
-            int index);
+            size_t index);
 
   // Returns the favicon for |node|. If the favicon has not yet been loaded,
   // a load will be triggered and the observer of the model notified when done.
@@ -194,26 +194,26 @@
 
   // Adds a new folder node at the specified position.
   const BookmarkNode* AddFolder(const BookmarkNode* parent,
-                                int index,
+                                size_t index,
                                 const base::string16& title);
 
   // Adds a new folder with meta info.
   const BookmarkNode* AddFolderWithMetaInfo(
       const BookmarkNode* parent,
-      int index,
+      size_t index,
       const base::string16& title,
       const BookmarkNode::MetaInfoMap* meta_info);
 
   // Adds a url at the specified position.
   const BookmarkNode* AddURL(const BookmarkNode* parent,
-                             int index,
+                             size_t index,
                              const base::string16& title,
                              const GURL& url);
 
   // Adds a url with a specific creation date and meta info.
   const BookmarkNode* AddURLWithCreationTimeAndMetaInfo(
       const BookmarkNode* parent,
-      int index,
+      size_t index,
       const base::string16& title,
       const GURL& url,
       const base::Time& creation_time,
@@ -318,7 +318,7 @@
 
   // BookmarkUndoProvider:
   void RestoreRemovedNode(const BookmarkNode* parent,
-                          int index,
+                          size_t index,
                           std::unique_ptr<BookmarkNode> node) override;
 
   // Notifies the observers for adding every descedent of |node|.
@@ -336,14 +336,14 @@
   // Adds the |node| at |parent| in the specified |index| and notifies its
   // observers.
   BookmarkNode* AddNode(BookmarkNode* parent,
-                        int index,
+                        size_t index,
                         std::unique_ptr<BookmarkNode> node);
 
   // Adds |node| to |index_| and recursisvely invokes this for all children.
   void AddNodeToIndexRecursive(BookmarkNode* node);
 
   // Returns true if the parent and index are valid.
-  bool IsValidIndex(const BookmarkNode* parent, int index, bool allow_end);
+  bool IsValidIndex(const BookmarkNode* parent, size_t index, bool allow_end);
 
   // Notification that a favicon has finished loading. If we can decode the
   // favicon, FaviconLoaded is invoked.
diff --git a/components/bookmarks/browser/bookmark_model_observer.h b/components/bookmarks/browser/bookmark_model_observer.h
index a946d53..14a571f 100644
--- a/components/bookmarks/browser/bookmark_model_observer.h
+++ b/components/bookmarks/browser/bookmark_model_observer.h
@@ -28,14 +28,14 @@
   // Invoked when a node has moved.
   virtual void BookmarkNodeMoved(BookmarkModel* model,
                                  const BookmarkNode* old_parent,
-                                 int old_index,
+                                 size_t old_index,
                                  const BookmarkNode* new_parent,
-                                 int new_index) = 0;
+                                 size_t new_index) = 0;
 
   // Invoked when a node has been added.
   virtual void BookmarkNodeAdded(BookmarkModel* model,
                                  const BookmarkNode* parent,
-                                 int index) = 0;
+                                 size_t index) = 0;
 
   // Invoked prior to removing a node from the model. When a node is removed
   // it's descendants are implicitly removed from the model as
@@ -49,7 +49,7 @@
   // |node| is the node to be removed.
   virtual void OnWillRemoveBookmarks(BookmarkModel* model,
                                      const BookmarkNode* parent,
-                                     int old_index,
+                                     size_t old_index,
                                      const BookmarkNode* node) {}
 
   // Invoked after a node has been removed from the model. Removing a node
@@ -66,7 +66,7 @@
   virtual void BookmarkNodeRemoved(
       BookmarkModel* model,
       const BookmarkNode* parent,
-      int old_index,
+      size_t old_index,
       const BookmarkNode* node,
       const std::set<GURL>& no_longer_bookmarked) = 0;
 
diff --git a/components/bookmarks/browser/bookmark_model_unittest.cc b/components/bookmarks/browser/bookmark_model_unittest.cc
index e969e7d..2aeffc1 100644
--- a/components/bookmarks/browser/bookmark_model_unittest.cc
+++ b/components/bookmarks/browser/bookmark_model_unittest.cc
@@ -186,8 +186,8 @@
 void PopulateBookmarkNode(TestNode* parent,
                           BookmarkModel* model,
                           const BookmarkNode* bb_node) {
-  for (int i = 0; i < parent->child_count(); ++i) {
-    TestNode* child = parent->GetChild(i);
+  for (size_t i = 0; i < parent->children().size(); ++i) {
+    TestNode* child = parent->children()[i].get();
     if (child->value == BookmarkNode::FOLDER) {
       const BookmarkNode* new_bb_node =
           model->AddFolder(bb_node, i, child->GetTitle());
@@ -230,12 +230,12 @@
                           public BookmarkUndoDelegate {
  public:
   struct ObserverDetails {
-    ObserverDetails() { Set(nullptr, nullptr, -1, -1); }
+    ObserverDetails() { Set(nullptr, nullptr, size_t{-1}, size_t{-1}); }
 
     void Set(const BookmarkNode* node1,
              const BookmarkNode* node2,
-             int index1,
-             int index2) {
+             size_t index1,
+             size_t index2) {
       node1_ = node1;
       node2_ = node2;
       index1_ = index1;
@@ -244,8 +244,8 @@
 
     void ExpectEquals(const BookmarkNode* node1,
                       const BookmarkNode* node2,
-                      int index1,
-                      int index2) {
+                      size_t index1,
+                      size_t index2) {
       EXPECT_EQ(node1_, node1);
       EXPECT_EQ(node2_, node2);
       EXPECT_EQ(index1_, index1);
@@ -255,13 +255,13 @@
    private:
     const BookmarkNode* node1_;
     const BookmarkNode* node2_;
-    int index1_;
-    int index2_;
+    size_t index1_;
+    size_t index2_;
   };
 
   struct NodeRemovalDetail {
     NodeRemovalDetail(const BookmarkNode* parent,
-                      int index,
+                      size_t index,
                       const BookmarkNode* node)
         : parent_node_id(parent->id()), index(index), node_id(node->id()) {}
 
@@ -272,7 +272,7 @@
     }
 
     int64_t parent_node_id;
-    int index;
+    size_t index;
     int64_t node_id;
   };
 
@@ -288,23 +288,23 @@
 
   void BookmarkNodeMoved(BookmarkModel* model,
                          const BookmarkNode* old_parent,
-                         int old_index,
+                         size_t old_index,
                          const BookmarkNode* new_parent,
-                         int new_index) override {
+                         size_t new_index) override {
     ++moved_count_;
     observer_details_.Set(old_parent, new_parent, old_index, new_index);
   }
 
   void BookmarkNodeAdded(BookmarkModel* model,
                          const BookmarkNode* parent,
-                         int index) override {
+                         size_t index) override {
     ++added_count_;
-    observer_details_.Set(parent, nullptr, index, -1);
+    observer_details_.Set(parent, nullptr, index, size_t{-1});
   }
 
   void OnWillRemoveBookmarks(BookmarkModel* model,
                              const BookmarkNode* parent,
-                             int old_index,
+                             size_t old_index,
                              const BookmarkNode* node) override {
     ++before_remove_count_;
   }
@@ -313,17 +313,17 @@
 
   void BookmarkNodeRemoved(BookmarkModel* model,
                            const BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override {
     ++removed_count_;
-    observer_details_.Set(parent, nullptr, old_index, -1);
+    observer_details_.Set(parent, nullptr, old_index, size_t{-1});
   }
 
   void BookmarkNodeChanged(BookmarkModel* model,
                            const BookmarkNode* node) override {
     ++changed_count_;
-    observer_details_.Set(node, nullptr, -1, -1);
+    observer_details_.Set(node, nullptr, size_t{-1}, size_t{-1});
   }
 
   void OnWillChangeBookmarkNode(BookmarkModel* model,
@@ -375,7 +375,7 @@
 
   void OnBookmarkNodeRemoved(BookmarkModel* model,
                              const BookmarkNode* parent,
-                             int index,
+                             size_t index,
                              std::unique_ptr<BookmarkNode> node) override {
     node_removal_details_.push_back(
         NodeRemovalDetail(parent, index, node.get()));
@@ -500,7 +500,7 @@
 
   const BookmarkNode* new_node = model_->AddURL(root, 0, title, url);
   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, -1);
+  observer_details_.ExpectEquals(root, nullptr, 0, size_t{-1});
 
   ASSERT_EQ(1, root->child_count());
   ASSERT_EQ(title, new_node->GetTitle());
@@ -521,7 +521,7 @@
 
   const BookmarkNode* new_node = model_->AddURL(root, 0, title, url);
   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, -1);
+  observer_details_.ExpectEquals(root, nullptr, 0, size_t{-1});
 
   ASSERT_EQ(1, root->child_count());
   ASSERT_EQ(title, new_node->GetTitle());
@@ -562,7 +562,7 @@
   const BookmarkNode* new_node = model_->AddURLWithCreationTimeAndMetaInfo(
       root, 0, title, url, time, &meta_info);
   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, -1);
+  observer_details_.ExpectEquals(root, nullptr, 0, size_t{-1});
 
   ASSERT_EQ(1, root->child_count());
   ASSERT_EQ(title, new_node->GetTitle());
@@ -585,7 +585,7 @@
 
   const BookmarkNode* new_node = model_->AddURL(root, 0, title, url);
   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, -1);
+  observer_details_.ExpectEquals(root, nullptr, 0, size_t{-1});
 
   ASSERT_EQ(1, root->child_count());
   ASSERT_EQ(title, new_node->GetTitle());
@@ -604,7 +604,7 @@
 
   const BookmarkNode* new_node = model_->AddFolder(root, 0, title);
   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, -1);
+  observer_details_.ExpectEquals(root, nullptr, 0, size_t{-1});
 
   ASSERT_EQ(1, root->child_count());
   ASSERT_EQ(title, new_node->GetTitle());
@@ -618,7 +618,7 @@
   ClearCounts();
   model_->AddFolder(root, 0, title);
   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, -1);
+  observer_details_.ExpectEquals(root, nullptr, 0, size_t{-1});
 }
 
 TEST_F(BookmarkModelTest, AddFolderWithWhitespaceTitle) {
@@ -647,7 +647,7 @@
   model_->Remove(root->GetChild(0));
   ASSERT_EQ(0, root->child_count());
   AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, -1);
+  observer_details_.ExpectEquals(root, nullptr, 0, size_t{-1});
 
   // Make sure there is no mapping for the URL.
   ASSERT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == nullptr);
@@ -670,7 +670,7 @@
   model_->Remove(root->GetChild(0));
   ASSERT_EQ(0, root->child_count());
   AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, -1);
+  observer_details_.ExpectEquals(root, nullptr, 0, size_t{-1});
 
   // Make sure there is no mapping for the URL.
   ASSERT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == nullptr);
@@ -730,7 +730,7 @@
   title = ASCIIToUTF16("foo2");
   model_->SetTitle(node, title);
   AssertObserverCount(0, 0, 0, 1, 0, 0, 1, 0, 0);
-  observer_details_.ExpectEquals(node, nullptr, -1, -1);
+  observer_details_.ExpectEquals(node, nullptr, size_t{-1}, size_t{-1});
   EXPECT_EQ(title, node->GetTitle());
 }
 
@@ -759,7 +759,7 @@
   url = GURL("http://foo2.com");
   model_->SetURL(node, url);
   AssertObserverCount(0, 0, 0, 1, 0, 0, 1, 0, 0);
-  observer_details_.ExpectEquals(node, nullptr, -1, -1);
+  observer_details_.ExpectEquals(node, nullptr, size_t{-1}, size_t{-1});
   EXPECT_EQ(url, node->url());
 }
 
@@ -800,7 +800,7 @@
   ClearCounts();
   model_->Remove(root->GetChild(0));
   AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, -1);
+  observer_details_.ExpectEquals(root, nullptr, 0, size_t{-1});
   EXPECT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == nullptr);
   EXPECT_EQ(0, root->child_count());
 }
@@ -1369,17 +1369,17 @@
 
   void BookmarkNodeMoved(BookmarkModel* model,
                          const BookmarkNode* old_parent,
-                         int old_index,
+                         size_t old_index,
                          const BookmarkNode* new_parent,
-                         int new_index) override {}
+                         size_t new_index) override {}
 
   void BookmarkNodeAdded(BookmarkModel* model,
                          const BookmarkNode* parent,
-                         int index) override {}
+                         size_t index) override {}
 
   void BookmarkNodeRemoved(BookmarkModel* model,
                            const BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override {}
 
diff --git a/components/bookmarks/browser/bookmark_undo_delegate.h b/components/bookmarks/browser/bookmark_undo_delegate.h
index a5e2142c..89d6908 100644
--- a/components/bookmarks/browser/bookmark_undo_delegate.h
+++ b/components/bookmarks/browser/bookmark_undo_delegate.h
@@ -25,7 +25,7 @@
   // Called when |node| was removed from |parent| at position |index|.
   virtual void OnBookmarkNodeRemoved(BookmarkModel* model,
                                      const BookmarkNode* parent,
-                                     int index,
+                                     size_t index,
                                      std::unique_ptr<BookmarkNode> node) = 0;
 };
 
diff --git a/components/bookmarks/browser/bookmark_undo_provider.h b/components/bookmarks/browser/bookmark_undo_provider.h
index f6edfe4..72aab21 100644
--- a/components/bookmarks/browser/bookmark_undo_provider.h
+++ b/components/bookmarks/browser/bookmark_undo_provider.h
@@ -17,7 +17,7 @@
   // Restores the previously removed |node| at |parent| in the specified
   // |index|.
   virtual void RestoreRemovedNode(const BookmarkNode* parent,
-                                  int index,
+                                  size_t index,
                                   std::unique_ptr<BookmarkNode> node) = 0;
 
  protected:
diff --git a/components/bookmarks/browser/bookmark_utils.cc b/components/bookmarks/browser/bookmark_utils.cc
index 3d389005..b14e122 100644
--- a/components/bookmarks/browser/bookmark_utils.cc
+++ b/components/bookmarks/browser/bookmark_utils.cc
@@ -46,7 +46,7 @@
 void CloneBookmarkNodeImpl(BookmarkModel* model,
                            const BookmarkNodeData::Element& element,
                            const BookmarkNode* parent,
-                           int index_to_add_at,
+                           size_t index_to_add_at,
                            bool reset_node_times) {
   // Make sure to not copy non clonable keys.
   BookmarkNode::MetaInfoMap meta_info_map = element.meta_info_map;
@@ -231,15 +231,14 @@
 void CloneBookmarkNode(BookmarkModel* model,
                        const std::vector<BookmarkNodeData::Element>& elements,
                        const BookmarkNode* parent,
-                       int index_to_add_at,
+                       size_t index_to_add_at,
                        bool reset_node_times) {
   if (!parent->is_folder() || !model) {
     NOTREACHED();
     return;
   }
   for (size_t i = 0; i < elements.size(); ++i) {
-    CloneBookmarkNodeImpl(model, elements[i], parent,
-                          index_to_add_at + static_cast<int>(i),
+    CloneBookmarkNodeImpl(model, elements[i], parent, index_to_add_at + i,
                           reset_node_times);
   }
 }
@@ -303,7 +302,7 @@
 
 void PasteFromClipboard(BookmarkModel* model,
                         const BookmarkNode* parent,
-                        int index) {
+                        size_t index) {
   if (!parent)
     return;
 
@@ -316,8 +315,7 @@
     node.SetTitle(base::ASCIIToUTF16(url.spec()));
     bookmark_data = BookmarkNodeData(&node);
   }
-  if (index == -1)
-    index = parent->child_count();
+  DCHECK_LE(index, parent->children().size());
   ScopedGroupBookmarkActions group_paste(model);
 
   if (bookmark_data.size() == 1 &&
@@ -468,7 +466,7 @@
 const BookmarkNode* GetParentForNewNodes(
     const BookmarkNode* parent,
     const std::vector<const BookmarkNode*>& selection,
-    int* index) {
+    size_t* index) {
   const BookmarkNode* real_parent = parent;
 
   if (selection.size() == 1 && selection[0]->is_folder())
@@ -476,10 +474,10 @@
 
   if (index) {
     if (selection.size() == 1 && selection[0]->is_url()) {
-      *index = real_parent->GetIndexOf(selection[0]) + 1;
-      DCHECK_NE(0, *index);
+      *index = size_t{real_parent->GetIndexOf(selection[0]) + 1};
+      DCHECK_NE(0u, *index);
     } else {
-      *index = real_parent->child_count();
+      *index = real_parent->children().size();
     }
   }
 
@@ -505,7 +503,7 @@
     return;  // Nothing to do, a user bookmark with that url already exists.
   model->client()->RecordAction(base::UserMetricsAction("BookmarkAdded"));
   const BookmarkNode* parent = GetParentForNewNodes(model);
-  model->AddURL(parent, parent->child_count(), title, url);
+  model->AddURL(parent, parent->children().size(), title, url);
 }
 
 void RemoveAllBookmarks(BookmarkModel* model, const GURL& url) {
diff --git a/components/bookmarks/browser/bookmark_utils.h b/components/bookmarks/browser/bookmark_utils.h
index c549e2c..59a606a 100644
--- a/components/bookmarks/browser/bookmark_utils.h
+++ b/components/bookmarks/browser/bookmark_utils.h
@@ -48,7 +48,7 @@
 void CloneBookmarkNode(BookmarkModel* model,
                        const std::vector<BookmarkNodeData::Element>& elements,
                        const BookmarkNode* parent,
-                       int index_to_add_at,
+                       size_t index_to_add_at,
                        bool reset_node_times);
 
 // Copies nodes onto the clipboard. If |remove_nodes| is true the nodes are
@@ -60,10 +60,10 @@
 
 // Pastes from the clipboard. The new nodes are added to |parent|, unless
 // |parent| is null in which case this does nothing. The nodes are inserted
-// at |index|. If |index| is -1 the nodes are added to the end.
+// at |index|.
 void PasteFromClipboard(BookmarkModel* model,
                         const BookmarkNode* parent,
-                        int index);
+                        size_t index);
 
 // Returns true if the user can copy from the pasteboard.
 bool CanPasteFromClipboard(BookmarkModel* model, const BookmarkNode* node);
@@ -103,7 +103,7 @@
 const BookmarkNode* GetParentForNewNodes(
     const BookmarkNode* parent,
     const std::vector<const BookmarkNode*>& selection,
-    int* index);
+    size_t* index);
 
 // Deletes the bookmark folders for the given list of |ids|.
 void DeleteBookmarkFolders(BookmarkModel* model,
diff --git a/components/bookmarks/browser/bookmark_utils_unittest.cc b/components/bookmarks/browser/bookmark_utils_unittest.cc
index d750236..60884f1a 100644
--- a/components/bookmarks/browser/bookmark_utils_unittest.cc
+++ b/components/bookmarks/browser/bookmark_utils_unittest.cc
@@ -455,11 +455,11 @@
   // folder.
   std::vector<const BookmarkNode*> nodes;
   nodes.push_back(model->bookmark_bar_node());
-  int index = -1;
+  size_t index = size_t{-1};
   const BookmarkNode* real_parent =
       GetParentForNewNodes(model->bookmark_bar_node(), nodes, &index);
   EXPECT_EQ(real_parent, model->bookmark_bar_node());
-  EXPECT_EQ(0, index);
+  EXPECT_EQ(0u, index);
 
   nodes.clear();
 
@@ -472,7 +472,7 @@
   nodes.push_back(page1);
   real_parent = GetParentForNewNodes(model->bookmark_bar_node(), nodes, &index);
   EXPECT_EQ(real_parent, model->bookmark_bar_node());
-  EXPECT_EQ(1, index);
+  EXPECT_EQ(1u, index);
 
   // This tests the case where selection has more than one item.
   const BookmarkNode* folder1 =
@@ -480,13 +480,13 @@
   nodes.push_back(folder1);
   real_parent = GetParentForNewNodes(model->bookmark_bar_node(), nodes, &index);
   EXPECT_EQ(real_parent, model->bookmark_bar_node());
-  EXPECT_EQ(2, index);
+  EXPECT_EQ(2u, index);
 
   // This tests the case where selection doesn't contain any items.
   nodes.clear();
   real_parent = GetParentForNewNodes(model->bookmark_bar_node(), nodes, &index);
   EXPECT_EQ(real_parent, model->bookmark_bar_node());
-  EXPECT_EQ(2, index);
+  EXPECT_EQ(2u, index);
 }
 
 // Verifies that meta info is copied when nodes are cloned.
diff --git a/components/bookmarks/browser/url_index.cc b/components/bookmarks/browser/url_index.cc
index 4b87500..1af9762 100644
--- a/components/bookmarks/browser/url_index.cc
+++ b/components/bookmarks/browser/url_index.cc
@@ -15,7 +15,7 @@
 }
 
 void UrlIndex::Add(BookmarkNode* parent,
-                   int index,
+                   size_t index,
                    std::unique_ptr<BookmarkNode> node) {
   base::AutoLock url_lock(url_lock_);
   AddImpl(parent->Add(std::move(node), index));
@@ -42,7 +42,7 @@
     }
   }
   BookmarkNode* parent = node->parent();
-  return parent->Remove(parent->GetIndexOf(node));
+  return parent->Remove(size_t{parent->GetIndexOf(node)});
 }
 
 void UrlIndex::SetUrl(BookmarkNode* node, const GURL& url) {
diff --git a/components/bookmarks/browser/url_index.h b/components/bookmarks/browser/url_index.h
index 1f7f131..89be4cb 100644
--- a/components/bookmarks/browser/url_index.h
+++ b/components/bookmarks/browser/url_index.h
@@ -40,7 +40,9 @@
   BookmarkNode* root() { return root_.get(); }
 
   // Adds |node| to |parent| at |index|.
-  void Add(BookmarkNode* parent, int index, std::unique_ptr<BookmarkNode> node);
+  void Add(BookmarkNode* parent,
+           size_t index,
+           std::unique_ptr<BookmarkNode> node);
 
   // Removes |node| and all its descendants from the map, returns the set of
   // urls that are no longer contained in the index.
diff --git a/components/bookmarks/managed/managed_bookmarks_tracker.cc b/components/bookmarks/managed/managed_bookmarks_tracker.cc
index 53ea480..3e744d6 100644
--- a/components/bookmarks/managed/managed_bookmarks_tracker.cc
+++ b/components/bookmarks/managed/managed_bookmarks_tracker.cc
@@ -116,7 +116,7 @@
 
 void ManagedBookmarksTracker::UpdateBookmarks(const BookmarkNode* folder,
                                               const base::ListValue* list) {
-  int folder_index = 0;
+  size_t folder_index = 0;
   for (size_t i = 0; i < list->GetSize(); ++i) {
     // Extract the data for the next bookmark from the |list|.
     base::string16 title;
@@ -129,32 +129,23 @@
 
     // Look for a bookmark at |folder_index| or ahead that matches the current
     // bookmark from the pref.
-    const BookmarkNode* existing = nullptr;
-    for (int k = folder_index; k < folder->child_count(); ++k) {
-      const BookmarkNode* node = folder->GetChild(k);
-      if (node->GetTitle() == title &&
-          ((children && node->is_folder()) ||
-           (!children && node->url() == url))) {
-        existing = node;
-        break;
-      }
-    }
-
-    if (existing) {
+    const auto matches_current = [&title, &url, children](const auto& node) {
+      return node->GetTitle() == title &&
+             (children ? node->is_folder() : (node->url() == url));
+    };
+    const auto j = std::find_if(folder->children().cbegin() + folder_index,
+                                folder->children().cend(), matches_current);
+    if (j != folder->children().cend()) {
       // Reuse the existing node. The Move() is a nop if |existing| is already
       // at |folder_index|.
+      const BookmarkNode* existing = j->get();
       model_->Move(existing, folder, folder_index);
       if (children)
         UpdateBookmarks(existing, children);
+    } else if (children) {
+      UpdateBookmarks(model_->AddFolder(folder, folder_index, title), children);
     } else {
-      // Create a new node for this bookmark now.
-      if (children) {
-        const BookmarkNode* sub =
-            model_->AddFolder(folder, folder_index, title);
-        UpdateBookmarks(sub, children);
-      } else {
-        model_->AddURL(folder, folder_index, title, url);
-      }
+      model_->AddURL(folder, folder_index, title, url);
     }
 
     // The |folder_index| index of |folder| has been updated, so advance it.
@@ -162,8 +153,8 @@
   }
 
   // Remove any extra children of |folder| that haven't been reused.
-  while (folder->child_count() != folder_index)
-    model_->Remove(folder->GetChild(folder_index));
+  while (folder->children().size() != folder_index)
+    model_->Remove(folder->children()[folder_index].get());
 }
 
 // static
diff --git a/components/bookmarks/test/bookmark_test_helpers.cc b/components/bookmarks/test/bookmark_test_helpers.cc
index 3379465..98a2283 100644
--- a/components/bookmarks/test/bookmark_test_helpers.cc
+++ b/components/bookmarks/test/bookmark_test_helpers.cc
@@ -57,7 +57,7 @@
                                           const std::string& model_string,
                                           std::string::size_type start_pos) {
   DCHECK(node);
-  int index = node->child_count();
+  size_t index = node->children().size();
   static const std::string folder_tell(":[");
   std::string::size_type end_pos = model_string.find(' ', start_pos);
   while (end_pos != std::string::npos) {
diff --git a/components/bookmarks/test/mock_bookmark_model_observer.h b/components/bookmarks/test/mock_bookmark_model_observer.h
index 7228d2f..7391c92 100644
--- a/components/bookmarks/test/mock_bookmark_model_observer.h
+++ b/components/bookmarks/test/mock_bookmark_model_observer.h
@@ -18,21 +18,22 @@
 
   MOCK_METHOD2(BookmarkModelLoaded, void(BookmarkModel*, bool));
 
-  MOCK_METHOD5(BookmarkNodeMoved, void(BookmarkModel*,
-                                       const BookmarkNode*,
-                                       int,
-                                       const BookmarkNode*,
-                                       int));
+  MOCK_METHOD5(BookmarkNodeMoved,
+               void(BookmarkModel*,
+                    const BookmarkNode*,
+                    size_t,
+                    const BookmarkNode*,
+                    size_t));
 
-  MOCK_METHOD3(BookmarkNodeAdded, void(BookmarkModel*,
-                                       const BookmarkNode*,
-                                       int));
+  MOCK_METHOD3(BookmarkNodeAdded,
+               void(BookmarkModel*, const BookmarkNode*, size_t));
 
-  MOCK_METHOD5(BookmarkNodeRemoved, void(BookmarkModel*,
-                                         const BookmarkNode*,
-                                         int,
-                                         const BookmarkNode*,
-                                         const std::set<GURL>&));
+  MOCK_METHOD5(BookmarkNodeRemoved,
+               void(BookmarkModel*,
+                    const BookmarkNode*,
+                    size_t,
+                    const BookmarkNode*,
+                    const std::set<GURL>&));
 
   MOCK_METHOD2(BookmarkNodeChanged, void(BookmarkModel*, const BookmarkNode*));
 
diff --git a/components/browser_sync/BUILD.gn b/components/browser_sync/BUILD.gn
index 91e2fbf..e41eaf2 100644
--- a/components/browser_sync/BUILD.gn
+++ b/components/browser_sync/BUILD.gn
@@ -41,7 +41,6 @@
   testonly = true
 
   sources = [
-    "profile_sync_service_autofill_unittest.cc",
     "profile_sync_service_bookmark_unittest.cc",
   ]
 
@@ -68,7 +67,6 @@
     "//components/version_info",
     "//components/version_info:generate_version_info",
     "//components/webdata/common",
-    "//components/webdata_services:test_support",
     "//services/identity/public/cpp:test_support",
     "//services/network:test_support",
     "//testing/gmock",
@@ -79,8 +77,6 @@
 static_library("test_support") {
   testonly = true
   sources = [
-    "abstract_profile_sync_service_test.cc",
-    "abstract_profile_sync_service_test.h",
     "test_http_bridge_factory.cc",
     "test_http_bridge_factory.h",
   ]
diff --git a/components/browser_sync/abstract_profile_sync_service_test.cc b/components/browser_sync/abstract_profile_sync_service_test.cc
deleted file mode 100644
index 3ca7428b..0000000
--- a/components/browser_sync/abstract_profile_sync_service_test.cc
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/browser_sync/abstract_profile_sync_service_test.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/files/file_path.h"
-#include "base/location.h"
-#include "base/run_loop.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "components/browser_sync/test_http_bridge_factory.h"
-#include "components/sync/driver/glue/sync_engine_backend.h"
-#include "components/sync/driver/sync_api_component_factory_mock.h"
-#include "components/sync/driver/test_profile_sync_service.h"
-#include "components/sync/engine/sync_manager_factory_for_profile_sync_test.h"
-#include "components/sync/engine/test_engine_components_factory.h"
-#include "components/sync/protocol/sync.pb.h"
-#include "components/sync/syncable/test_user_share.h"
-#include "services/network/test/test_network_connection_tracker.h"
-
-using syncer::ModelType;
-using syncer::SyncEngineImpl;
-using testing::_;
-using testing::ByMove;
-using testing::Return;
-
-namespace browser_sync {
-
-namespace {
-
-std::unique_ptr<syncer::HttpPostProviderFactory> GetHttpPostProviderFactory(
-    syncer::CancelationSignal* signal) {
-  return std::make_unique<TestHttpBridgeFactory>();
-}
-
-class SyncEngineForProfileSyncTest : public SyncEngineImpl {
- public:
-  SyncEngineForProfileSyncTest(
-      const base::FilePath& temp_dir,
-      invalidation::InvalidationService* invalidator,
-      const base::WeakPtr<syncer::SyncPrefs>& sync_prefs,
-      base::OnceClosure callback);
-  ~SyncEngineForProfileSyncTest() override;
-
-  void Initialize(InitParams params) override;
-
-  void ConfigureDataTypes(ConfigureParams params) override;
-
- private:
-  // Invoked at the start of HandleSyncManagerInitializationOnFrontendLoop.
-  // Allows extra initialization work to be performed before the engine comes
-  // up.
-  base::OnceClosure callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(SyncEngineForProfileSyncTest);
-};
-
-SyncEngineForProfileSyncTest::SyncEngineForProfileSyncTest(
-    const base::FilePath& temp_dir,
-    invalidation::InvalidationService* invalidator,
-    const base::WeakPtr<syncer::SyncPrefs>& sync_prefs,
-    base::OnceClosure callback)
-    : SyncEngineImpl(
-          "dummy_debug_name",
-          invalidator,
-          sync_prefs,
-          temp_dir.Append(base::FilePath(FILE_PATH_LITERAL("test")))),
-      callback_(std::move(callback)) {}
-
-SyncEngineForProfileSyncTest::~SyncEngineForProfileSyncTest() {}
-
-void SyncEngineForProfileSyncTest::Initialize(InitParams params) {
-  params.http_factory_getter = base::Bind(&GetHttpPostProviderFactory);
-  params.sync_manager_factory =
-      std::make_unique<syncer::SyncManagerFactoryForProfileSyncTest>(
-          std::move(callback_),
-          network::TestNetworkConnectionTracker::GetInstance());
-  params.restored_key_for_bootstrapping.clear();
-
-  // It'd be nice if we avoided creating the EngineComponentsFactory in the
-  // first place, but SyncEngine will have created one by now so we must free
-  // it. Grab the switches to pass on first.
-  syncer::EngineComponentsFactory::Switches factory_switches =
-      params.engine_components_factory->GetSwitches();
-  params.engine_components_factory =
-      std::make_unique<syncer::TestEngineComponentsFactory>(
-          factory_switches, syncer::EngineComponentsFactory::STORAGE_IN_MEMORY,
-          nullptr);
-
-  SyncEngineImpl::Initialize(std::move(params));
-}
-
-void SyncEngineForProfileSyncTest::ConfigureDataTypes(ConfigureParams params) {
-  // The first parameter there should be the set of enabled types.  That's not
-  // something we have access to from this strange test harness.  We'll just
-  // send back the list of newly configured types instead and hope it doesn't
-  // break anything.
-  // Posted to avoid re-entrancy issues.
-  base::SequencedTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &SyncEngineForProfileSyncTest::FinishConfigureDataTypesOnFrontendLoop,
-          base::Unretained(this), params.to_download, params.to_download,
-          syncer::ModelTypeSet(), params.ready_task));
-}
-
-}  // namespace
-
-AbstractProfileSyncServiceTest::AbstractProfileSyncServiceTest()
-    : data_type_thread_("Extra thread") {
-  EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
-}
-
-AbstractProfileSyncServiceTest::~AbstractProfileSyncServiceTest() {
-  sync_service_->Shutdown();
-}
-
-bool AbstractProfileSyncServiceTest::CreateRoot(ModelType model_type) {
-  return syncer::TestUserShare::CreateRoot(model_type,
-                                           sync_service_->GetUserShare());
-}
-
-void AbstractProfileSyncServiceTest::CreateSyncService(
-    std::unique_ptr<syncer::SyncClient> sync_client,
-    base::OnceClosure initialization_success_callback) {
-  ASSERT_TRUE(sync_client);
-  syncer::ProfileSyncService::InitParams init_params =
-      profile_sync_service_bundle_.CreateBasicInitParams(
-          syncer::ProfileSyncService::AUTO_START, std::move(sync_client));
-  sync_service_ =
-      std::make_unique<syncer::TestProfileSyncService>(std::move(init_params));
-
-  syncer::SyncApiComponentFactoryMock* components =
-      profile_sync_service_bundle_.component_factory();
-  auto engine = std::make_unique<SyncEngineForProfileSyncTest>(
-      temp_dir_.GetPath(),
-      profile_sync_service_bundle_.fake_invalidation_service(),
-      sync_service_->sync_prefs()->AsWeakPtr(),
-      std::move(initialization_success_callback));
-  EXPECT_CALL(*components, CreateSyncEngine(_, _, _))
-      .WillOnce(Return(ByMove(std::move(engine))));
-
-  sync_service_->sync_prefs()->SetSyncRequested(true);
-  sync_service_->sync_prefs()->SetFirstSetupComplete();
-}
-
-CreateRootHelper::CreateRootHelper(AbstractProfileSyncServiceTest* test,
-                                   ModelType model_type)
-    : callback_(base::Bind(&CreateRootHelper::CreateRootCallback,
-                           base::Unretained(this))),
-      test_(test),
-      model_type_(model_type),
-      success_(false) {}
-
-CreateRootHelper::~CreateRootHelper() {}
-
-const base::Closure& CreateRootHelper::callback() const {
-  return callback_;
-}
-
-bool CreateRootHelper::success() {
-  return success_;
-}
-
-void CreateRootHelper::CreateRootCallback() {
-  success_ = test_->CreateRoot(model_type_);
-}
-
-}  // namespace browser_sync
diff --git a/components/browser_sync/abstract_profile_sync_service_test.h b/components/browser_sync/abstract_profile_sync_service_test.h
deleted file mode 100644
index deb622f..0000000
--- a/components/browser_sync/abstract_profile_sync_service_test.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_BROWSER_SYNC_ABSTRACT_PROFILE_SYNC_SERVICE_TEST_H_
-#define COMPONENTS_BROWSER_SYNC_ABSTRACT_PROFILE_SYNC_SERVICE_TEST_H_
-
-#include <memory>
-
-#include "base/callback.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/macros.h"
-#include "base/test/scoped_task_environment.h"
-#include "components/sync/base/model_type.h"
-#include "components/sync/driver/profile_sync_service_bundle.h"
-#include "components/sync/syncable/change_record.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace syncer {
-class TestProfileSyncService;
-}  // namespace syncer
-
-namespace browser_sync {
-
-class AbstractProfileSyncServiceTest : public testing::Test {
- public:
-  AbstractProfileSyncServiceTest();
-  ~AbstractProfileSyncServiceTest() override;
-
-  bool CreateRoot(syncer::ModelType model_type);
-
- protected:
-  // Creates a TestProfileSyncService instance based on
-  // |profile_sync_service_bundle_|, with start behavior AUTO_START. Passes
-  // |callback| down to SyncManagerForProfileSyncTest to be used by
-  // NotifyInitializationSuccess. |sync_client| is passed to the service. The
-  // created service is stored in |sync_service_|.
-  void CreateSyncService(std::unique_ptr<syncer::SyncClient> sync_client,
-                         base::OnceClosure initialization_success_callback);
-
-  base::Thread* data_type_thread() { return &data_type_thread_; }
-
-  syncer::TestProfileSyncService* sync_service() { return sync_service_.get(); }
-
-  syncer::ProfileSyncServiceBundle* profile_sync_service_bundle() {
-    return &profile_sync_service_bundle_;
-  }
-
- private:
-  // Use |data_type_thread_| for code disallowed on the UI thread.
-  base::Thread data_type_thread_;
-
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-  syncer::ProfileSyncServiceBundle profile_sync_service_bundle_;
-  std::unique_ptr<syncer::TestProfileSyncService> sync_service_;
-
-  base::ScopedTempDir temp_dir_;  // To pass to the backend host.
-
-  DISALLOW_COPY_AND_ASSIGN(AbstractProfileSyncServiceTest);
-};
-
-class CreateRootHelper {
- public:
-  CreateRootHelper(AbstractProfileSyncServiceTest* test,
-                   syncer::ModelType model_type);
-  virtual ~CreateRootHelper();
-
-  const base::Closure& callback() const;
-  bool success();
-
- private:
-  void CreateRootCallback();
-
-  base::Closure callback_;
-  AbstractProfileSyncServiceTest* test_;
-  syncer::ModelType model_type_;
-  bool success_;
-
-  DISALLOW_COPY_AND_ASSIGN(CreateRootHelper);
-};
-
-}  // namespace browser_sync
-
-#endif  // COMPONENTS_BROWSER_SYNC_ABSTRACT_PROFILE_SYNC_SERVICE_TEST_H_
diff --git a/components/browser_sync/profile_sync_service_autofill_unittest.cc b/components/browser_sync/profile_sync_service_autofill_unittest.cc
deleted file mode 100644
index b1a9e568..0000000
--- a/components/browser_sync/profile_sync_service_autofill_unittest.cc
+++ /dev/null
@@ -1,1010 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <memory>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/callback.h"
-#include "base/location.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/run_loop.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/string16.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/test/bind_test_util.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/geo/country_names.h"
-#include "components/autofill/core/browser/personal_data_manager.h"
-#include "components/autofill/core/browser/webdata/autofill_change.h"
-#include "components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h"
-#include "components/autofill/core/browser/webdata/autofill_profile_syncable_service.h"
-#include "components/autofill/core/browser/webdata/autofill_table.h"
-#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
-#include "components/autofill/core/common/autofill_prefs.h"
-#include "components/browser_sync/abstract_profile_sync_service_test.h"
-#include "components/sync/base/model_type.h"
-#include "components/sync/driver/data_type_controller.h"
-#include "components/sync/driver/data_type_manager_impl.h"
-#include "components/sync/driver/profile_sync_service.h"
-#include "components/sync/driver/sync_api_component_factory_mock.h"
-#include "components/sync/driver/sync_client_mock.h"
-#include "components/sync/driver/test_profile_sync_service.h"
-#include "components/sync/engine/data_type_debug_info_listener.h"
-#include "components/sync/engine/sequenced_model_worker.h"
-#include "components/sync/protocol/autofill_specifics.pb.h"
-#include "components/sync/syncable/mutable_entry.h"
-#include "components/sync/syncable/read_node.h"
-#include "components/sync/syncable/read_transaction.h"
-#include "components/sync/syncable/syncable_write_transaction.h"
-#include "components/sync/syncable/write_node.h"
-#include "components/sync/syncable/write_transaction.h"
-#include "components/sync_preferences/pref_service_syncable.h"
-#include "components/version_info/version_info.h"
-#include "components/webdata/common/web_database.h"
-#include "components/webdata_services/web_data_service_test_util.h"
-#include "services/identity/public/cpp/identity_test_utils.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using autofill::AutofillChange;
-using autofill::AutofillChangeList;
-using autofill::AutofillKey;
-using autofill::AutofillProfile;
-using autofill::AutofillProfileChange;
-using autofill::AutofillProfileSyncableService;
-using autofill::AutofillTable;
-using autofill::AutofillWebDataService;
-using autofill::NAME_FULL;
-using autofill::PersonalDataManager;
-using autofill::ServerFieldType;
-using base::ASCIIToUTF16;
-using base::Time;
-using base::TimeDelta;
-using base::WaitableEvent;
-using syncer::AUTOFILL_PROFILE;
-using syncer::BaseNode;
-using syncer::syncable::CREATE;
-using syncer::syncable::GET_TYPE_ROOT;
-using syncer::syncable::MutableEntry;
-using syncer::syncable::UNITTEST;
-using syncer::syncable::WriterTag;
-using syncer::syncable::WriteTransaction;
-using testing::_;
-using testing::ByMove;
-using testing::DoAll;
-using testing::ElementsAre;
-using testing::Not;
-using testing::Return;
-using testing::SetArgPointee;
-
-namespace browser_sync {
-
-namespace {
-
-void RegisterAutofillPrefs(user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterBooleanPref(autofill::prefs::kAutofillCreditCardEnabled,
-                                true);
-  registry->RegisterBooleanPref(autofill::prefs::kAutofillProfileEnabled, true);
-  registry->RegisterBooleanPref(autofill::prefs::kAutofillWalletImportEnabled,
-                                true);
-  registry->RegisterIntegerPref(autofill::prefs::kAutofillLastVersionDeduped,
-                                atoi(version_info::GetVersionNumber().c_str()));
-  registry->RegisterIntegerPref(autofill::prefs::kAutofillLastVersionValidated,
-                                atoi(version_info::GetVersionNumber().c_str()));
-  registry->RegisterBooleanPref(
-      autofill::prefs::kAutofillJapanCityFieldMigrated, true);
-  registry->RegisterBooleanPref(autofill::prefs::kAutofillOrphanRowsRemoved,
-                                true);
-  registry->RegisterIntegerPref(
-      autofill::prefs::kAutofillLastVersionDisusedAddressesDeleted, 0);
-  registry->RegisterIntegerPref(
-      autofill::prefs::kAutofillLastVersionDisusedCreditCardsDeleted, 0);
-  registry->RegisterStringPref(
-      autofill::prefs::kAutofillProfileValidity, "",
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
-}
-
-void RunAndSignal(base::OnceClosure cb, WaitableEvent* event) {
-  std::move(cb).Run();
-  event->Signal();
-}
-
-}  // namespace
-
-class AutofillTableMock : public AutofillTable {
- public:
-  AutofillTableMock() {}
-  MOCK_METHOD2(RemoveFormElement,
-               bool(const base::string16& name,
-                    const base::string16& value));  // NOLINT
-  MOCK_METHOD4(GetAutofillTimestamps,
-               bool(const base::string16& name,  // NOLINT
-                    const base::string16& value,
-                    Time* date_created,
-                    Time* date_last_used));
-  MOCK_METHOD1(GetAutofillProfiles,
-               bool(std::vector<std::unique_ptr<AutofillProfile>>*));  // NOLINT
-  MOCK_METHOD1(UpdateAutofillProfile, bool(const AutofillProfile&));   // NOLINT
-  MOCK_METHOD1(AddAutofillProfile, bool(const AutofillProfile&));      // NOLINT
-  MOCK_METHOD1(RemoveAutofillProfile, bool(const std::string&));       // NOLINT
-
-  // TODO(crbug.com/904390): Remove when the investigation is over.
-  MOCK_CONST_METHOD1(
-      GetServerProfiles,
-      bool(std::vector<std::unique_ptr<AutofillProfile>>*));  // NOLINT
-};
-
-MATCHER_P(MatchProfiles, profile, "") {
-  return (profile.Compare(arg) == 0);
-}
-
-ACTION_P(LoadAutofillProfiles, datafunc) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles =
-      std::move(datafunc());
-  arg0->swap(profiles);
-}
-
-class WebDatabaseFake : public WebDatabase {
- public:
-  explicit WebDatabaseFake(AutofillTable* autofill_table) {
-    AddTable(autofill_table);
-  }
-};
-
-class FakeAutofillBackend : public autofill::AutofillWebDataBackend {
- public:
-  FakeAutofillBackend(
-      WebDatabase* web_database,
-      const base::RepeatingClosure& on_changed,
-      const base::RepeatingCallback<void(syncer::ModelType)>& on_sync_started,
-      const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner)
-      : web_database_(web_database),
-        on_changed_(on_changed),
-        on_sync_started_(on_sync_started),
-        ui_task_runner_(ui_task_runner) {}
-
-  ~FakeAutofillBackend() override {}
-  WebDatabase* GetDatabase() override { return web_database_; }
-  void AddObserver(
-      autofill::AutofillWebDataServiceObserverOnDBSequence* observer) override {
-  }
-  void RemoveObserver(
-      autofill::AutofillWebDataServiceObserverOnDBSequence* observer) override {
-  }
-  void CommitChanges() override {}
-
-  void NotifyOfAutofillProfileChanged(
-      const autofill::AutofillProfileChange& change) override {}
-  void NotifyOfCreditCardChanged(
-      const autofill::CreditCardChange& change) override {}
-  void NotifyOfMultipleAutofillChanges() override {
-    DCHECK(!ui_task_runner_->RunsTasksInCurrentSequence());
-    ui_task_runner_->PostTask(FROM_HERE, on_changed_);
-  }
-  void NotifyOfAddressConversionCompleted() override {}
-  void NotifyThatSyncHasStarted(syncer::ModelType model_type) override {
-    DCHECK(!ui_task_runner_->RunsTasksInCurrentSequence());
-    ui_task_runner_->PostTask(FROM_HERE,
-                              base::BindOnce(on_sync_started_, model_type));
-  }
-
- private:
-  WebDatabase* web_database_;
-  base::RepeatingClosure on_changed_;
-  base::RepeatingCallback<void(syncer::ModelType)> on_sync_started_;
-  const scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
-};
-
-class ProfileSyncServiceAutofillTest;
-
-class TokenWebDataServiceFake : public TokenWebData {
- public:
-  TokenWebDataServiceFake(
-      const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
-      const scoped_refptr<base::SingleThreadTaskRunner>& db_task_runner)
-      : TokenWebData(ui_task_runner, db_task_runner) {}
-
-  bool IsDatabaseLoaded() override { return true; }
-
-  AutofillWebDataService::Handle GetAllTokens(
-      WebDataServiceConsumer* consumer) override {
-    // TODO(tim): It would be nice if WebDataService was injected on
-    // construction of ProfileOAuth2TokenService rather than fetched by
-    // Initialize so that this isn't necessary (we could pass a null service).
-    // We currently do return it via EXPECT_CALLs, but without depending on
-    // order-of-initialization (which seems way more fragile) we can't tell
-    // which component is asking at what time, and some components in these
-    // Autofill tests require a WebDataService.
-    return 0;
-  }
-
- private:
-  ~TokenWebDataServiceFake() override {}
-
-  DISALLOW_COPY_AND_ASSIGN(TokenWebDataServiceFake);
-};
-
-class WebDataServiceFake : public AutofillWebDataService {
- public:
-  WebDataServiceFake(
-      const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
-      const scoped_refptr<base::SingleThreadTaskRunner>& db_task_runner)
-      : AutofillWebDataService(ui_task_runner, db_task_runner),
-        web_database_(nullptr),
-        autofill_profile_syncable_service_(nullptr),
-        syncable_service_created_or_destroyed_(
-            base::WaitableEvent::ResetPolicy::AUTOMATIC,
-            base::WaitableEvent::InitialState::NOT_SIGNALED),
-        db_task_runner_(db_task_runner),
-        ui_task_runner_(ui_task_runner) {}
-
-  void SetDatabase(WebDatabase* web_database) { web_database_ = web_database; }
-
-  void StartSyncableService() {
-    // The |autofill_profile_syncable_service_| must be constructed on the DB
-    // sequence.
-    const base::RepeatingClosure& on_changed_callback = base::BindRepeating(
-        &WebDataServiceFake::NotifyAutofillMultipleChangedOnUISequence,
-        AsWeakPtr());
-    const base::RepeatingCallback<void(syncer::ModelType)>&
-        on_sync_started_callback = base::BindRepeating(
-            &WebDataServiceFake::NotifySyncStartedOnUISequence, AsWeakPtr());
-
-    db_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&WebDataServiceFake::CreateSyncableService,
-                                  base::Unretained(this), on_changed_callback,
-                                  std::move(on_sync_started_callback)));
-    syncable_service_created_or_destroyed_.Wait();
-  }
-
-  void ShutdownSyncableService() {
-    // The |autofill_profile_syncable_service_| must be destructed on the DB
-    // sequence.
-    db_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&WebDataServiceFake::DestroySyncableService,
-                                  base::Unretained(this)));
-    syncable_service_created_or_destroyed_.Wait();
-  }
-
-  bool IsDatabaseLoaded() override { return true; }
-
-  WebDatabase* GetDatabase() override { return web_database_; }
-
-  void OnAutofillProfileChanged(const AutofillProfileChange& changes) {
-    WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
-                        base::WaitableEvent::InitialState::NOT_SIGNALED);
-
-    base::OnceClosure notify_cb = base::BindOnce(
-        &AutofillProfileSyncableService::AutofillProfileChanged,
-        base::Unretained(autofill_profile_syncable_service_), changes);
-    db_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&RunAndSignal, std::move(notify_cb), &event));
-    event.Wait();
-  }
-
- private:
-  ~WebDataServiceFake() override {}
-
-  void CreateSyncableService(
-      const base::RepeatingClosure& on_changed_callback,
-      const base::RepeatingCallback<void(syncer::ModelType)>& on_sync_started) {
-    ASSERT_TRUE(db_task_runner_->RunsTasksInCurrentSequence());
-    // These services are deleted in DestroySyncableService().
-    backend_ = std::make_unique<FakeAutofillBackend>(
-        GetDatabase(), on_changed_callback, on_sync_started,
-        ui_task_runner_.get());
-    AutofillProfileSyncableService::CreateForWebDataServiceAndBackend(
-        this, backend_.get(), "en-US");
-
-    autofill_profile_syncable_service_ =
-        AutofillProfileSyncableService::FromWebDataService(this);
-
-    syncable_service_created_or_destroyed_.Signal();
-  }
-
-  void DestroySyncableService() {
-    ASSERT_TRUE(db_task_runner_->RunsTasksInCurrentSequence());
-    autofill_profile_syncable_service_ = nullptr;
-    backend_.reset();
-    syncable_service_created_or_destroyed_.Signal();
-  }
-
-  WebDatabase* web_database_;
-  AutofillProfileSyncableService* autofill_profile_syncable_service_;
-  std::unique_ptr<autofill::AutofillWebDataBackend> backend_;
-
-  WaitableEvent syncable_service_created_or_destroyed_;
-
-  const scoped_refptr<base::SingleThreadTaskRunner> db_task_runner_;
-  const scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebDataServiceFake);
-};
-
-ACTION_P(ReturnNewDataTypeManagerWithDebugListener, debug_listener) {
-  return std::make_unique<syncer::DataTypeManagerImpl>(arg0, debug_listener,
-                                                       arg2, arg3, arg4, arg5);
-}
-
-class MockPersonalDataManager : public PersonalDataManager {
- public:
-  MockPersonalDataManager() : PersonalDataManager("en-US") {}
-  MOCK_CONST_METHOD0(IsDataLoaded, bool());
-  MOCK_METHOD0(LoadProfiles, void());
-  MOCK_METHOD0(LoadCreditCards, void());
-  MOCK_METHOD0(LoadPaymentsCustomerData, void());
-  MOCK_METHOD0(Refresh, void());
-};
-
-class AddAutofillProfileHelper;
-
-class ProfileSyncServiceAutofillTest
-    : public AbstractProfileSyncServiceTest,
-      public syncer::DataTypeDebugInfoListener {
- public:
-  // DataTypeDebugInfoListener implementation.
-  void OnDataTypeConfigureComplete(
-      const std::vector<syncer::DataTypeConfigurationStats>&
-          configuration_stats) override {
-    for (const syncer::DataTypeConfigurationStats& stat : configuration_stats) {
-      if (stat.model_type == syncer::AUTOFILL_PROFILE) {
-        association_stats_ = stat.association_stats;
-        return;
-      }
-    }
-    ASSERT_TRUE(false) << "Autofill profile type did not get configured!";
-  }
-
- protected:
-  ProfileSyncServiceAutofillTest() : debug_ptr_factory_(this) {
-    autofill::CountryNames::SetLocaleString("en-US");
-    RegisterAutofillPrefs(
-        profile_sync_service_bundle()->pref_service()->registry());
-
-    data_type_thread()->Start();
-
-    web_database_ = std::make_unique<WebDatabaseFake>(&autofill_table_);
-    web_data_wrapper_ = std::make_unique<MockWebDataServiceWrapper>(
-        new WebDataServiceFake(base::ThreadTaskRunnerHandle::Get(),
-                               data_type_thread()->task_runner()),
-        new TokenWebDataServiceFake(base::ThreadTaskRunnerHandle::Get(),
-                                    data_type_thread()->task_runner()));
-    web_data_service_ = static_cast<WebDataServiceFake*>(
-        web_data_wrapper_->GetProfileAutofillWebData().get());
-    web_data_service_->SetDatabase(web_database_.get());
-
-    personal_data_manager_ = std::make_unique<MockPersonalDataManager>();
-
-    EXPECT_CALL(personal_data_manager(), LoadProfiles());
-    EXPECT_CALL(personal_data_manager(), LoadCreditCards());
-    EXPECT_CALL(personal_data_manager(), LoadPaymentsCustomerData());
-
-    personal_data_manager_->Init(web_data_service_,
-                                 /*account_database=*/nullptr,
-                                 profile_sync_service_bundle()->pref_service(),
-                                 /*identity_manager=*/nullptr,
-                                 /*client_profile_validator=*/nullptr,
-                                 /*history_service=*/nullptr,
-                                 /*is_off_the_record=*/false);
-
-    web_data_service_->StartSyncableService();
-  }
-
-  ~ProfileSyncServiceAutofillTest() override {
-    web_data_service_->ShutdownOnUISequence();
-    web_data_service_->ShutdownSyncableService();
-    web_data_wrapper_->Shutdown();
-    web_data_service_ = nullptr;
-    web_data_wrapper_.reset();
-    web_database_.reset();
-    // Shut down the service explicitly before some data members from this
-    // test it needs will be deleted.
-    sync_service()->Shutdown();
-  }
-
-  int GetSyncCount() {
-    syncer::ReadTransaction trans(FROM_HERE, sync_service()->GetUserShare());
-    syncer::ReadNode node(&trans);
-    if (node.InitTypeRoot(AUTOFILL_PROFILE) != BaseNode::INIT_OK)
-      return 0;
-    return node.GetTotalNodeCount() - 1;
-  }
-
-  void StartAutofillProfileSyncService(base::OnceClosure callback) {
-    profile_sync_service_bundle()
-        ->identity_test_env()
-        ->MakePrimaryAccountAvailable("test_user@gmail.com");
-
-    std::unique_ptr<syncer::SyncClientMock> sync_client =
-        profile_sync_service_bundle()->CreateSyncClientMock();
-    syncer::SyncClientMock* sync_client_copy = sync_client.get();
-    CreateSyncService(std::move(sync_client), std::move(callback));
-
-    syncer::DataTypeController::TypeVector controllers;
-    controllers.push_back(std::make_unique<AutofillProfileDataTypeController>(
-        data_type_thread()->task_runner(), /*dump_stack=*/base::DoNothing(),
-        sync_service(), sync_client_copy,
-        base::BindLambdaForTesting([&]() -> autofill::PersonalDataManager* {
-          return personal_data_manager_.get();
-        }),
-        web_data_service_));
-
-    ON_CALL(*sync_client_copy, GetSyncableServiceForType(AUTOFILL_PROFILE))
-        .WillByDefault(testing::Invoke([=](syncer::ModelType) {
-          return AutofillProfileSyncableService::FromWebDataService(
-                     web_data_service_.get())
-              ->AsWeakPtr();
-        }));
-    ON_CALL(*sync_client_copy, CreateDataTypeControllers(_))
-        .WillByDefault(Return(ByMove(std::move(controllers))));
-    ON_CALL(*sync_client_copy, CreateModelWorkerForGroup(syncer::GROUP_DB))
-        .WillByDefault(
-            Return(base::MakeRefCounted<syncer::SequencedModelWorker>(
-                data_type_thread()->task_runner(), syncer::GROUP_DB)));
-
-    ON_CALL(*profile_sync_service_bundle()->component_factory(),
-            CreateDataTypeManager(_, _, _, _, _, _))
-        .WillByDefault(ReturnNewDataTypeManagerWithDebugListener(
-            syncer::MakeWeakHandle(debug_ptr_factory_.GetWeakPtr())));
-
-    EXPECT_CALL(personal_data_manager(), IsDataLoaded())
-        .WillRepeatedly(Return(true));
-
-    sync_service()->Initialize();
-    base::RunLoop().Run();
-
-    // It's possible this test triggered an unrecoverable error, in which case
-    // we can't get the sync count.
-    if (sync_service()->IsSyncFeatureActive()) {
-      EXPECT_EQ(GetSyncCount(),
-                association_stats_.num_sync_items_after_association);
-    }
-    EXPECT_EQ(association_stats_.num_sync_items_after_association,
-              association_stats_.num_sync_items_before_association +
-                  association_stats_.num_sync_items_added -
-                  association_stats_.num_sync_items_deleted);
-  }
-
-  bool AddAutofillSyncNode(const AutofillProfile& profile) {
-    syncer::WriteTransaction trans(FROM_HERE, sync_service()->GetUserShare());
-    syncer::WriteNode node(&trans);
-    std::string tag = profile.guid();
-    syncer::WriteNode::InitUniqueByCreationResult result =
-        node.InitUniqueByCreation(AUTOFILL_PROFILE, tag);
-    if (result != syncer::WriteNode::INIT_SUCCESS)
-      return false;
-
-    sync_pb::EntitySpecifics specifics;
-    AutofillProfileSyncableService::WriteAutofillProfile(profile, &specifics);
-    node.SetEntitySpecifics(specifics);
-    return true;
-  }
-
-  bool GetAutofillProfilesFromSyncDBUnderProfileNode(
-      std::vector<AutofillProfile>* profiles) {
-    syncer::ReadTransaction trans(FROM_HERE, sync_service()->GetUserShare());
-    syncer::ReadNode autofill_root(&trans);
-    if (autofill_root.InitTypeRoot(AUTOFILL_PROFILE) != BaseNode::INIT_OK) {
-      return false;
-    }
-
-    int64_t child_id = autofill_root.GetFirstChildId();
-    while (child_id != syncer::kInvalidId) {
-      syncer::ReadNode child_node(&trans);
-      if (child_node.InitByIdLookup(child_id) != BaseNode::INIT_OK)
-        return false;
-
-      const sync_pb::AutofillProfileSpecifics& autofill(
-          child_node.GetEntitySpecifics().autofill_profile());
-      AutofillProfile p;
-      p.set_guid(autofill.guid());
-      AutofillProfileSyncableService::OverwriteProfileWithServerData(autofill,
-                                                                     &p);
-      profiles->push_back(p);
-      child_id = child_node.GetSuccessorId();
-    }
-    return true;
-  }
-
-  void SetIdleChangeProcessorExpectations() {
-    EXPECT_CALL(autofill_table_, RemoveFormElement(_, _)).Times(0);
-    EXPECT_CALL(autofill_table_, GetAutofillTimestamps(_, _, _, _)).Times(0);
-  }
-
-  AutofillTableMock& autofill_table() { return autofill_table_; }
-
-  MockPersonalDataManager& personal_data_manager() {
-    return *personal_data_manager_;
-  }
-
-  WebDataServiceFake* web_data_service() { return web_data_service_.get(); }
-
- private:
-  friend class AddAutofillProfileHelper;
-
-  AutofillTableMock autofill_table_;
-  std::unique_ptr<WebDatabaseFake> web_database_;
-  std::unique_ptr<MockWebDataServiceWrapper> web_data_wrapper_;
-  scoped_refptr<WebDataServiceFake> web_data_service_;
-  std::unique_ptr<MockPersonalDataManager> personal_data_manager_;
-  syncer::DataTypeAssociationStats association_stats_;
-  base::WeakPtrFactory<DataTypeDebugInfoListener> debug_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceAutofillTest);
-};
-
-class AddAutofillProfileHelper {
- public:
-  AddAutofillProfileHelper(ProfileSyncServiceAutofillTest* test,
-                           const std::vector<AutofillProfile>& entries)
-      : callback_(base::BindOnce(&AddAutofillProfileHelper::AddAutofillCallback,
-                                 base::Unretained(this),
-                                 test,
-                                 entries)),
-        success_(false) {}
-
-  base::OnceClosure callback() { return std::move(callback_); }
-  bool success() { return success_; }
-
- private:
-  void AddAutofillCallback(ProfileSyncServiceAutofillTest* test,
-                           const std::vector<AutofillProfile>& entries) {
-    if (!test->CreateRoot(AUTOFILL_PROFILE))
-      return;
-
-    for (size_t i = 0; i < entries.size(); ++i) {
-      if (!test->AddAutofillSyncNode(entries[i]))
-        return;
-    }
-    success_ = true;
-  }
-
-  base::OnceClosure callback_;
-  bool success_;
-};
-
-TEST_F(ProfileSyncServiceAutofillTest, HasProfileEmptySync) {
-  std::vector<std::unique_ptr<AutofillProfile>> profiles;
-  std::vector<AutofillProfile> expected_profiles;
-  std::unique_ptr<AutofillProfile> profile0 =
-      std::make_unique<AutofillProfile>();
-  autofill::test::SetProfileInfoWithGuid(
-      profile0.get(), "54B3F9AA-335E-4F71-A27D-719C41564230", "Billing",
-      "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.",
-      "unit 5", "Hollywood", "CA", "91601", "US", "12345678910");
-  expected_profiles.push_back(*profile0);
-  profiles.push_back(std::move(profile0));
-  auto profile_returner = [&profiles]() { return std::move(profiles); };
-  EXPECT_CALL(autofill_table(), GetAutofillProfiles(_))
-      .WillOnce(DoAll(LoadAutofillProfiles(profile_returner), Return(true)));
-  EXPECT_CALL(personal_data_manager(), Refresh());
-  SetIdleChangeProcessorExpectations();
-  CreateRootHelper create_root(this, AUTOFILL_PROFILE);
-  StartAutofillProfileSyncService(create_root.callback());
-  ASSERT_TRUE(create_root.success());
-  std::vector<AutofillProfile> sync_profiles;
-  ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(&sync_profiles));
-  EXPECT_EQ(1U, sync_profiles.size());
-  EXPECT_EQ(0, expected_profiles[0].Compare(sync_profiles[0]));
-}
-
-TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeProfile) {
-  AutofillProfile sync_profile;
-  autofill::test::SetProfileInfoWithGuid(
-      &sync_profile, "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing",
-      "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.",
-      "unit 5", "Hollywood", "CA", "91601", "US", "12345678910");
-
-  std::unique_ptr<AutofillProfile> native_profile =
-      std::make_unique<AutofillProfile>();
-  autofill::test::SetProfileInfoWithGuid(
-      native_profile.get(), "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing",
-      "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5",
-      "Orlando", "FL", "32801", "US", "19482937549");
-
-  std::vector<std::unique_ptr<AutofillProfile>> native_profiles;
-  native_profiles.push_back(std::move(native_profile));
-  auto profile_returner = [&native_profiles]() {
-    return std::move(native_profiles);
-  };
-  EXPECT_CALL(autofill_table(), GetAutofillProfiles(_))
-      .WillOnce(DoAll(LoadAutofillProfiles(profile_returner), Return(true)));
-
-  std::vector<AutofillProfile> sync_profiles;
-  sync_profiles.push_back(sync_profile);
-  AddAutofillProfileHelper add_autofill(this, sync_profiles);
-
-  EXPECT_CALL(autofill_table(),
-              UpdateAutofillProfile(MatchProfiles(sync_profile)))
-      .WillOnce(Return(true));
-  EXPECT_CALL(personal_data_manager(), Refresh());
-  StartAutofillProfileSyncService(add_autofill.callback());
-  ASSERT_TRUE(add_autofill.success());
-
-  std::vector<AutofillProfile> new_sync_profiles;
-  ASSERT_TRUE(
-      GetAutofillProfilesFromSyncDBUnderProfileNode(&new_sync_profiles));
-  ASSERT_EQ(1U, new_sync_profiles.size());
-  EXPECT_EQ(0, sync_profile.Compare(new_sync_profiles[0]));
-}
-
-// Tests that a sync with a new native profile that matches a more recent new
-// sync profile but with less information results in the native profile being
-// deleted and replaced by the sync profile with merged usage stats.
-TEST_F(
-    ProfileSyncServiceAutofillTest,
-    HasNativeHasSyncMergeSimilarProfileCombine_SyncHasMoreInfoAndMoreRecent) {
-  // Create two almost identical profiles. The GUIDs are different and the
-  // native profile has no value for company name.
-  AutofillProfile sync_profile;
-  autofill::test::SetProfileInfoWithGuid(
-      &sync_profile, "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing",
-      "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.",
-      "unit 5", "Hollywood", "CA", "91601", "US", "12345678910");
-  sync_profile.set_use_date(base::Time::FromTimeT(4321));
-
-  std::unique_ptr<AutofillProfile> native_profile =
-      std::make_unique<AutofillProfile>();
-  autofill::test::SetProfileInfoWithGuid(
-      native_profile.get(), "23355099-1170-4B71-8ED4-144470CC9EBF", "Billing",
-      "Mitchell", "Morrison", "johnwayne@me.xyz", "", "123 Zoo St.", "unit 5",
-      "Hollywood", "CA", "91601", "US", "12345678910");
-  native_profile->set_use_date(base::Time::FromTimeT(1234));
-
-  AutofillProfile expected_profile(sync_profile);
-  expected_profile.SetRawInfo(NAME_FULL,
-                              ASCIIToUTF16("Billing Mitchell Morrison"));
-  expected_profile.set_use_count(1);
-
-  std::vector<std::unique_ptr<AutofillProfile>> native_profiles;
-  native_profiles.push_back(std::move(native_profile));
-  auto profile_returner = [&native_profiles]() {
-    return std::move(native_profiles);
-  };
-  EXPECT_CALL(autofill_table(), GetAutofillProfiles(_))
-      .WillOnce(DoAll(LoadAutofillProfiles(profile_returner), Return(true)));
-  EXPECT_CALL(autofill_table(),
-              AddAutofillProfile(MatchProfiles(expected_profile)))
-      .WillOnce(Return(true));
-  EXPECT_CALL(autofill_table(),
-              RemoveAutofillProfile("23355099-1170-4B71-8ED4-144470CC9EBF"))
-      .WillOnce(Return(true));
-  std::vector<AutofillProfile> sync_profiles;
-  sync_profiles.push_back(sync_profile);
-  AddAutofillProfileHelper add_autofill(this, sync_profiles);
-
-  EXPECT_CALL(personal_data_manager(), Refresh());
-  // Adds all entries in |sync_profiles| to sync.
-  StartAutofillProfileSyncService(add_autofill.callback());
-  ASSERT_TRUE(add_autofill.success());
-
-  std::vector<AutofillProfile> new_sync_profiles;
-  ASSERT_TRUE(
-      GetAutofillProfilesFromSyncDBUnderProfileNode(&new_sync_profiles));
-  ASSERT_EQ(1U, new_sync_profiles.size());
-  // Check that key fields are the same.
-  EXPECT_TRUE(new_sync_profiles[0].IsSubsetOf(sync_profile, "en-US"));
-  // Make sure the additional information from the sync profile was kept.
-  EXPECT_EQ(ASCIIToUTF16("Fox"),
-            new_sync_profiles[0].GetRawInfo(ServerFieldType::COMPANY_NAME));
-  // Check that the latest use date is saved.
-  EXPECT_EQ(base::Time::FromTimeT(4321), new_sync_profiles[0].use_date());
-  // Check that the use counts were added (default value is 1).
-  EXPECT_EQ(1U, new_sync_profiles[0].use_count());
-}
-
-// Tests that a sync with a new native profile that matches an older new sync
-// profile but with less information results in the native profile being deleted
-// and replaced by the sync profile with merged usage stats.
-TEST_F(ProfileSyncServiceAutofillTest,
-       HasNativeHasSyncMergeSimilarProfileCombine_SyncHasMoreInfoAndOlder) {
-  // Create two almost identical profiles. The GUIDs are different and the
-  // native profile has no value for company name.
-  AutofillProfile sync_profile;
-  autofill::test::SetProfileInfoWithGuid(
-      &sync_profile, "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing",
-      "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.",
-      "unit 5", "Hollywood", "CA", "91601", "US", "12345678910");
-  sync_profile.set_use_date(base::Time::FromTimeT(1234));
-
-  std::unique_ptr<AutofillProfile> native_profile =
-      std::make_unique<AutofillProfile>();
-  autofill::test::SetProfileInfoWithGuid(
-      native_profile.get(), "23355099-1170-4B71-8ED4-144470CC9EBF", "Billing",
-      "Mitchell", "Morrison", "johnwayne@me.xyz", "", "123 Zoo St.", "unit 5",
-      "Hollywood", "CA", "91601", "US", "12345678910");
-  native_profile->set_use_date(base::Time::FromTimeT(4321));
-
-  AutofillProfile expected_profile(sync_profile);
-  expected_profile.SetRawInfo(NAME_FULL,
-                              ASCIIToUTF16("Billing Mitchell Morrison"));
-  expected_profile.set_use_count(1);
-  expected_profile.set_use_date(native_profile->use_date());
-
-  std::vector<std::unique_ptr<AutofillProfile>> native_profiles;
-  native_profiles.push_back(std::move(native_profile));
-  auto profile_returner = [&native_profiles]() {
-    return std::move(native_profiles);
-  };
-  EXPECT_CALL(autofill_table(), GetAutofillProfiles(_))
-      .WillOnce(DoAll(LoadAutofillProfiles(profile_returner), Return(true)));
-  EXPECT_CALL(autofill_table(),
-              AddAutofillProfile(MatchProfiles(expected_profile)))
-      .WillOnce(Return(true));
-  EXPECT_CALL(autofill_table(),
-              RemoveAutofillProfile("23355099-1170-4B71-8ED4-144470CC9EBF"))
-      .WillOnce(Return(true));
-  std::vector<AutofillProfile> sync_profiles;
-  sync_profiles.push_back(sync_profile);
-  AddAutofillProfileHelper add_autofill(this, sync_profiles);
-
-  EXPECT_CALL(personal_data_manager(), Refresh());
-  // Adds all entries in |sync_profiles| to sync.
-  StartAutofillProfileSyncService(add_autofill.callback());
-  ASSERT_TRUE(add_autofill.success());
-
-  std::vector<AutofillProfile> new_sync_profiles;
-  ASSERT_TRUE(
-      GetAutofillProfilesFromSyncDBUnderProfileNode(&new_sync_profiles));
-  ASSERT_EQ(1U, new_sync_profiles.size());
-  // Check that key fields are the same.
-  EXPECT_TRUE(new_sync_profiles[0].IsSubsetOf(sync_profile, "en-US"));
-  // Make sure the additional information from the sync profile was kept.
-  EXPECT_EQ(ASCIIToUTF16("Fox"),
-            new_sync_profiles[0].GetRawInfo(ServerFieldType::COMPANY_NAME));
-  // Check that the latest use date is saved.
-  EXPECT_EQ(base::Time::FromTimeT(4321), new_sync_profiles[0].use_date());
-  // Check that the use counts were added (default value is 1).
-  EXPECT_EQ(1U, new_sync_profiles[0].use_count());
-}
-
-// Tests that a sync with a new native profile that matches an a new sync
-// profile but with more information results in the native profile being deleted
-// and replaced by the sync profile with the native profiles additional
-// information merged in. The merge should happen even if the sync profile is
-// more recent.
-TEST_F(ProfileSyncServiceAutofillTest,
-       HasNativeHasSyncMergeSimilarProfileCombine_NativeHasMoreInfo) {
-  // Create two almost identical profiles. The GUIDs are different and the
-  // sync profile has no value for company name.
-  AutofillProfile sync_profile;
-  autofill::test::SetProfileInfoWithGuid(
-      &sync_profile, "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing",
-      "Mitchell", "Morrison", "johnwayne@me.xyz", "", "123 Zoo St.", "unit 5",
-      "Hollywood", "CA", "91601", "US", "12345678910");
-  sync_profile.set_use_date(base::Time::FromTimeT(4321));
-
-  std::unique_ptr<AutofillProfile> native_profile =
-      std::make_unique<AutofillProfile>();
-  autofill::test::SetProfileInfoWithGuid(
-      native_profile.get(), "23355099-1170-4B71-8ED4-144470CC9EBF", "Billing",
-      "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.",
-      "unit 5", "Hollywood", "CA", "91601", "US", "12345678910");
-  native_profile->set_use_date(base::Time::FromTimeT(1234));
-
-  AutofillProfile expected_profile(*native_profile);
-  expected_profile.SetRawInfo(NAME_FULL,
-                              ASCIIToUTF16("Billing Mitchell Morrison"));
-  expected_profile.set_use_date(sync_profile.use_date());
-  expected_profile.set_use_count(1);
-
-  std::vector<std::unique_ptr<AutofillProfile>> native_profiles;
-  native_profiles.push_back(std::move(native_profile));
-  auto profile_returner = [&native_profiles]() {
-    return std::move(native_profiles);
-  };
-  EXPECT_CALL(autofill_table(), GetAutofillProfiles(_))
-      .WillOnce(DoAll(LoadAutofillProfiles(profile_returner), Return(true)));
-  EXPECT_CALL(autofill_table(),
-              AddAutofillProfile(MatchProfiles(expected_profile)))
-      .WillOnce(Return(true));
-  EXPECT_CALL(autofill_table(),
-              RemoveAutofillProfile("23355099-1170-4B71-8ED4-144470CC9EBF"))
-      .WillOnce(Return(true));
-  std::vector<AutofillProfile> sync_profiles;
-  sync_profiles.push_back(sync_profile);
-  AddAutofillProfileHelper add_autofill(this, sync_profiles);
-
-  EXPECT_CALL(personal_data_manager(), Refresh());
-  // Adds all entries in |sync_profiles| to sync.
-  StartAutofillProfileSyncService(add_autofill.callback());
-  ASSERT_TRUE(add_autofill.success());
-
-  std::vector<AutofillProfile> new_sync_profiles;
-  ASSERT_TRUE(
-      GetAutofillProfilesFromSyncDBUnderProfileNode(&new_sync_profiles));
-  ASSERT_EQ(1U, new_sync_profiles.size());
-  // Check that key fields are the same.
-  EXPECT_TRUE(new_sync_profiles[0].IsSubsetOf(expected_profile, "en-US"));
-  // Make sure the additional information of the native profile was saved into
-  // the sync profile.
-  EXPECT_EQ(ASCIIToUTF16("Fox"),
-            new_sync_profiles[0].GetRawInfo(ServerFieldType::COMPANY_NAME));
-  // Check that the latest use date is saved.
-  EXPECT_EQ(base::Time::FromTimeT(4321), new_sync_profiles[0].use_date());
-  // Check that the use counts were added (default value is 1).
-  EXPECT_EQ(1U, new_sync_profiles[0].use_count());
-}
-
-// Tests that a sync with a new native profile that differ only by name a new
-// sync profile results in keeping both profiles.
-TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSync_DifferentPrimaryInfo) {
-  AutofillProfile sync_profile;
-  autofill::test::SetProfileInfoWithGuid(
-      &sync_profile, "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing",
-      "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.",
-      "unit 5", "Hollywood", "CA", "91601", "US", "12345678910");
-  sync_profile.set_use_date(base::Time::FromTimeT(4321));
-
-  std::unique_ptr<AutofillProfile> native_profile =
-      std::make_unique<AutofillProfile>();
-  autofill::test::SetProfileInfoWithGuid(
-      native_profile.get(), "23355099-1170-4B71-8ED4-144470CC9EBF", "Billing",
-      "John", "Smith", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5",
-      "Hollywood", "CA", "91601", "US", "12345678910");
-  native_profile->set_use_date(base::Time::FromTimeT(1234));
-
-  std::vector<std::unique_ptr<AutofillProfile>> native_profiles;
-  native_profiles.push_back(std::move(native_profile));
-  auto profile_returner = [&native_profiles]() {
-    return std::move(native_profiles);
-  };
-  EXPECT_CALL(autofill_table(), GetAutofillProfiles(_))
-      .WillOnce(DoAll(LoadAutofillProfiles(profile_returner), Return(true)));
-  EXPECT_CALL(autofill_table(), AddAutofillProfile(MatchProfiles(sync_profile)))
-      .WillOnce(Return(true));
-  std::vector<AutofillProfile> sync_profiles;
-  sync_profiles.push_back(sync_profile);
-  AddAutofillProfileHelper add_autofill(this, sync_profiles);
-
-  EXPECT_CALL(personal_data_manager(), Refresh());
-  // Adds all entries in |sync_profiles| to sync.
-  StartAutofillProfileSyncService(add_autofill.callback());
-  ASSERT_TRUE(add_autofill.success());
-
-  std::vector<AutofillProfile> new_sync_profiles;
-  ASSERT_TRUE(
-      GetAutofillProfilesFromSyncDBUnderProfileNode(&new_sync_profiles));
-  // The two profiles should be kept.
-  ASSERT_EQ(2U, new_sync_profiles.size());
-}
-
-// Tests that a new native profile that is the same as a new sync profile except
-// with different GUIDs results in the native profile being deleted and replaced
-// by the sync profile.
-TEST_F(ProfileSyncServiceAutofillTest, MergeProfileWithDifferentGuid) {
-  AutofillProfile sync_profile;
-
-  autofill::test::SetProfileInfoWithGuid(
-      &sync_profile, "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing",
-      "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.",
-      "unit 5", "Hollywood", "CA", "91601", "US", "12345678910");
-  sync_profile.set_use_count(20);
-  sync_profile.set_use_date(base::Time::FromTimeT(1234));
-
-  std::string native_guid = "EDC609ED-7EEE-4F27-B00C-423242A9C44B";
-  std::unique_ptr<AutofillProfile> native_profile =
-      std::make_unique<AutofillProfile>();
-  autofill::test::SetProfileInfoWithGuid(
-      native_profile.get(), native_guid.c_str(), "Billing", "Mitchell",
-      "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5",
-      "Hollywood", "CA", "91601", "US", "12345678910");
-  native_profile->set_use_count(5);
-  native_profile->set_use_date(base::Time::FromTimeT(4321));
-
-  std::vector<std::unique_ptr<AutofillProfile>> native_profiles;
-  native_profiles.push_back(std::move(native_profile));
-  auto profile_returner = [&native_profiles]() {
-    return std::move(native_profiles);
-  };
-  EXPECT_CALL(autofill_table(), GetAutofillProfiles(_))
-      .WillOnce(DoAll(LoadAutofillProfiles(profile_returner), Return(true)));
-
-  std::vector<AutofillProfile> sync_profiles;
-  sync_profiles.push_back(sync_profile);
-  AddAutofillProfileHelper add_autofill(this, sync_profiles);
-
-  EXPECT_CALL(autofill_table(), AddAutofillProfile(_)).WillOnce(Return(true));
-  EXPECT_CALL(autofill_table(), RemoveAutofillProfile(native_guid))
-      .WillOnce(Return(true));
-  EXPECT_CALL(personal_data_manager(), Refresh());
-  StartAutofillProfileSyncService(add_autofill.callback());
-  ASSERT_TRUE(add_autofill.success());
-
-  std::vector<AutofillProfile> new_sync_profiles;
-  ASSERT_TRUE(
-      GetAutofillProfilesFromSyncDBUnderProfileNode(&new_sync_profiles));
-  // Check that the profiles were merged.
-  ASSERT_EQ(1U, new_sync_profiles.size());
-  EXPECT_EQ(0, sync_profile.Compare(new_sync_profiles[0]));
-  // Check that the sync guid was kept.
-  EXPECT_EQ(sync_profile.guid(), new_sync_profiles[0].guid());
-  // Check that the sync profile use count was kept.
-  EXPECT_EQ(20U, new_sync_profiles[0].use_count());
-  // Check that the sync profile use date was kept.
-  EXPECT_EQ(base::Time::FromTimeT(1234), new_sync_profiles[0].use_date());
-}
-
-TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfile) {
-  EXPECT_CALL(autofill_table(), GetAutofillProfiles(_)).WillOnce(Return(true));
-  EXPECT_CALL(personal_data_manager(), Refresh());
-  SetIdleChangeProcessorExpectations();
-  CreateRootHelper create_root(this, AUTOFILL_PROFILE);
-  StartAutofillProfileSyncService(create_root.callback());
-  ASSERT_TRUE(create_root.success());
-
-  AutofillProfile added_profile;
-  autofill::test::SetProfileInfoWithGuid(
-      &added_profile, "D6ADA912-D374-4C0A-917D-F5C8EBE43011", "Josephine",
-      "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5",
-      "Orlando", "FL", "32801", "US", "19482937549");
-
-  // TODO(crbug.com/904390): Remove when the investigation is over. This call is
-  // needed in the AutofillProfileChanged() callback.
-  EXPECT_CALL(autofill_table(), GetServerProfiles(_)).WillOnce(Return(true));
-
-  AutofillProfileChange change(AutofillProfileChange::ADD, added_profile.guid(),
-                               &added_profile);
-  web_data_service()->OnAutofillProfileChanged(change);
-
-  std::vector<AutofillProfile> new_sync_profiles;
-  ASSERT_TRUE(
-      GetAutofillProfilesFromSyncDBUnderProfileNode(&new_sync_profiles));
-  ASSERT_EQ(1U, new_sync_profiles.size());
-  EXPECT_EQ(0, added_profile.Compare(new_sync_profiles[0]));
-}
-
-TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveProfile) {
-  AutofillProfile sync_profile;
-  autofill::test::SetProfileInfoWithGuid(
-      &sync_profile, "3BA5FA1B-1EC4-4BB3-9B57-EC92BE3C1A09", "Josephine",
-      "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5",
-      "Orlando", "FL", "32801", "US", "19482937549");
-  std::unique_ptr<AutofillProfile> native_profile =
-      std::make_unique<AutofillProfile>();
-  autofill::test::SetProfileInfoWithGuid(
-      native_profile.get(), "3BA5FA1B-1EC4-4BB3-9B57-EC92BE3C1A09", "Josephine",
-      "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5",
-      "Orlando", "FL", "32801", "US", "19482937549");
-
-  std::vector<std::unique_ptr<AutofillProfile>> native_profiles;
-  native_profiles.push_back(std::move(native_profile));
-  auto profile_returner = [&native_profiles]() {
-    return std::move(native_profiles);
-  };
-  EXPECT_CALL(autofill_table(), GetAutofillProfiles(_))
-      .WillOnce(DoAll(LoadAutofillProfiles(profile_returner), Return(true)));
-
-  std::vector<AutofillProfile> sync_profiles;
-  sync_profiles.push_back(sync_profile);
-  AddAutofillProfileHelper add_autofill(this, sync_profiles);
-  EXPECT_CALL(personal_data_manager(), Refresh());
-  StartAutofillProfileSyncService(add_autofill.callback());
-  ASSERT_TRUE(add_autofill.success());
-
-  AutofillProfileChange change(AutofillProfileChange::REMOVE,
-                               sync_profile.guid(), &sync_profile);
-  web_data_service()->OnAutofillProfileChanged(change);
-
-  std::vector<AutofillProfile> new_sync_profiles;
-  ASSERT_TRUE(
-      GetAutofillProfilesFromSyncDBUnderProfileNode(&new_sync_profiles));
-  ASSERT_EQ(0U, new_sync_profiles.size());
-}
-
-}  // namespace browser_sync
diff --git a/components/browser_sync/profile_sync_service_bookmark_unittest.cc b/components/browser_sync/profile_sync_service_bookmark_unittest.cc
index cfe2311..d490c656a 100644
--- a/components/browser_sync/profile_sync_service_bookmark_unittest.cc
+++ b/components/browser_sync/profile_sync_service_bookmark_unittest.cc
@@ -15,6 +15,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/containers/adapters.h"
 #include "base/containers/queue.h"
 #include "base/containers/stack.h"
 #include "base/files/file_util.h"
@@ -934,10 +935,10 @@
 TEST_F(ProfileSyncServiceBookmarkTest,
        InitialModelAssociateVerifyExternalIdMatch) {
   LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
-  const int kNumFolders = 10;
-  const int kNumBookmarks = 10;
-  const int kFolderToIncludeBookmarks = 7;
-  const int kBookmarkToDelete = 4;
+  constexpr size_t kNumFolders = 10;
+  constexpr size_t kNumBookmarks = 10;
+  constexpr size_t kFolderToIncludeBookmarks = 7;
+  constexpr size_t kBookmarkToDelete = 4;
 
   int64_t folder_ids[kNumFolders];
   int64_t bookmark_ids[kNumBookmarks];
@@ -960,16 +961,15 @@
   // +- folder (#9)
 
   const BookmarkNode* parent_folder = nullptr;
-  for (int i = 0; i < kNumFolders; i++) {
+  for (size_t i = 0; i < kNumFolders; i++) {
     const BookmarkNode* folder = model()->AddFolder(
         model()->bookmark_bar_node(), i, base::ASCIIToUTF16("folder"));
     folder_ids[i] = folder->id();
-    if (i == kFolderToIncludeBookmarks) {
+    if (i == kFolderToIncludeBookmarks)
       parent_folder = folder;
-    }
   }
 
-  for (int i = 0; i < kNumBookmarks; i++) {
+  for (size_t i = 0; i < kNumBookmarks; i++) {
     const BookmarkNode* bookmark =
         model()->AddURL(parent_folder, i, base::ASCIIToUTF16("bookmark"),
                         GURL("http://www.google.com/"));
@@ -988,7 +988,7 @@
     syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share());
     // Create in reverse order because AddFolderToShare passes null for
     // |predecessor| argument.
-    for (int i = kNumFolders - 1; i >= 0; i--) {
+    for (auto folder_id : base::Reversed(folder_ids)) {
       int64_t id = AddFolderToShare(&trans, "folder");
 
       // Pre-map sync folders to native folders by setting
@@ -998,16 +998,15 @@
       // a wrong folder.
       syncer::WriteNode node(&trans);
       EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(id));
-      node.GetMutableEntryForTest()->PutLocalExternalId(folder_ids[i]);
+      node.GetMutableEntryForTest()->PutLocalExternalId(folder_id);
 
-      if (i == kFolderToIncludeBookmarks) {
+      if (folder_id == parent_folder->id())
         parent_id = id;
-      }
     }
 
     // Create sync bookmark matching native bookmarks above in reverse order
     // because AddBookmarkToShare passes null for |predecessor| argument.
-    for (int i = kNumBookmarks - 1; i >= 0; i--) {
+    for (auto bookmark_id : base::Reversed(bookmark_ids)) {
       int id = AddBookmarkToShare(&trans, parent_id, "bookmark",
                                   "http://www.google.com/");
 
@@ -1016,11 +1015,10 @@
       // the right ones despite all of them having identical names and URLs.
       syncer::WriteNode node(&trans);
       EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(id));
-      node.GetMutableEntryForTest()->PutLocalExternalId(bookmark_ids[i]);
+      node.GetMutableEntryForTest()->PutLocalExternalId(bookmark_id);
 
-      if (i == kBookmarkToDelete) {
+      if (bookmark_id == bookmark_ids[kBookmarkToDelete])
         sync_bookmark_id_to_delete = id;
-      }
     }
   }
 
@@ -1042,16 +1040,16 @@
 
   // Only one native node should have been deleted and no nodes cloned due to
   // matching folder names.
-  EXPECT_EQ(kNumFolders, model()->bookmark_bar_node()->child_count());
-  EXPECT_EQ(kNumBookmarks - 1, parent_folder->child_count());
+  EXPECT_EQ(kNumFolders, model()->bookmark_bar_node()->children().size());
+  EXPECT_EQ(kNumBookmarks - 1, parent_folder->children().size());
   EXPECT_EQ(total_node_count - 1,
             model()->bookmark_bar_node()->GetTotalNodeCount());
 
   // Verify that the right bookmark got deleted and no bookmarks reordered.
-  for (int i = 0; i < parent_folder->child_count(); i++) {
-    int index_in_bookmark_ids = (i < kBookmarkToDelete) ? i : i + 1;
+  for (size_t i = 0; i < parent_folder->children().size(); i++) {
+    size_t index_in_bookmark_ids = (i < kBookmarkToDelete) ? i : i + 1;
     EXPECT_EQ(bookmark_ids[index_in_bookmark_ids],
-              parent_folder->GetChild(i)->id());
+              parent_folder->children()[i]->id());
   }
 }
 
@@ -1426,7 +1424,7 @@
   // Create two book-end nodes to insert between.
   model()->AddFolder(model()->other_node(), 0, base::ASCIIToUTF16("Alpha"));
   model()->AddFolder(model()->other_node(), 1, base::ASCIIToUTF16("Omega"));
-  int count = 2;
+  size_t count = 2;
 
   // Test insertion in first half of range by repeatedly inserting in second
   // position.
@@ -1447,7 +1445,7 @@
   }
 
   // Verify that the browser model matches the sync model.
-  EXPECT_EQ(model()->other_node()->child_count(), count);
+  EXPECT_EQ(model()->other_node()->children().size(), count);
   ExpectModelMatch();
 }
 
@@ -1613,7 +1611,7 @@
   // date for CompareWithTestData()).
   void PopulateFromTestData(const BookmarkNode* node,
                             const TestData* data,
-                            int size,
+                            size_t size,
                             int* running_count);
   void CompareWithTestData(const BookmarkNode* node,
                            const TestData* data,
@@ -1751,12 +1749,12 @@
 void ProfileSyncServiceBookmarkTestWithData::PopulateFromTestData(
     const BookmarkNode* node,
     const TestData* data,
-    int size,
+    size_t size,
     int* running_count) {
   ASSERT_TRUE(node);
   ASSERT_TRUE(data);
   ASSERT_TRUE(node->is_folder());
-  for (int i = 0; i < size; ++i) {
+  for (size_t i = 0; i < size; ++i) {
     const TestData& item = data[i];
     if (item.url) {
       const base::Time add_time =
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json
index 8028870..e1cd958 100644
--- a/components/certificate_transparency/data/log_list.json
+++ b/components/certificate_transparency/data/log_list.json
@@ -1,16 +1,6 @@
 {
  "logs": [
   {
-   "description": "Google 'Argon2018' log",
-   "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0gBVBa3VR7QZu82V+ynXWD14JM3ORp37MtRxTmACJV5ZPtfUA7htQ2hofuigZQs+bnFZkje+qejxoyvk2Q1VaA==",
-   "url": "ct.googleapis.com/logs/argon2018/",
-   "maximum_merge_delay": 86400,
-   "operated_by": [
-    0
-   ],
-   "dns_api_endpoint": "argon2018.ct.googleapis.com"
-  },
-  {
    "description": "Google 'Argon2019' log",
    "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEI3MQm+HzXvaYa2mVlhB4zknbtAT8cSxakmBoJcBKGqGwYS0bhxSpuvABM1kdBTDpQhXnVdcq+LSiukXJRpGHVg==",
    "url": "ct.googleapis.com/logs/argon2019/",
@@ -137,16 +127,6 @@
    "dns_api_endpoint": "skydiver.ct.googleapis.com"
   },
   {
-   "description": "Cloudflare 'Nimbus2018' Log",
-   "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAsVpWvrH3Ke0VRaMg9ZQoQjb5g/xh1z3DDa6IuxY5DyPsk6brlvrUNXZzoIg0DcvFiAn2kd6xmu4Obk5XA/nRg==",
-   "url": "ct.cloudflare.com/logs/nimbus2018/",
-   "maximum_merge_delay": 86400,
-   "operated_by": [
-    1
-   ],
-   "dns_api_endpoint": "cloudflare-nimbus2018.ct.googleapis.com"
-  },
-  {
    "description": "Cloudflare 'Nimbus2019' Log",
    "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkZHz1v5r8a9LmXSMegYZAg4UW+Ug56GtNfJTDNFZuubEJYgWf4FcC5D+ZkYwttXTDSo4OkanG9b3AI4swIQ28g==",
    "url": "ct.cloudflare.com/logs/nimbus2019/",
@@ -177,6 +157,26 @@
    "dns_api_endpoint": "cloudflare-nimbus2021.ct.googleapis.com"
   },
   {
+   "description": "Cloudflare 'Nimbus2022' Log",
+   "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESLJHTlAycmJKDQxIv60pZG8g33lSYxYpCi5gteI6HLevWbFVCdtZx+m9b+0LrwWWl/87mkNN6xE0M4rnrIPA/w==",
+   "url": "ct.cloudflare.com/logs/nimbus2022/",
+   "maximum_merge_delay": 86400,
+   "operated_by": [
+    1
+   ],
+   "dns_api_endpoint": "cloudflare-nimbus2022.ct.googleapis.com"
+  },
+  {
+   "description": "Cloudflare 'Nimbus2023' Log",
+   "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEi/8tkhjLRp0SXrlZdTzNkTd6HqmcmXiDJz3fAdWLgOhjmv4mohvRhwXul9bgW0ODgRwC9UGAgH/vpGHPvIS1qA==",
+   "url": "ct.cloudflare.com/logs/nimbus2023/",
+   "maximum_merge_delay": 86400,
+   "operated_by": [
+    1
+   ],
+   "dns_api_endpoint": "cloudflare-nimbus2023.ct.googleapis.com"
+  },
+  {
    "description": "DigiCert Log Server",
    "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAkbFvhu7gkAW6MHSrBlpE1n4+HCFRkC5OLAjgqhkTH+/uzSfSl8ois8ZxAD2NgaTZe1M9akhYlrYkes4JECs6A==",
    "url": "ct1.digicert-ct.com/log/",
@@ -197,16 +197,6 @@
    "dns_api_endpoint": "digicert2.ct.googleapis.com"
   },
   {
-   "description": "DigiCert Yeti2018 Log",
-   "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESYlKFDLLFmA9JScaiaNnqlU8oWDytxIYMfswHy9Esg0aiX+WnP/yj4O0ViEHtLwbmOQeSWBGkIu9YK9CLeer+g==",
-   "url": "yeti2018.ct.digicert.com/log/",
-   "maximum_merge_delay": 86400,
-   "operated_by": [
-    2
-   ],
-   "dns_api_endpoint": "digicert-yeti2018.ct.googleapis.com"
-  },
-  {
    "description": "DigiCert Yeti2019 Log",
    "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkZd/ow8X+FSVWAVSf8xzkFohcPph/x6pS1JHh7g1wnCZ5y/8Hk6jzJxs6t3YMAWz2CPd4VkCdxwKexGhcFxD9A==",
    "url": "yeti2019.ct.digicert.com/log/",
@@ -247,14 +237,14 @@
    "dns_api_endpoint": "digicert-yeti2022.ct.googleapis.com"
   },
   {
-   "description": "DigiCert Nessie2018 Log",
-   "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVqpLa2W+Rz1XDZPBIyKJO+KKFOYZTj9MpJWnZeFUqzc5aivOiWEVhs8Gy2AlH3irWPFjIZPZMs3Dv7M+0LbPyQ==",
-   "url": "nessie2018.ct.digicert.com/log/",
+   "description": "DigiCert Yeti2023 Log",
+   "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfQ0DsdWYitzwFTvG3F4Nbj8Nv5XIVYzQpkyWsU4nuSYlmcwrAp6m092fsdXEw6w1BAeHlzaqrSgNfyvZaJ9y0Q==",
+   "url": "yeti2023.ct.digicert.com/log/",
    "maximum_merge_delay": 86400,
    "operated_by": [
     2
    ],
-   "dns_api_endpoint": "digicert-nessie2018.ct.googleapis.com"
+   "dns_api_endpoint": "digicert-yeti2023.ct.googleapis.com"
   },
   {
    "description": "DigiCert Nessie2019 Log",
@@ -297,6 +287,16 @@
    "dns_api_endpoint": "digicert-nessie2022.ct.googleapis.com"
   },
   {
+   "description": "DigiCert Nessie2023 Log",
+   "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEXu8iQwSCRSf2CbITGpUpBtFVt8+I0IU0d1C36Lfe1+fbwdaI0Z5FktfM2fBoI1bXBd18k2ggKGYGgdZBgLKTg==",
+   "url": "nessie2023.ct.digicert.com/log/",
+   "maximum_merge_delay": 86400,
+   "operated_by": [
+    2
+   ],
+   "dns_api_endpoint": "digicert-nessie2023.ct.googleapis.com"
+  },
+  {
    "description": "Symantec log",
    "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEluqsHEYMG1XcDfy1lCdGV0JwOmkY4r87xNuroPS2bMBTP01CEDPwWJePa75y9CrsHEKqAy8afig1dpkIPSEUhg==",
    "url": "ct.ws.symantec.com/",
diff --git a/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc b/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc
index 9b26ac1..87cc8cfd 100644
--- a/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc
+++ b/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc
@@ -587,10 +587,9 @@
       encoder_->GetMaxCodedBufferSize(test_image->visible_size);
   PrepareMemory(bitstream_buffer_id);
 
-  base::SharedMemoryHandle dup_handle =
-      base::SharedMemoryHandle(hw_out_shm_->handle());
+  // media::BitstreamBuffer will duplicate hw_out_shm_->handle().
   encoded_buffer_ =
-      media::BitstreamBuffer(bitstream_buffer_id, dup_handle,
+      media::BitstreamBuffer(bitstream_buffer_id, hw_out_shm_->handle(),
                              false /* read_only */, test_image->output_size);
   scoped_refptr<media::VideoFrame> input_frame_ =
       media::VideoFrame::WrapExternalSharedMemory(
diff --git a/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.cc b/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.cc
index 4324d5c..bcad935 100644
--- a/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.cc
+++ b/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.cc
@@ -199,21 +199,28 @@
   }
 
   base::UnguessableToken input_guid = base::UnguessableToken::Create();
-  base::UnguessableToken exif_guid = base::UnguessableToken::Create();
-  base::UnguessableToken output_guid = base::UnguessableToken::Create();
   base::SharedMemoryHandle input_shm_handle(
       base::FileDescriptor(input_fd, true), input_buffer_size, input_guid);
-  base::SharedMemoryHandle exif_shm_handle(base::FileDescriptor(exif_fd, true),
-                                           exif_buffer_size, exif_guid);
-  base::SharedMemoryHandle output_shm_handle(
-      base::FileDescriptor(output_fd, true), output_buffer_size, output_guid);
 
-  media::BitstreamBuffer output_buffer(
-      task_id, output_shm_handle, false /* read_only */, output_buffer_size);
+  base::subtle::PlatformSharedMemoryRegion output_shm_region =
+      base::subtle::PlatformSharedMemoryRegion::Take(
+          base::subtle::ScopedFDPair(base::ScopedFD(output_fd),
+                                     base::ScopedFD()),
+          base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe,
+          output_buffer_size, base::UnguessableToken::Create());
+
+  media::BitstreamBuffer output_buffer(task_id, std::move(output_shm_region),
+                                       output_buffer_size);
   std::unique_ptr<media::BitstreamBuffer> exif_buffer;
   if (exif_buffer_size > 0) {
+    base::subtle::PlatformSharedMemoryRegion exif_shm_region =
+        base::subtle::PlatformSharedMemoryRegion::Take(
+            base::subtle::ScopedFDPair(base::ScopedFD(exif_fd),
+                                       base::ScopedFD()),
+            base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe,
+            exif_buffer_size, base::UnguessableToken::Create());
     exif_buffer = std::make_unique<media::BitstreamBuffer>(
-        task_id, exif_shm_handle, false /* read_only */, exif_buffer_size);
+        task_id, std::move(exif_shm_region), exif_buffer_size);
   }
   gfx::Size coded_size(coded_size_width, coded_size_height);
 
@@ -314,15 +321,18 @@
         0, ::chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
     return;
   }
-  base::UnguessableToken exif_guid = base::UnguessableToken::Create();
-  base::SharedMemoryHandle exif_shm_handle(base::FileDescriptor(exif_fd, true),
-                                           exif_buffer_size, exif_guid);
   std::unique_ptr<media::BitstreamBuffer> exif_buffer;
   if (exif_buffer_size > 0) {
     // Currently we use our zero-based |task_id| as id of |exif_buffer| to track
     // the encode task process from both Chrome OS and Chrome side.
+    base::subtle::PlatformSharedMemoryRegion exif_shm_region =
+        base::subtle::PlatformSharedMemoryRegion::Take(
+            base::subtle::ScopedFDPair(base::ScopedFD(exif_fd),
+                                       base::ScopedFD()),
+            base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe,
+            exif_buffer_size, base::UnguessableToken::Create());
     exif_buffer = std::make_unique<media::BitstreamBuffer>(
-        task_id, exif_shm_handle, false /* read_only */, exif_buffer_size);
+        task_id, std::move(exif_shm_region), exif_buffer_size);
   }
   encode_cb_map_.emplace(task_id, std::move(callback));
 
diff --git a/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.cc b/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.cc
index 93ed4ba..1e33ee4 100644
--- a/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.cc
+++ b/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.cc
@@ -208,16 +208,19 @@
     return;
   }
 
-  base::UnguessableToken guid = base::UnguessableToken::Create();
-  base::SharedMemoryHandle input_shm_handle(
-      base::FileDescriptor(input_fd, true), input_buffer_size, guid);
-  base::SharedMemoryHandle output_shm_handle(
-      base::FileDescriptor(output_fd, true), output_buffer_size, guid);
-
-  media::BitstreamBuffer in_buffer(buffer_id, input_shm_handle,
-                                   false /* read_only */, input_buffer_size);
+  base::subtle::PlatformSharedMemoryRegion input_shm_region =
+      base::subtle::PlatformSharedMemoryRegion::Take(
+          base::subtle::ScopedFDPair(base::ScopedFD(input_fd),
+                                     base::ScopedFD()),
+          base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe,
+          input_buffer_size, base::UnguessableToken::Create());
+  media::BitstreamBuffer in_buffer(buffer_id, std::move(input_shm_region),
+                                   input_buffer_size);
   gfx::Size coded_size(coded_size_width, coded_size_height);
 
+  base::SharedMemoryHandle output_shm_handle(
+      base::FileDescriptor(output_fd, true), output_buffer_size,
+      base::UnguessableToken::Create());
   mojo::ScopedSharedBufferHandle output_scoped_handle =
       mojo::WrapSharedMemoryHandle(
           output_shm_handle, output_buffer_size,
diff --git a/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service_unittest.cc b/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service_unittest.cc
index 7d5324e..ca2e74f 100644
--- a/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service_unittest.cc
+++ b/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/memory/platform_shared_memory_region.h"
 #include "base/run_loop.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread.h"
@@ -68,16 +69,16 @@
   subsamples.push_back(media::SubsampleEntry(15, 7));
 
   base::RunLoop run_loop2;
-  base::SharedMemory shm;
-  ASSERT_TRUE(shm.CreateAndMapAnonymous(kInputBufferSizeInBytes));
+  base::subtle::PlatformSharedMemoryRegion shm_region =
+      base::subtle::PlatformSharedMemoryRegion::CreateUnsafe(
+          kInputBufferSizeInBytes);
 
   mojo::ScopedSharedBufferHandle output_frame_handle =
       mojo::SharedBufferHandle::Create(kOutputFrameSizeInBytes);
 
-  media::BitstreamBuffer bitstream_buffer(
-      kArbitraryBitstreamBufferId,
-      base::SharedMemory::DuplicateHandle(shm.handle()), false /* read_only */,
-      kInputBufferSizeInBytes);
+  media::BitstreamBuffer bitstream_buffer(kArbitraryBitstreamBufferId,
+                                          std::move(shm_region),
+                                          kInputBufferSizeInBytes);
   bitstream_buffer.SetDecryptionSettings(kKeyId, kIv, subsamples);
 
   jpeg_decoder->Decode(
diff --git a/components/crx_file/OWNERS b/components/crx_file/OWNERS
index 8c5a6db..6fbc7de 100644
--- a/components/crx_file/OWNERS
+++ b/components/crx_file/OWNERS
@@ -1,2 +1,4 @@
 file://extensions/OWNERS
 
+waffles@chromium.org
+sorin@chromium.org
diff --git a/components/download/internal/common/download_create_info.cc b/components/download/internal/common/download_create_info.cc
index ebc8df19..b875d99 100644
--- a/components/download/internal/common/download_create_info.cc
+++ b/components/download/internal/common/download_create_info.cc
@@ -28,7 +28,7 @@
       save_info(std::move(save_info)),
       render_process_id(-1),
       render_frame_id(-1),
-      accept_range(false),
+      accept_range(RangeRequestSupportType::kNoSupport),
       connection_info(net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN),
       method("GET"),
       ukm_source_id(ukm::kInvalidSourceId) {}
diff --git a/components/download/internal/common/download_file_impl.cc b/components/download/internal/common/download_file_impl.cc
index 13f52e03..8a1b78e 100644
--- a/components/download/internal/common/download_file_impl.cc
+++ b/components/download/internal/common/download_file_impl.cc
@@ -10,9 +10,9 @@
 
 #include "base/bind.h"
 #include "base/files/file_util.h"
-#include "base/message_loop/message_loop_current.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "base/values.h"
@@ -167,7 +167,7 @@
       bytes_seen_without_parallel_streams_(0),
       is_paused_(false),
       download_id_(download_id),
-      main_task_runner_(base::MessageLoopCurrent::Get()->task_runner()),
+      main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       observer_(observer),
       weak_factory_(this) {
   TRACE_EVENT_INSTANT0("download", "DownloadFileCreated",
diff --git a/components/download/internal/common/download_job_factory.cc b/components/download/internal/common/download_job_factory.cc
index 0c35188..7d62075 100644
--- a/components/download/internal/common/download_job_factory.cc
+++ b/components/download/internal/common/download_job_factory.cc
@@ -47,10 +47,11 @@
       create_info.method == "GET" && create_info.url().SchemeIsHTTPOrHTTPS();
   bool partial_response_success =
       download_item->GetReceivedSlices().empty() || create_info.offset != 0;
-  bool is_parallelizable = has_strong_validator && create_info.accept_range &&
-                           has_content_length && satisfy_min_file_size &&
-                           satisfy_connection_type && http_get_method &&
-                           partial_response_success;
+  bool is_parallelizable =
+      has_strong_validator &&
+      create_info.accept_range == RangeRequestSupportType::kSupport &&
+      has_content_length && satisfy_min_file_size && satisfy_connection_type &&
+      http_get_method && partial_response_success;
 
   if (!IsParallelDownloadEnabled())
     return is_parallelizable;
@@ -64,7 +65,7 @@
     RecordParallelDownloadCreationEvent(
         ParallelDownloadCreationEvent::FALLBACK_REASON_STRONG_VALIDATORS);
   }
-  if (!create_info.accept_range) {
+  if (create_info.accept_range != RangeRequestSupportType::kSupport) {
     RecordParallelDownloadCreationEvent(
         ParallelDownloadCreationEvent::FALLBACK_REASON_ACCEPT_RANGE_HEADER);
   }
diff --git a/components/download/internal/common/download_utils.cc b/components/download/internal/common/download_utils.cc
index 43c5713..29a52c5 100644
--- a/components/download/internal/common/download_utils.cc
+++ b/components/download/internal/common/download_utils.cc
@@ -237,10 +237,15 @@
   // In RFC 7233, a single part 206 partial response must generate
   // Content-Range. Accept-Range may be sent in 200 response to indicate the
   // server can handle range request, but optional in 206 response.
-  create_info->accept_range =
-      headers->HasHeaderValue("Accept-Ranges", "bytes") ||
+  if (headers->HasHeaderValue("Accept-Ranges", "bytes") ||
       (headers->HasHeader("Content-Range") &&
-       headers->response_code() == net::HTTP_PARTIAL_CONTENT);
+       headers->response_code() == net::HTTP_PARTIAL_CONTENT)) {
+    create_info->accept_range = RangeRequestSupportType::kSupport;
+  } else if (headers->HasHeaderValue("Accept-Ranges", "none")) {
+    create_info->accept_range = RangeRequestSupportType::kNoSupport;
+  } else {
+    create_info->accept_range = RangeRequestSupportType::kUnknown;
+  }
 }
 
 std::unique_ptr<network::ResourceRequest> CreateResourceRequest(
diff --git a/components/download/public/common/download_create_info.h b/components/download/public/common/download_create_info.h
index 19552ad5..0e97e30 100644
--- a/components/download/public/common/download_create_info.h
+++ b/components/download/public/common/download_create_info.h
@@ -35,6 +35,16 @@
 
 namespace download {
 
+// Server support for range request inferred from the response headers.
+// |kSupport| value means the server supports range requests. |kNoSupport|
+// means no range request is accepted by server. and |kUnknown| is used if
+// range request support cannot be inferred from response headers.
+enum class RangeRequestSupportType {
+  kSupport = 0,
+  kUnknown,
+  kNoSupport,
+};
+
 // Used for informing the download manager of a new download, since we don't
 // want to pass |DownloadItem|s between threads.
 struct COMPONENTS_DOWNLOAD_EXPORT DownloadCreateInfo {
@@ -142,10 +152,8 @@
   // For continuing a download, the ETag of the file.
   std::string etag;
 
-  // If the download response can be partial content.
-  // Either "Accept-Ranges" or "Content-Range" header presents in the
-  // response header.
-  bool accept_range;
+  // Whether the server supports range requests.
+  RangeRequestSupportType accept_range;
 
   // The HTTP connection type.
   net::HttpResponseInfo::ConnectionInfo connection_info;
diff --git a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java
index a19a438..4d5a22eb 100644
--- a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java
+++ b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java
@@ -33,7 +33,8 @@
         FeatureConstants.DOWNLOAD_INFOBAR_DOWNLOADS_ARE_FASTER_FEATURE,
         FeatureConstants.TAB_GROUPS_QUICKLY_COMPARE_PAGES_FEATURE,
         FeatureConstants.TAB_GROUPS_TAP_TO_SEE_ANOTHER_TAB_FEATURE,
-        FeatureConstants.TAB_GROUPS_YOUR_TABS_ARE_TOGETHER_FEATURE})
+        FeatureConstants.TAB_GROUPS_YOUR_TABS_ARE_TOGETHER_FEATURE,
+        FeatureConstants.FEED_CARD_MENU_FEATURE})
 @Retention(RetentionPolicy.SOURCE)
 public @interface FeatureConstants {
     String DOWNLOAD_PAGE_FEATURE = "IPH_DownloadPage";
@@ -108,4 +109,9 @@
      * An IPH feature to show on tab switcher cards with multiple tab thumbnails.
      */
     String TAB_GROUPS_YOUR_TABS_ARE_TOGETHER_FEATURE = "IPH_TabGroupsYourTabsTogether";
+
+    /**
+     * An IPH feature to show on a card menu on the FeedNewTabPage.
+     */
+    String FEED_CARD_MENU_FEATURE = "IPH_FeedCardMenu";
 }
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc
index d94c6079..131c0a2 100644
--- a/components/feature_engagement/public/feature_constants.cc
+++ b/components/feature_engagement/public/feature_constants.cc
@@ -49,6 +49,8 @@
     "IPH_DownloadInfoBarDownloadContinuing", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kIPHDownloadInfoBarDownloadsAreFasterFeature{
     "IPH_DownloadInfoBarDownloadsAreFaster", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kIPHFeedCardMenuFeature{"IPH_FeedCardMenu",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kIPHHomePageButtonFeature{
     "IPH_HomePageButton", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kIPHHomepageTileFeature{"IPH_HomepageTile",
diff --git a/components/feature_engagement/public/feature_constants.h b/components/feature_engagement/public/feature_constants.h
index b23570f..529c25f1 100644
--- a/components/feature_engagement/public/feature_constants.h
+++ b/components/feature_engagement/public/feature_constants.h
@@ -38,6 +38,7 @@
 extern const base::Feature kIPHDownloadSettingsFeature;
 extern const base::Feature kIPHDownloadInfoBarDownloadContinuingFeature;
 extern const base::Feature kIPHDownloadInfoBarDownloadsAreFasterFeature;
+extern const base::Feature kIPHFeedCardMenuFeature;
 extern const base::Feature kIPHHomePageButtonFeature;
 extern const base::Feature kIPHHomepageTileFeature;
 extern const base::Feature kIPHKeyboardAccessoryAddressFillingFeature;
diff --git a/components/feature_engagement/public/feature_list.cc b/components/feature_engagement/public/feature_list.cc
index d2661454..640ac03d 100644
--- a/components/feature_engagement/public/feature_list.cc
+++ b/components/feature_engagement/public/feature_list.cc
@@ -34,6 +34,7 @@
     &kIPHDownloadSettingsFeature,
     &kIPHDownloadInfoBarDownloadContinuingFeature,
     &kIPHDownloadInfoBarDownloadsAreFasterFeature,
+    &kIPHFeedCardMenuFeature,
     &kIPHHomePageButtonFeature,
     &kIPHHomepageTileFeature,
     &kIPHKeyboardAccessoryAddressFillingFeature,
diff --git a/components/feature_engagement/public/feature_list.h b/components/feature_engagement/public/feature_list.h
index 871d861..eedcfd63 100644
--- a/components/feature_engagement/public/feature_list.h
+++ b/components/feature_engagement/public/feature_list.h
@@ -74,6 +74,7 @@
                        "IPH_DownloadInfoBarDownloadContinuing");
 DEFINE_VARIATION_PARAM(kIPHDownloadInfoBarDownloadsAreFasterFeature,
                        "IPH_DownloadInfoBarDownloadsAreFaster");
+DEFINE_VARIATION_PARAM(kIPHFeedCardMenuFeature, "IPH_FeedCardMenu");
 DEFINE_VARIATION_PARAM(kIPHHomePageButtonFeature, "IPH_HomePageButton");
 DEFINE_VARIATION_PARAM(kIPHHomepageTileFeature, "IPH_HomepageTile");
 DEFINE_VARIATION_PARAM(kIPHKeyboardAccessoryAddressFillingFeature,
@@ -137,6 +138,7 @@
         VARIATION_ENTRY(kIPHDownloadSettingsFeature),
         VARIATION_ENTRY(kIPHDownloadInfoBarDownloadContinuingFeature),
         VARIATION_ENTRY(kIPHDownloadInfoBarDownloadsAreFasterFeature),
+        VARIATION_ENTRY(kIPHFeedCardMenuFeature),
         VARIATION_ENTRY(kIPHHomePageButtonFeature),
         VARIATION_ENTRY(kIPHHomepageTileFeature),
         VARIATION_ENTRY(kIPHKeyboardAccessoryAddressFillingFeature),
diff --git a/components/guest_view/browser/guest_view_base.cc b/components/guest_view/browser/guest_view_base.cc
index 693a508..08e1e0e6 100644
--- a/components/guest_view/browser/guest_view_base.cc
+++ b/components/guest_view/browser/guest_view_base.cc
@@ -645,7 +645,7 @@
 bool GuestViewBase::HandleKeyboardEvent(
     WebContents* source,
     const content::NativeWebKeyboardEvent& event) {
-  if (!attached())
+  if (!attached() || !embedder_web_contents()->GetDelegate())
     return false;
 
   // Send the keyboard events back to the embedder to reprocess them.
diff --git a/components/language/core/common/language_experiments.cc b/components/language/core/common/language_experiments.cc
index 5e86dfe..d38d2e46 100644
--- a/components/language/core/common/language_experiments.cc
+++ b/components/language/core/common/language_experiments.cc
@@ -23,9 +23,13 @@
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kUseFluentLanguageModel{"UseFluentLanguageModel",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kNotifySyncOnLanguageDetermined{
+    "NotifySyncOnLanguageDetermined", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Base feature for Translate desktop UI experiment
 const base::Feature kUseButtonTranslateBubbleUI{
     "UseButtonTranslateBubbleUI", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Params:
 const char kBackoffThresholdKey[] = "backoff_threshold";
 const char kOverrideModelKey[] = "override_model";
diff --git a/components/language/core/common/language_experiments.h b/components/language/core/common/language_experiments.h
index 529b28e..e7b0e98e 100644
--- a/components/language/core/common/language_experiments.h
+++ b/components/language/core/common/language_experiments.h
@@ -33,6 +33,9 @@
 extern const char kOverrideModelDefaultValue[];
 extern const char kBackoffThresholdKey[];
 
+// Notify sync to update data on language determined.
+extern const base::Feature kNotifySyncOnLanguageDetermined;
+
 // This feature uses the existing UI for translate bubble.
 extern const base::Feature kUseButtonTranslateBubbleUI;
 
diff --git a/components/management_strings.grdp b/components/management_strings.grdp
index 3c2d4c69..5156b468 100644
--- a/components/management_strings.grdp
+++ b/components/management_strings.grdp
@@ -69,40 +69,7 @@
     <message name="IDS_MANAGEMENT_ACCOUNT_MANAGED_BY" desc="Message indicating that the account is enterprise enrolled to be managed by an administrator">
       Your account is managed by <ph name="ENROLLMENT_DOMAIN">$1<ex>example.com</ex></ph>.
     </message>
-    <!-- Device managed clarification -->
-    <message name="IDS_MANAGEMENT_DEVICE_MANAGED_CLARIFICATION" desc="First part of the sentence of the managed device clarification.">
-      As the manager of this device, <ph name="ENROLLMENT_DOMAIN">$1<ex>example.com</ex></ph>:
-    </message>
-    <message name="IDS_MANAGEMENT_DEVICE_MANAGED_SETUP" desc="Continuation of the managed device clarification sentence explaining device setup management.">
-      Can remotely change your device setup.
-    </message>
-    <message name="IDS_MANAGEMENT_DEVICE_MANAGED_DATA" desc="Continuation of the managed device clarification sentence explaining device user data management.">
-      Controls how your device data is handled. How Google processes and uses your device data is determined by your device manager.
-    </message>
-    <!-- Device and account managed clarification -->
-    <message name="IDS_MANAGEMENT_DEVICE_AND_ACCOUNT_MANAGED_CLARIFICATION" desc="First part of the sentence of the managed device and account clarification, when enrollment domain equals account domain.">
-      As the manager of your device and account, <ph name="ENROLLMENT_DOMAIN">$1<ex>example.com</ex></ph>:
-    </message>
-    <message name="IDS_MANAGEMENT_DEVICE_AND_ACCOUNT_MANAGED_SETUP" desc="Continuation of the managed device and account clarification sentence explaining device and account setup management.">
-      Can remotely change your device and account setup.
-    </message>
-    <message name="IDS_MANAGEMENT_DEVICE_AND_ACCOUNT_MANAGED_DATA" desc="Continuation of the managed device and account clarification sentence explaining device and account user data management.">
-      Controls how your device and account data is handled. How Google processes and uses your device and account data is determined by your device and account manager.
-    </message>
   </if>
-  <!-- Account managed clarification -->
-  <message name="IDS_MANAGEMENT_ACCOUNT_MANAGED_CLARIFICATION_UNKNOWN_DOMAIN" desc="First part of the sentence for managed account clarification. This is a header over a bullet point list.">
-    The company, school, or organization that manages this account:
-  </message>
-  <message name="IDS_MANAGEMENT_ACCOUNT_MANAGED_CLARIFICATION" desc="First part of the sentence for managed account clarification. This is a header over a bullet point list.">
-    As the manager of your account, <ph name="ACCOUNT_DOMAIN">$1<ex>example.com</ex></ph>:
-  </message>
-  <message name="IDS_MANAGEMENT_ACCOUNT_MANAGED_SETUP" desc="Continuation of the managed account clarification sentence explaining account setup management.">
-    Can remotely change your account setup.
-  </message>
-  <message name="IDS_MANAGEMENT_ACCOUNT_MANAGED_DATA" desc="Continuation of the managed account clarification sentence explaining account user data management.">
-    Controls how your account data is handled. How Google processes and uses your account data is determined by your account manager.
-  </message>
 
   <if expr="chromeos">
     <!-- Strings related to Local trust roots section -->
diff --git a/components/mirroring/browser/single_client_video_capture_host.cc b/components/mirroring/browser/single_client_video_capture_host.cc
index b67c71e..2db124e 100644
--- a/components/mirroring/browser/single_client_video_capture_host.cc
+++ b/components/mirroring/browser/single_client_video_capture_host.cc
@@ -51,7 +51,7 @@
 
 SingleClientVideoCaptureHost::SingleClientVideoCaptureHost(
     const std::string& device_id,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     DeviceLauncherCreateCallback callback)
     : device_id_(device_id),
       type_(type),
diff --git a/components/mirroring/browser/single_client_video_capture_host.h b/components/mirroring/browser/single_client_video_capture_host.h
index 08b4e313..7a8acf2 100644
--- a/components/mirroring/browser/single_client_video_capture_host.h
+++ b/components/mirroring/browser/single_client_video_capture_host.h
@@ -38,7 +38,7 @@
   using DeviceLauncherCreateCallback = base::RepeatingCallback<
       std::unique_ptr<content::VideoCaptureDeviceLauncher>()>;
   SingleClientVideoCaptureHost(const std::string& device_id,
-                               blink::MediaStreamType type,
+                               blink::mojom::MediaStreamType type,
                                DeviceLauncherCreateCallback callback);
   ~SingleClientVideoCaptureHost() override;
 
@@ -99,7 +99,7 @@
                                  double consumer_resource_utilization);
 
   const std::string device_id_;
-  const blink::MediaStreamType type_;
+  const blink::mojom::MediaStreamType type_;
   const DeviceLauncherCreateCallback device_launcher_callback_;
 
   media::mojom::VideoCaptureObserverPtr observer_;
diff --git a/components/mirroring/browser/single_client_video_capture_host_unittest.cc b/components/mirroring/browser/single_client_video_capture_host_unittest.cc
index 8632066..a6c58afd 100644
--- a/components/mirroring/browser/single_client_video_capture_host_unittest.cc
+++ b/components/mirroring/browser/single_client_video_capture_host_unittest.cc
@@ -55,7 +55,7 @@
 
   // content::VideoCaptureDeviceLauncher implementation.
   void LaunchDeviceAsync(const std::string& device_id,
-                         blink::MediaStreamType stream_type,
+                         blink::mojom::MediaStreamType stream_type,
                          const VideoCaptureParams& params,
                          base::WeakPtr<VideoFrameReceiver> receiver,
                          base::OnceClosure connection_lost_cb,
@@ -168,7 +168,7 @@
  public:
   SingleClientVideoCaptureHostTest() : weak_factory_(this) {
     auto host_impl = std::make_unique<SingleClientVideoCaptureHost>(
-        std::string(), blink::MediaStreamType::MEDIA_GUM_TAB_VIDEO_CAPTURE,
+        std::string(), blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE,
         base::BindRepeating(
             &SingleClientVideoCaptureHostTest::CreateDeviceLauncher,
             base::Unretained(this)));
diff --git a/components/nacl/broker/nacl_broker_manifest.cc b/components/nacl/broker/nacl_broker_manifest.cc
index 9cdf5447..0cec707 100644
--- a/components/nacl/broker/nacl_broker_manifest.cc
+++ b/components/nacl/broker/nacl_broker_manifest.cc
@@ -27,6 +27,7 @@
                                 "content.mojom.ChildControl",
                                 "content.mojom.ChildHistogramFetcherFactory",
                                 "content.mojom.ResourceUsageReporter",
+                                "tracing.mojom.BackgroundTracingAgent",
                             })
 
           .Build()};
diff --git a/components/nacl/loader/nacl_loader_manifest.cc b/components/nacl/loader/nacl_loader_manifest.cc
index e6c5f61..a3ca0395 100644
--- a/components/nacl/loader/nacl_loader_manifest.cc
+++ b/components/nacl/loader/nacl_loader_manifest.cc
@@ -19,13 +19,15 @@
           // service, but they are requested from all child processes by common
           // browser-side code. We list them here to make the Service Manager
           // happy.
-          .ExposeCapability(
-              "browser",
-              std::set<const char*>{
-                  "IPC.mojom.ChannelBootstrap", "content.mojom.Child",
-                  "content.mojom.ChildControl",
-                  "content.mojom.ChildHistogramFetcherFactory",
-                  "content.mojom.ResourceUsageReporter"})
+          .ExposeCapability("browser",
+                            std::set<const char*>{
+                                "IPC.mojom.ChannelBootstrap",
+                                "content.mojom.Child",
+                                "content.mojom.ChildControl",
+                                "content.mojom.ChildHistogramFetcherFactory",
+                                "content.mojom.ResourceUsageReporter",
+                                "tracing.mojom.BackgroundTracingAgent",
+                            })
           .Build()};
   return *manifest;
 }
diff --git a/components/offline_pages/content/background_loader/background_loader_contents.cc b/components/offline_pages/content/background_loader/background_loader_contents.cc
index eccb73f1c..e42fce6 100644
--- a/components/offline_pages/content/background_loader/background_loader_contents.cc
+++ b/components/offline_pages/content/background_loader/background_loader_contents.cc
@@ -127,7 +127,7 @@
 bool BackgroundLoaderContents::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   return false;  // No permissions granted.
 }
 
diff --git a/components/offline_pages/content/background_loader/background_loader_contents.h b/components/offline_pages/content/background_loader/background_loader_contents.h
index cbed236..77638f0 100644
--- a/components/offline_pages/content/background_loader/background_loader_contents.h
+++ b/components/offline_pages/content/background_loader/background_loader_contents.h
@@ -87,7 +87,7 @@
       content::MediaResponseCallback callback) override;
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type) override;
+                                  blink::mojom::MediaStreamType type) override;
   void AdjustPreviewsStateForNavigation(
       content::WebContents* web_contents,
       content::PreviewsState* previews_state) override;
diff --git a/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc b/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
index 6b28793..455ee6d 100644
--- a/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
+++ b/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
@@ -163,8 +163,8 @@
       blink::MediaStreamRequestType::MEDIA_DEVICE_ACCESS /* request_type */,
       std::string() /* requested_audio_device_id */,
       std::string() /* requested_video_device_id */,
-      blink::MediaStreamType::MEDIA_GUM_TAB_AUDIO_CAPTURE /* audio_type */,
-      blink::MediaStreamType::MEDIA_GUM_TAB_VIDEO_CAPTURE /* video_type */,
+      blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE /* audio_type */,
+      blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE /* video_type */,
       false /* disable_local_echo */);
   contents()->RequestMediaAccessPermission(
       nullptr /* contents */, request /* request */,
@@ -182,7 +182,7 @@
 TEST_F(BackgroundLoaderContentsTest, CheckMediaAccessPermissionFalse) {
   ASSERT_FALSE(contents()->CheckMediaAccessPermission(
       nullptr /* contents */, GURL::EmptyGURL() /* security_origin */,
-      blink::MediaStreamType::MEDIA_GUM_TAB_VIDEO_CAPTURE /* type */));
+      blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE /* type */));
 }
 
 TEST_F(BackgroundLoaderContentsTest, AdjustPreviewsState) {
diff --git a/components/omnibox/browser/bookmark_provider_unittest.cc b/components/omnibox/browser/bookmark_provider_unittest.cc
index 077dd3dd..815c9052 100644
--- a/components/omnibox/browser/bookmark_provider_unittest.cc
+++ b/components/omnibox/browser/bookmark_provider_unittest.cc
@@ -195,7 +195,7 @@
   for (size_t i = 0; i < base::size(bookmark_provider_test_data); ++i) {
     const BookmarksTestInfo& cur(bookmark_provider_test_data[i]);
     const GURL url(cur.url);
-    model_->AddURL(other_node, other_node->child_count(),
+    model_->AddURL(other_node, other_node->children().size(),
                    base::ASCIIToUTF16(cur.title), url);
   }
 }
diff --git a/components/policy/core/common/BUILD.gn b/components/policy/core/common/BUILD.gn
index 186d6b9..b0c3b6b 100644
--- a/components/policy/core/common/BUILD.gn
+++ b/components/policy/core/common/BUILD.gn
@@ -74,8 +74,6 @@
     "cloud/machine_level_user_cloud_policy_metrics.h",
     "cloud/machine_level_user_cloud_policy_store.cc",
     "cloud/machine_level_user_cloud_policy_store.h",
-    "cloud/policy_header_service.cc",
-    "cloud/policy_header_service.h",
     "cloud/policy_value_validator.h",
     "cloud/realtime_reporting_job_configuration.cc",
     "cloud/realtime_reporting_job_configuration.h",
@@ -342,7 +340,6 @@
     "cloud/cloud_policy_service_unittest.cc",
     "cloud/cloud_policy_validator_unittest.cc",
     "cloud/device_management_service_unittest.cc",
-    "cloud/policy_header_service_unittest.cc",
     "cloud/user_info_fetcher_unittest.cc",
     "generate_policy_source_unittest.cc",
     "plist_writer_unittest.cc",
diff --git a/components/policy/core/common/cloud/cloud_policy_constants.cc b/components/policy/core/common/cloud/cloud_policy_constants.cc
index 0d531f4..ed899e4c 100644
--- a/components/policy/core/common/cloud/cloud_policy_constants.cc
+++ b/components/policy/core/common/cloud/cloud_policy_constants.cc
@@ -86,8 +86,6 @@
 
 }  // namespace dm_protocol
 
-const char kChromePolicyHeader[] = "Chrome-Policy-Posture";
-
 const uint8_t kPolicyVerificationKey[] = {
     0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
     0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00,
diff --git a/components/policy/core/common/cloud/cloud_policy_constants.h b/components/policy/core/common/cloud/cloud_policy_constants.h
index d4474c0..9c5f01d 100644
--- a/components/policy/core/common/cloud/cloud_policy_constants.h
+++ b/components/policy/core/common/cloud/cloud_policy_constants.h
@@ -75,9 +75,6 @@
 
 }  // namespace dm_protocol
 
-// The header used to transmit the policy ID for this client.
-POLICY_EXPORT extern const char kChromePolicyHeader[];
-
 // Public half of the verification key that is used to verify that policy
 // signing keys are originating from DM server.
 POLICY_EXPORT std::string GetPolicyVerificationKey();
diff --git a/components/policy/core/common/cloud/policy_header_service.cc b/components/policy/core/common/cloud/policy_header_service.cc
deleted file mode 100644
index f2f71bd..0000000
--- a/components/policy/core/common/cloud/policy_header_service.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/policy/core/common/cloud/policy_header_service.h"
-
-#include <utility>
-
-#include "base/base64.h"
-#include "base/json/json_writer.h"
-#include "base/sequenced_task_runner.h"
-#include "base/values.h"
-#include "components/policy/core/common/cloud/cloud_policy_constants.h"
-#include "components/policy/core/common/cloud/cloud_policy_store.h"
-#include "components/policy/proto/device_management_backend.pb.h"
-
-namespace {
-const char kUserDMTokenKey[] = "user_dmtoken";
-const char kUserPolicyTokenKey[] = "user_policy_token";
-const char kVerificationKeyHashKey[] = "verification_key_id";
-}
-
-namespace policy {
-
-PolicyHeaderService::PolicyHeaderService(
-    const std::string& server_url,
-    const std::string& verification_key_hash,
-    CloudPolicyStore* user_policy_store)
-    : server_url_(server_url),
-      verification_key_hash_(verification_key_hash),
-      user_policy_store_(user_policy_store) {
-  user_policy_store_->AddObserver(this);
-  policy_header_ = CreateHeaderValue();
-}
-
-PolicyHeaderService::~PolicyHeaderService() {
-  user_policy_store_->RemoveObserver(this);
-}
-
-void PolicyHeaderService::AddPolicyHeaders(const GURL& url,
-                                           AddHeaderFunction add_header) const {
-  if (policy_header_.empty())
-    return;
-
-  if (url.spec().compare(0, server_url_.size(), server_url_) != 0)
-    return;
-
-  std::move(add_header).Run(kChromePolicyHeader, policy_header_);
-}
-
-std::string PolicyHeaderService::CreateHeaderValue() {
-  // If we have no user policy or no token, return an empty header.
-  if (!user_policy_store_->has_policy() ||
-      !user_policy_store_->policy()->has_request_token()) {
-    return "";
-  }
-
-  // Generate a Base64-encoded header of the form:
-  // {
-  //   user_dmtoken: <dm_token>
-  //   user_policy_token: <policy_token>
-  //   verification_key_hash: <key_hash>
-  // }
-  std::string user_dm_token = user_policy_store_->policy()->request_token();
-  base::DictionaryValue value;
-  value.SetString(kUserDMTokenKey, user_dm_token);
-  if (user_policy_store_->policy()->has_policy_token()) {
-    value.SetString(kUserPolicyTokenKey,
-                    user_policy_store_->policy()->policy_token());
-  }
-  if (!verification_key_hash_.empty())
-    value.SetString(kVerificationKeyHashKey, verification_key_hash_);
-
-  // TODO(atwilson): add user_policy_token once the server starts sending it
-  // down (http://crbug.com/326799).
-  std::string json;
-  base::JSONWriter::Write(value, &json);
-  DCHECK(!json.empty());
-
-  // Base64-encode the result so we can include it in a header.
-  std::string encoded;
-  base::Base64Encode(json, &encoded);
-  return encoded;
-}
-
-void PolicyHeaderService::OnStoreLoaded(CloudPolicyStore* store) {
-  policy_header_ = CreateHeaderValue();
-}
-
-void PolicyHeaderService::OnStoreError(CloudPolicyStore* store) {
-  // Do nothing on errors.
-}
-
-void PolicyHeaderService::SetHeaderForTest(const std::string& new_header) {
-  policy_header_ = new_header;
-}
-
-void PolicyHeaderService::SetServerURLForTest(const std::string& server_url) {
-  server_url_ = server_url;
-}
-
-}  // namespace policy
diff --git a/components/policy/core/common/cloud/policy_header_service.h b/components/policy/core/common/cloud/policy_header_service.h
deleted file mode 100644
index 5f339f55..0000000
--- a/components/policy/core/common/cloud/policy_header_service.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_POLICY_CORE_COMMON_CLOUD_POLICY_HEADER_SERVICE_H_
-#define COMPONENTS_POLICY_CORE_COMMON_CLOUD_POLICY_HEADER_SERVICE_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/callback_forward.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "components/policy/core/common/cloud/cloud_policy_store.h"
-#include "components/policy/policy_export.h"
-#include "url/gurl.h"
-
-namespace policy {
-
-// Per-profile service used to keep track of policy changes.
-// TODO(atwilson): Move to components/policy once CloudPolicyStore is moved.
-class POLICY_EXPORT PolicyHeaderService : public CloudPolicyStore::Observer {
- public:
-  // |device_policy_store| can be null on platforms that do not support
-  // device policy. Both |user_policy_store| and |device_policy_store| must
-  // outlive this object.
-  PolicyHeaderService(const std::string& server_url,
-                      const std::string& verification_key_hash,
-                      CloudPolicyStore* user_policy_store);
-  ~PolicyHeaderService() override;
-
-  // Update navigation request headers with the policy header if the URL matches
-  // |server_url_|.
-  // Note: To avoid depending on content/public, a callback is used here.
-  // It correspond to NavigationHandle::SetRequestHeader(). It is called
-  // synchronously.
-  using AddHeaderFunction =
-      base::OnceCallback<void(const std::string& /* header_name*/,
-                              const std::string& /* header_value */)>;
-  void AddPolicyHeaders(const GURL& url, AddHeaderFunction add_header) const;
-
-  // Overridden CloudPolicyStore::Observer methods:
-  void OnStoreLoaded(CloudPolicyStore* store) override;
-  void OnStoreError(CloudPolicyStore* store) override;
-
-  // Test-only routines used to inject the server URL/headers at runtime.
-  void SetHeaderForTest(const std::string& new_header);
-  void SetServerURLForTest(const std::string& server_url);
-
- private:
-  // Generate a policy header based on the currently loaded policy.
-  std::string CreateHeaderValue();
-
-  // The current policy header value.
-  std::string policy_header_;
-
-  // URL of the policy server.
-  std::string server_url_;
-
-  // Identifier for the verification key this Chrome instance is using.
-  std::string verification_key_hash_;
-
-  // Weak pointer to the User-level policy store.
-  CloudPolicyStore* user_policy_store_;
-
-  DISALLOW_COPY_AND_ASSIGN(PolicyHeaderService);
-};
-
-}  // namespace policy
-
-#endif  // COMPONENTS_POLICY_CORE_COMMON_CLOUD_POLICY_HEADER_SERVICE_H_
diff --git a/components/policy/core/common/cloud/policy_header_service_unittest.cc b/components/policy/core/common/cloud/policy_header_service_unittest.cc
deleted file mode 100644
index e5fc35a..0000000
--- a/components/policy/core/common/cloud/policy_header_service_unittest.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/policy/core/common/cloud/policy_header_service.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/base64.h"
-#include "base/json/json_reader.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/values.h"
-#include "components/policy/core/common/cloud/cloud_policy_constants.h"
-#include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
-#include "components/policy/proto/device_management_backend.pb.h"
-#include "net/http/http_request_headers.h"
-#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace policy {
-using enterprise_management::PolicyData;
-
-namespace {
-const char kDMServerURL[] = "http://server_url";
-const char kPolicyHeaderName[] = "Chrome-Policy-Posture";
-
-class TestCloudPolicyStore : public MockCloudPolicyStore {
- public:
-  void SetPolicy(std::unique_ptr<PolicyData> policy) {
-    policy_ = std::move(policy);
-    // Notify observers.
-    NotifyStoreLoaded();
-  }
-};
-
-class PolicyHeaderServiceTest : public testing::Test {
- public:
-  PolicyHeaderServiceTest() {
-    task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>();
-  }
-  ~PolicyHeaderServiceTest() override {}
-
-  void SetUp() override {
-    service_.reset(new PolicyHeaderService(kDMServerURL,
-                                           kPolicyVerificationKeyHash,
-                                           &user_store_));
-  }
-
-  void TearDown() override {
-    task_runner_->RunUntilIdle();
-    // Helper should outlive the service.
-    service_.reset();
-  }
-
-  void ValidateHeader(const std::string& header_name,
-                      const std::string& header_value,
-                      const std::string& expected_dmtoken,
-                      const std::string& expected_policy_token) {
-    EXPECT_EQ(kPolicyHeaderName, header_name);
-
-    // Decode the base64 value into JSON.
-    std::string decoded;
-    base::Base64Decode(header_value, &decoded);
-
-    // Parse the JSON.
-    std::unique_ptr<base::Value> value =
-        base::JSONReader::ReadDeprecated(decoded);
-    EXPECT_TRUE(value);
-    base::DictionaryValue* dict;
-    EXPECT_TRUE(value->GetAsDictionary(&dict));
-
-    // Read the values and verify them vs the expected values.
-    std::string dm_token;
-    dict->GetString("user_dmtoken", &dm_token);
-    EXPECT_EQ(dm_token, expected_dmtoken);
-    std::string policy_token;
-    dict->GetString("user_policy_token", &policy_token);
-    EXPECT_EQ(policy_token, expected_policy_token);
-  }
-
-  base::test::ScopedTaskEnvironment task_environment_;
-  std::unique_ptr<PolicyHeaderService> service_;
-  TestCloudPolicyStore user_store_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-};
-
-void AddHeader(std::string* header_name,
-               std::string* header_value,
-               const std::string& name,
-               const std::string& value) {
-  *header_name = name;
-  *header_value = value;
-}
-
-void ExpectNoHeaderAdded(const std::string&, const std::string&) {
-  FAIL();
-}
-
-}  // namespace
-
-TEST_F(PolicyHeaderServiceTest, TestCreationAndShutdown) {
-  // Just tests that the objects can be created and shutdown properly.
-  EXPECT_TRUE(service_);
-}
-
-TEST_F(PolicyHeaderServiceTest, TestWithAndWithoutPolicyHeader) {
-  // Set policy.
-  std::unique_ptr<PolicyData> policy(new PolicyData());
-  std::string expected_dmtoken = "expected_dmtoken";
-  std::string expected_policy_token = "expected_dmtoken";
-  policy->set_request_token(expected_dmtoken);
-  policy->set_policy_token(expected_policy_token);
-  user_store_.SetPolicy(std::move(policy));
-  task_runner_->RunUntilIdle();
-  std::string header_name, header_value;
-  service_->AddPolicyHeaders(
-      GURL(kDMServerURL),
-      base::BindOnce(AddHeader, base::Unretained(&header_name),
-                     base::Unretained(&header_value)));
-  ValidateHeader(header_name, header_value, expected_dmtoken,
-                 expected_policy_token);
-
-  // Now blow away the policy data.
-  user_store_.SetPolicy(std::unique_ptr<PolicyData>());
-  task_runner_->RunUntilIdle();
-  service_->AddPolicyHeaders(GURL(kDMServerURL),
-                             base::BindOnce(&ExpectNoHeaderAdded));
-}
-
-TEST_F(PolicyHeaderServiceTest, NoHeaderOnNonMatchingURL) {
-  service_->SetHeaderForTest("new_header");
-  service_->AddPolicyHeaders(GURL("http://non-matching.com"),
-                             base::BindOnce(&ExpectNoHeaderAdded));
-}
-
-TEST_F(PolicyHeaderServiceTest, HeaderChange) {
-  std::string new_header = "new_header";
-  service_->SetHeaderForTest(new_header);
-  std::string header_name, header_value;
-  service_->AddPolicyHeaders(
-      GURL(kDMServerURL),
-      base::BindOnce(AddHeader, base::Unretained(&header_name),
-                     base::Unretained(&header_value)));
-  EXPECT_EQ(kPolicyHeaderName, header_name);
-  EXPECT_EQ(new_header, header_value);
-}
-
-TEST_F(PolicyHeaderServiceTest, ChangeToNoHeader) {
-  service_->SetHeaderForTest("");
-  service_->AddPolicyHeaders(GURL(kDMServerURL),
-                             base::BindOnce(&ExpectNoHeaderAdded));
-}
-
-}  // namespace policy
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index ba9176e..3554200 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -1512,6 +1512,8 @@
   optional string package_name = 4;
   // If available, the version of the Debian package belonging to this App.
   optional string package_version = 5;
+  // If available, a hash of the package belonging to this App.
+  optional string package_hash = 6;
 }
 
 message CrostiniStatus {
@@ -2209,6 +2211,10 @@
     INSTALLATION_FINISHED = 11;
     // Package installation failed
     INSTALLATION_FAILED = 12;
+    // Direct install scheduled
+    DIRECT_INSTALL = 13;
+    // No more regular attempts to install
+    CLOUDDPC_MAIN_LOOP_FAILED = 14;
   }
 
   // Enumerates the possible changes in session state.
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index b1581fbe..8879c62 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -107,7 +107,7 @@
 #
 #   Currently supported product names:
 #       'chrome_frame', 'chrome_os', 'android', 'webview_android',
-#       'chrome.win', 'chrome.linux', 'chrome.mac', 'chrome.*'
+#       'chrome.win', 'chrome.win7', 'chrome.linux', 'chrome.mac', 'chrome.*'
 #     For example if 'chrome.*:5-10' is specified for a policy, then it should
 #     be read as:
 #       'chrome.linux:5-10', 'chrome.mac:5-10', 'chrome.win:5-10'
@@ -1099,7 +1099,7 @@
       'name': 'DefaultBrowserSettingEnabled',
       'type': 'main',
       'schema': { 'type': 'boolean' },
-      'supported_on': ['chrome.*:11-'],
+      'supported_on': ['chrome.win7:11-', 'chrome.mac:11-', 'chrome.linux:11-'],
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
@@ -16116,10 +16116,14 @@
 
   'messages': {
     # Messages that are not associated to any policies.
-    'win_supported_win7': {
+    'win_supported_all': {
       'desc': '''A label specifying the oldest possible compatible version of Windows. This text will appear right next to a label containing the text 'Supported on:'.''',
       'text': '''Microsoft Windows 7 or later'''
     },
+    'win_supported_win7': {
+      'desc': '''A label specifying the policy compatibles with Windows 7. This text will appear right next to a label containing the text 'Supported on:'.''',
+      'text': '''Microsoft Windows 7'''
+    },
     'mac_chrome_preferences': {
       'desc': '''A text indicating in Mac OS X Workgroup Manager, that currently the preferences of Chromium are being edited''',
       'text': '''<ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> preferences'''
diff --git a/components/policy/resources/policy_templates_am.xtb b/components/policy/resources/policy_templates_am.xtb
index 132eb23..80beb94 100644
--- a/components/policy/resources/policy_templates_am.xtb
+++ b/components/policy/resources/policy_templates_am.xtb
@@ -3776,13 +3776,6 @@
 <translation id="8050080920415773384">ቤተኛ ህትመት</translation>
 <translation id="8053580360728293758">ነባሪው የህትመት ቀለም ሁነታውን ይሽራል። ሁነታው የማይገኝ ከሆነ ይህ መመሪያ ችላ ይባላል።</translation>
 <translation id="8059164285174960932">የርቀት መዳረሻ ደንበኞች የማረጋገጫ ማስመሰያቸውን ማግኘት ያለባቸው ዩአርኤል</translation>
-<translation id="806280865577636339">ይህ መመሪያ ወደ እውነተ ከተዋቀረ የተደራሽነት አማራጮች ሁልጊዜ በስርዓት መሳቢያ ምናሌው ላይ ይታያሉ።
-
-          ይህ መመሪያ ወደ ሐሰት ከተዋቀረ የተደራሽነት አማራጮች በጭራሽ በስርዓት መሳቢያ ምናሌው ላይ አይታዩም።
-
-          ይህን መመሪያ ካዋቀሩት ተጠቃሚዎች ሊቀይሩት ወይም ሊሽሩት አይችሉም።
-
-          ይህ መመሪያ እንዳልተዋቀረ ከተተወ የተደራሽነት አማራጮች በስርዓቱ የመሳቢያ ምናሌው ላይ አይታዩም፣ ነገር ግን ተጠቃሚው የተደራሽነት አማራጮቹ በቅንብሮች ገጽ በኩል እንዲታይ ማድረግ ይችላል።</translation>
 <translation id="806523868782250975">የሚቀናበሩ የእልባቶች ዝርዝርን ያዋቅራል።
 
       መመሪያው የዕልባቶች ዝርዝር ሲሆን እያንዳንዱ ዕልባት የዕልባቱን ስም እና ዒላማ ዕልባት የ«<ph name="NAME" />» እና «<ph name="URL_LABEL" />» ቁልፎችን የያዘ መዝገበ-ቃላት ነው። አንድ ዕልባት ያለ«<ph name="URL_LABEL" />» ቁልፍ ግን ከላይ እንደተገለጸው (አንዳንዶቹ እንደገና አቃፊዎች ሊሆኑ የሚችሉ) የዕልባቶች ዝርዝር በያዘ ተጨማሪ የ«<ph name="CHILDREN" />» ቁልፍ በመግለጽ ንዑስ አቃፊ ሊዋቀር ይችላል። <ph name="PRODUCT_NAME" /> ያልተሟሉ ዩአርኤሎች በኦምኒቦክሱ እንደገቡ ቆጥሮ ያርማቸዋል፣ ለምሳሌ «<ph name="GOOGLE_COM" />» ወደ «<ph name="HTTPS_GOOGLE_COM" />» ይቀየራል።
diff --git a/components/policy/resources/policy_templates_ar.xtb b/components/policy/resources/policy_templates_ar.xtb
index 6df6866..5689105 100644
--- a/components/policy/resources/policy_templates_ar.xtb
+++ b/components/policy/resources/policy_templates_ar.xtb
@@ -3669,13 +3669,6 @@
 <translation id="8050080920415773384">الطباعة الأصلية</translation>
 <translation id="8053580360728293758">يُلغَى الوضع التقائي للطباعة بالألوان. وفي حال عدم توفُّر هذا الوضع، سيتم تجاهل هذه السياسة.</translation>
 <translation id="8059164285174960932">‏عنوان URL الذي ينبغي أن يحصل العملاء الذين يتمتعون بإمكانية الوصول عن بعد على رمز المصادقة المميز من خلاله</translation>
-<translation id="806280865577636339">‏في حال ضبط هذه السياسة على "True"، ستظهر خيارات تمكين الوصول دائمًا في قائمة لوحة النظام.
-
-          وفي حال ضبط هذه السياسة على "False"، لن تظهر خيارات تمكين الوصول مطلقًا في قائمة لوحة النظام.
-
-          في حال تحديد هذه السياسة، لن يتمكَّن المستخدمون من تغييرها أو إلغائها.
-
-          وفي حال ترك هذه السياسة بدون تحديد، لن تظهر خيارات تمكين الوصول في قائمة لوحة النظام، ولكن يمكن أن يظهرها المستخدم من خلال صفحة "الإعدادات".</translation>
 <translation id="806523868782250975">‏تتيح هذه السياسة ضبط قائمة بالإشارات المرجعية المُدارة.
 
       تتكون هذه السياسة من قائمة بالإشارات المرجعية حيث تمثِّل كل إشارة مرجعية قاموسًا يحتوي على المفاتيح "<ph name="NAME" />" و"<ph name="URL_LABEL" />" التي تحمل اسم الإشارة المرجعية وهدفها. قد يتم ضبط مجلد فرعي من خلال تحديد إشارة مرجعية بدون مفتاح "<ph name="URL_LABEL" />" ولكن مع مفتاح "<ph name="CHILDREN" />" إضافي الذي يحتوي بدوره على قائمة بالإشارات المرجعية كما هو مُحدَّد أعلاه (البعض منها قد يكون مجلدات مرة أخرى). سيعمل <ph name="PRODUCT_NAME" /> على تعديل عناوين URL غير الكاملة كما لو تم إرسالها من خلال مربَّع متعدد الاستخدامات، مثلاً، يتحول"<ph name="GOOGLE_COM" />" إلى "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_bg.xtb b/components/policy/resources/policy_templates_bg.xtb
index 59a1b35..0f0e3170 100644
--- a/components/policy/resources/policy_templates_bg.xtb
+++ b/components/policy/resources/policy_templates_bg.xtb
@@ -3684,13 +3684,6 @@
 <translation id="8050080920415773384">Стандартно отпечатване</translation>
 <translation id="8053580360728293758">Заменя стандартния режим за цветно отпечатване. Ако режимът не е наличен, това правило се пренебрегва.</translation>
 <translation id="8059164285174960932">URL адрес, откъдето клиентските програми за отдалечен достъп да получат означението си за удостоверяване</translation>
-<translation id="806280865577636339">Ако за това правило е зададено true, опциите за достъпност са винаги видими в менюто на системната област.
-
-          При false те никога не се показват там.
-
-          Ако зададете правилото, потребителите няма да могат да го променят или отменят.
-
-          В случай че то не е зададено, опциите за достъпност няма да се показват в менюто на системната област, но потребителят може да ги направи видими чрез страницата „Настройки“.</translation>
 <translation id="806523868782250975">Служи за конфигуриране на списък с управлявани отметки.
 
       Правилото представлява списък с отметки. Всяка от тях е представена като речник с ключовете <ph name="NAME" /> и <ph name="URL_LABEL" />, съдържащи името и целевия й адрес. Може да конфигурирате подпапка, като дефинирате отметка без ключ <ph name="URL_LABEL" />, но с допълнителен ключ <ph name="CHILDREN" />, който съдържа списък с отметки в гореописания формат (някои от които също може да са папки). <ph name="PRODUCT_NAME" /> променя непълните URL адреси, както при изпращането им чрез полето за всичко. Например <ph name="GOOGLE_COM" /> се преобразува в <ph name="HTTPS_GOOGLE_COM" />.
diff --git a/components/policy/resources/policy_templates_bn.xtb b/components/policy/resources/policy_templates_bn.xtb
index 7b97261..66c8e587 100644
--- a/components/policy/resources/policy_templates_bn.xtb
+++ b/components/policy/resources/policy_templates_bn.xtb
@@ -3798,13 +3798,6 @@
 <translation id="8050080920415773384">নেটিভ প্রিন্ট</translation>
 <translation id="8053580360728293758">ডিফল্ট রঙিন মোডে প্রিন্টিংকে ওভাররাইড করে। মোডটি উপলভ্য না থাকলে এই নীতি উপেক্ষা করা হয়।</translation>
 <translation id="8059164285174960932">রিমোট অ্যাক্সেস ক্লায়েন্টদের যেখানে তাদের যাচাইকরণ টোকেন নেওয়া উচিত সেখানকার URL</translation>
-<translation id="806280865577636339">যদি এই নীতি সত্যতে সেট করা থাকে তাহলে ব্যবহারযোগ্যতার বিকল্পগুলি সবসময় সিস্টেম ট্রের মেনুতে দেখানো হয়।
-
-          যদি এই নীতি মিথ্যাতে সেট করা থাকে তাহলে ব্যবহারযোগ্যতার বিকল্পগুলি কখনই সিস্টেম ট্রের মেনুতে দেখানো হয় না।
-
-          আপনি যদি এই নীতি সেট করেন তাহলে ব্যবহারকারীরা এটি পরিবর্তন বা ওভাররাইড করতে পারেন না।
-
-         যদি এই নীতি সেট না করে ফেলে রাখা হয়, তাহলে ব্যবহারযোগ্যতার বিকল্পগুলি সিস্টেম ট্রের মেনুতে দেখানো হবে না, কিন্তু ব্যবহারকারী ব্যবহারযোগ্যতার বিকল্পগুলি সেটিংস পৃষ্ঠার মাধ্যমে দেখানো হবে কিনা তা নির্ধারণ করতে পারেন।</translation>
 <translation id="806523868782250975">পরিচালিত বুকমার্কের একটি তালিকা কনফিগার করে।
 
       নীতি বুকমার্কগুলির একটি তালিকা দ্বারা গঠিত যেখানে প্রতিটি বুকমার্কে তার নাম এবং লক্ষ্যকে সূচিত করে "<ph name="NAME" />" এবং "<ph name="URL_LABEL" />" কী সম্বলিত একটি ডিকশনারি রয়েছে। একটি সাবফোল্ডার কনফিগার করা যেতে পারে এমন একটি বুকমার্ক সংজ্ঞায়নের মাধ্যমে যা "<ph name="URL_LABEL" />" কী বিহীন অথচ একটি অতিরিক্ত "<ph name="CHILDREN" />" কী বিশিষ্ট যা নিজেই উপরে সংজ্ঞায়িত বুকমার্কগুলির একটি তালিকা ধারণ করে (যার কয়েকটি আবার ফোল্ডার হতে পারে)। <ph name="PRODUCT_NAME" /> অসম্পূর্ণ ইউআরএলগুলিকে এমনভাবে সংশোধন করবে যেন সেগুলিকে ওমনিবক্সের মাধ্যমে জমা দেওয়া হয়েছে। যেমন, "<ph name="GOOGLE_COM" />" হয়ে যায় "<ph name="HTTPS_GOOGLE_COM" />"।
diff --git a/components/policy/resources/policy_templates_cs.xtb b/components/policy/resources/policy_templates_cs.xtb
index 006cd5ead..a680be4 100644
--- a/components/policy/resources/policy_templates_cs.xtb
+++ b/components/policy/resources/policy_templates_cs.xtb
@@ -3719,13 +3719,6 @@
 <translation id="8050080920415773384">Nativní tisk</translation>
 <translation id="8053580360728293758">Přepíše výchozí barevný režim tisku. Pokud režim není k dispozici, zásada bude ignorována.</translation>
 <translation id="8059164285174960932">Adresa URL, u které by klienti vzdáleného připojení měli obdržet ověřovací token</translation>
-<translation id="806280865577636339">Pokud je tato zásada nastavená na hodnotu true, v nabídce hlavního panelu systému se vždy zobrazí možnosti usnadnění přístupu.
-
-          Pokud je tato zásada nastavená na hodnotu false, možnosti usnadnění přístupu se v nabídce hlavního panelu systému nikdy nezobrazí.
-
-          Pokud tuto zásadu nastavíte, uživatelé ji nebudou moci změnit ani přepsat.
-
-          Pokud tato zásada není nastavená, možnosti usnadnění přístupu se v nabídce hlavního panelu systému nezobrazí, ale uživatelé jejich zobrazení mohou aktivovat na stránce Nastavení.</translation>
 <translation id="806523868782250975">Umožňuje nakonfigurovat seznam spravovaných záložek.
 
       Hodnotou této zásady je seznam záložek. Každá záložka je slovník, který obsahuje název záložky a cílovou adresu URL (klíče „<ph name="NAME" />“ a „<ph name="URL_LABEL" />“). Podsložku lze nakonfigurovat definováním záložky bez klíče „<ph name="URL_LABEL" />“, ale s dodatečným klíčem „<ph name="CHILDREN" />“, který sám obsahuje seznam záložek podle výše uvedeného popisu (z nichž některé mohou být opět složky). Neúplné adresy URL doplní <ph name="PRODUCT_NAME" /> stejně, jako kdybyste je odeslali pomocí omniboxu. Například adresa <ph name="GOOGLE_COM" /> bude doplněna na <ph name="HTTPS_GOOGLE_COM" />.
diff --git a/components/policy/resources/policy_templates_da.xtb b/components/policy/resources/policy_templates_da.xtb
index e65bec3e..75a767cc 100644
--- a/components/policy/resources/policy_templates_da.xtb
+++ b/components/policy/resources/policy_templates_da.xtb
@@ -3696,13 +3696,6 @@
 <translation id="8050080920415773384">Indbygget udskrivning</translation>
 <translation id="8053580360728293758">Tilsidesætter farvetilstanden for standardudskrivning. Hvis tilstanden ikke er tilgængelig, ignoreres denne politik.</translation>
 <translation id="8059164285174960932">Den webadresse, hvor klienten til fjernadgang skal hente deres godkendelsestoken</translation>
-<translation id="806280865577636339">Hvis denne politik er angivet som sand, vises indstillingerne for Tilgængelighed altid i systembakkemenuen.
-
-          Hvis denne politik er angivet som falsk, vises indstillingerne for Tilgængelighed aldrig i systembakkemenuen.
-
-          Hvis du konfigurerer denne politik, kan brugerne ikke ændre eller tilsidesætte den.
-
-          Hvis denne politik ikke angives, vises indstillingerne for Tilgængelighed ikke i systembakkemenuen, men brugeren kan få vist indstillingerne for Tilgængelighed via siden Indstillinger.</translation>
 <translation id="806523868782250975">Konfigurerer en liste over administrerede bogmærker.
 
       Politikken består af en liste over bogmærker, hvor hvert bogmærke er en ordbog med nøglerne "<ph name="NAME" />" og "<ph name="URL_LABEL" />", som indeholder bogmærkets navn og mål. En undermappe kan konfigureres ved at definere et bogmærke uden en "<ph name="URL_LABEL" />"-nøgle, men med en ekstra "<ph name="CHILDREN" />"-nøgle, der selv indeholder en liste over bogmærker som defineret ovenfor (nogle af disse kan også være mapper). <ph name="PRODUCT_NAME" /> ændrer ufuldstændige webadresser på samme måde, som hvis de blev indsendt via omnifeltet. "<ph name="GOOGLE_COM" />" bliver f.eks. til "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_de.xtb b/components/policy/resources/policy_templates_de.xtb
index a6bbcad7..f32529a 100644
--- a/components/policy/resources/policy_templates_de.xtb
+++ b/components/policy/resources/policy_templates_de.xtb
@@ -3685,13 +3685,6 @@
 <translation id="8050080920415773384">Natives Drucken</translation>
 <translation id="8053580360728293758">Dadurch wird der standardmäßige Farbdruckmodus überschrieben. Wenn der Modus nicht verfügbar ist, wird die Richtlinie ignoriert.</translation>
 <translation id="8059164285174960932">URL, unter der Remotezugriff-Clients ihr Authentifizierungs-Token abrufen sollten</translation>
-<translation id="806280865577636339">Ist diese Richtlinie auf "true" gesetzt, werden die Optionen für Bedienungshilfen immer im Taskleistenmenü angezeigt.
-
-          Wenn die Richtlinie auf "false" gesetzt ist, werden die Optionen für Bedienungshilfen nie im Taskleistenmenü angezeigt.
-
-          Wenn Sie diese Richtlinie konfigurieren, kann sie vom Nutzer nicht geändert oder überschrieben werden.
-
-          Ist diese Richtlinie nicht konfiguriert, erscheinen keine Optionen für Bedienungshilfen im Taskleistenmenü, der Nutzer kann deren Anzeige jedoch auf der Seite "Einstellungen" aktivieren.</translation>
 <translation id="806523868782250975">Konfiguriert eine Liste verwalteter Lesezeichen.
 
       Die Richtlinie besteht aus einer Lesezeichenliste, bei der jedes Lesezeichen ein Wörterbuch mit den Schlüsseln "<ph name="NAME" />" und "<ph name="URL_LABEL" />" ist, die den Namen und das Ziel des Lesezeichens enthalten. Ein Unterordner kann konfiguriert werden, indem ein Lesezeichen ohne den Schlüssel "<ph name="URL_LABEL" />", dafür aber mit einem zusätzlichen Schlüssel "<ph name="CHILDREN" />" festgelegt wird, der selbst eine Liste von Lesezeichen enthält, die wie oben beschrieben definiert sind. Einige davon können wiederum Ordner sein. <ph name="PRODUCT_NAME" /> ergänzt unvollständige URLs automatisch so wie in der Omnibox. Beispielsweise wird aus "<ph name="GOOGLE_COM" />" dann "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_el.xtb b/components/policy/resources/policy_templates_el.xtb
index b078467..5d5d6c26 100644
--- a/components/policy/resources/policy_templates_el.xtb
+++ b/components/policy/resources/policy_templates_el.xtb
@@ -3819,13 +3819,6 @@
 <translation id="8050080920415773384">Εγγενής εκτύπωση</translation>
 <translation id="8053580360728293758">Παρακάμπτει την προεπιλεγμένη λειτουργία έγχρωμης εκτύπωσης. Εάν η λειτουργία δεν είναι διαθέσιμη, αυτή η πολιτική αγνοείται.</translation>
 <translation id="8059164285174960932">URL όπου οι εφαρμογές πελάτες απομακρυσμένης πρόσβασης πρέπει να αποκτούν το διακριτικό ελέγχου ταυτότητας</translation>
-<translation id="806280865577636339">Εάν αυτή η πολιτική οριστεί ως αληθής, οι επιλογές προσβασιμότητας εμφανίζονται πάντα στο μενού της περιοχής ειδοποιήσεων.
-
-          Εάν αυτή η πολιτική οριστεί ως μη αληθής, οι επιλογές προσβασιμότητας δεν εμφανίζονται ποτέ στο μενού της περιοχής ειδοποιήσεων.
-
-          Εάν ρυθμίσετε αυτήν την πολιτική, οι χρήστες δεν μπορούν να την αλλάξουν ή να την παρακάμψουν.
-
-          Εάν δεν ορίσετε αυτήν την πολιτική, οι επιλογές προσβασιμότητας δεν θα εμφανίζονται στο μενού της περιοχής ειδοποιήσεων, αλλά ο χρήστης θα μπορεί να εμφανίζει τις επιλογές προσβασιμότητας μέσω της σελίδας "Ρυθμίσεις".</translation>
 <translation id="806523868782250975">Διαμορφώνει μια λίστα των διαχειριζόμενων σελιδοδεικτών.
 
       Η πολιτική αποτελείται από μια λίστα σελιδοδεικτών και κάθε σελιδοδείκτης αποτελεί ένα λεξικό που περιέχει τα κλειδιά "<ph name="NAME" />" και "<ph name="URL_LABEL" />", τα οποία εμπεριέχουν το όνομα του σελιδοδείκτη του και τον στόχο του. Ένας υποφάκελος μπορεί να διαμορφωθεί με τον καθορισμό σελιδοδείκτη χωρίς κλειδί "<ph name="URL_LABEL" />", αλλά με επιπλέον κλειδί "<ph name="CHILDREN" />" το οποίο περιέχει μια λίστα σελιδοδεικτών όπως καθορίζεται παραπάνω (κάποιοι από τους οποίους μπορεί να είναι και πάλι φάκελοι). Το <ph name="PRODUCT_NAME" /> τροποποιεί τα μη ολοκληρωμένα URL σαν να είχαν υποβληθεί μέσω του κύριου πλαισίου. Για παράδειγμα, το "<ph name="GOOGLE_COM" />" γίνεται <ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_en-GB.xtb b/components/policy/resources/policy_templates_en-GB.xtb
index fb68386..32e35e4 100644
--- a/components/policy/resources/policy_templates_en-GB.xtb
+++ b/components/policy/resources/policy_templates_en-GB.xtb
@@ -3807,13 +3807,6 @@
 <translation id="8050080920415773384">Native Printing</translation>
 <translation id="8053580360728293758">Overrides default printing colour mode. If the mode is unavailable, this policy is ignored.</translation>
 <translation id="8059164285174960932">URL where remote access clients should obtain their authentication token</translation>
-<translation id="806280865577636339">If this policy is set to true, Accessibility options always appear in system tray menu.
-
-          If this policy is set to false, Accessibility options never appear in system tray menu.
-
-          If you set this policy, users cannot change or override it.
-
-          If this policy is left unset, Accessibility options will not appear in the system tray menu, but the user can cause the Accessibility options to appear via the Settings page.</translation>
 <translation id="806523868782250975">Configures a list of managed bookmarks.
 
       The policy consists of a list of bookmarks whereas each bookmark is a dictionary containing the keys '<ph name="NAME" />' and '<ph name="URL_LABEL" />' which hold the bookmark's name and its target. A subfolder may be configured by defining a bookmark without an '<ph name="URL_LABEL" />' key but with an additional '<ph name="CHILDREN" />' key, which itself contains a list of bookmarks as defined above (some of which may be folders again). <ph name="PRODUCT_NAME" /> amends incomplete URLs as if they were submitted via the Omnibox, for example '<ph name="GOOGLE_COM" />' becomes '<ph name="HTTPS_GOOGLE_COM" />'.
diff --git a/components/policy/resources/policy_templates_es-419.xtb b/components/policy/resources/policy_templates_es-419.xtb
index f8f58e9..0a70400 100644
--- a/components/policy/resources/policy_templates_es-419.xtb
+++ b/components/policy/resources/policy_templates_es-419.xtb
@@ -3698,13 +3698,6 @@
 <translation id="8050080920415773384">Impresión nativa</translation>
 <translation id="8053580360728293758">Anula el modo predeterminado de impresión a color. Si el modo no está disponible, se ignorará esta política.</translation>
 <translation id="8059164285174960932">URL donde los clientes de acceso remoto deben obtener el token de autenticación</translation>
-<translation id="806280865577636339">Si esta política se configura como verdadera, las opciones de accesibilidad aparecerán siempre en el menú de la bandeja del sistema.
-
-          Si esta política se configura como falsa, las opciones de accesibilidad no aparecerán nunca en el menú de la bandeja del sistema.
-
-          Si configuras esta política, los usuarios no podrán modificarla ni anularla.
-
-          Si no configuras esta política, las opciones de accesibilidad no aparecerán en el menú de la bandeja del sistema, pero el usuario podrá modificar esto en la página de configuración.</translation>
 <translation id="806523868782250975">Configura una lista de favoritos administrados.
 
       La política consiste en una lista de favoritos, mientras que cada uno de ellos es un diccionario que contiene las claves "<ph name="NAME" />" y "<ph name="URL_LABEL" />" con el nombre y objetivo del favorito. Puede configurarse una subcarpeta al definir un favorito sin una clave "<ph name="URL_LABEL" />", pero con una clave "<ph name="CHILDREN" />" adicional que contenga una lista de favoritos, como se define anteriormente (algunos pueden ser carpetas). <ph name="PRODUCT_NAME" /> modifica las URL incompletas como si fueran enviadas a través del cuadro multifunción, p. ej., "<ph name="GOOGLE_COM" />" cambia a "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_es.xtb b/components/policy/resources/policy_templates_es.xtb
index 4e56d7d..34e744d7 100644
--- a/components/policy/resources/policy_templates_es.xtb
+++ b/components/policy/resources/policy_templates_es.xtb
@@ -3817,13 +3817,6 @@
 <translation id="8050080920415773384">Impresión nativa</translation>
 <translation id="8053580360728293758">Anula el modo predeterminado de impresión en color. Si el modo no está disponible, se ignora la política.</translation>
 <translation id="8059164285174960932">URL en la que los clientes de acceso remoto deben obtener su token de autenticación</translation>
-<translation id="806280865577636339">Si se asigna el valor "True" a esta política, las opciones de accesibilidad siempre aparecerán en el menú de la bandeja del sistema.
-
-          Si se le asigna el valor "False", las opciones de accesibilidad nunca aparecerán en el menú de la bandeja del sistema.
-
-          Si asignas un valor a esta política, los usuarios no podrán cambiarla ni anularla.
-
-          Si no asignas ningún valor a esta política, no aparecerán opciones de accesibilidad en el menú de la bandeja del sistema, pero los usuarios podrán hacer que aparezcan opciones de accesibilidad a través de la página de configuración.</translation>
 <translation id="806523868782250975">Permite configurar una lista de marcadores administrados.
 
       La política consta de una lista de marcadores, mientras que cada marcador es un diccionario que contiene las claves "<ph name="NAME" />" y "<ph name="URL_LABEL" />", que definen el nombre y la URL de destino del marcador. Para configurar una subcarpeta, puedes definir un marcador sin la clave "<ph name="URL_LABEL" />" y utilizar una clave "<ph name="CHILDREN" />" adicional que contenga una lista de marcadores en sí misma como se ha definido más arriba (algunos de los cuales pueden ser carpetas de nuevo). <ph name="PRODUCT_NAME" /> modifica las URL incompletas como si se hubieran enviado a través del omnibox. Por ejemplo, "<ph name="GOOGLE_COM" />" se convertirá en "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_et.xtb b/components/policy/resources/policy_templates_et.xtb
index 8535800..5a425e2 100644
--- a/components/policy/resources/policy_templates_et.xtb
+++ b/components/policy/resources/policy_templates_et.xtb
@@ -3822,13 +3822,6 @@
 <translation id="8050080920415773384">Printimise omaprotsess</translation>
 <translation id="8053580360728293758">Alistab printimisel vaikimisi kasutatava värvirežiimi. Reeglit eiratakse, kui režiim ei ole saadaval.</translation>
 <translation id="8059164285174960932">URL, kust kaugjuurdepääsuga kliendid peaksid saama autentimismärgi</translation>
-<translation id="806280865577636339">Kui reegli väärtuseks määratakse Tõene, kuvatakse juurdepääsuvalikud alati süsteemisalve menüüs.
-
-          Kui reegli väärtuseks määratakse Väär, ei kuvata juurdepääsuvalikuid süsteemisalve menüüs kunagi.
-
-          Kui määrate reegli, ei saa kasutajad seda muuta ega alistada.
-
-          Kui jätate reegli määramata, ei kuvata juurdepääsuvalikuid süsteemisalve menüüs, kuid kasutaja saab juurdepääsuvalikud kuvada lehe Seaded kaudu.</translation>
 <translation id="806523868782250975">Seadistab hallatud järjehoidjate loendi.
 
       Reegel koosneb järjehoidjate loendist, kusjuures iga järjehoidja on sõnastik, mis sisaldab võtmeid „<ph name="NAME" />” ja „<ph name="URL_LABEL" />”, kus hoiustatakse järjehoidja nime ning selle sihtmärki. Alamkausta saab seadistada, määrates järjehoidja ilma võtmeta „<ph name="URL_LABEL" />”, kuid lisavõtmega „<ph name="CHILDREN" />”, mis sisaldab ise ülalmääratud järjehoidjate loendit (millest mõned võivad olla kaustad). <ph name="PRODUCT_NAME" /> täiendab poolikuid URL-e, nagu need oleksid edastatud omnikastikese kaudu, nt aadressist „<ph name="GOOGLE_COM" />” saab „<ph name="HTTPS_GOOGLE_COM" />”.
diff --git a/components/policy/resources/policy_templates_fa.xtb b/components/policy/resources/policy_templates_fa.xtb
index 992b4da9..0b61d58 100644
--- a/components/policy/resources/policy_templates_fa.xtb
+++ b/components/policy/resources/policy_templates_fa.xtb
@@ -3692,13 +3692,6 @@
 <translation id="8050080920415773384">چاپ محلی</translation>
 <translation id="8053580360728293758">حالت پیش‌فرض رنگ چاپ را لغو می‌کند. اگر این حالت دردسترس نباشد، این خط‌مشی نادیده گرفته می‌شود.</translation>
 <translation id="8059164285174960932">نشانی اینترنتی که در آن سرویس‌گیرنده‌های دسترسی راه دور باید رمز احراز هویت دریافت کنند</translation>
-<translation id="806280865577636339">اگر این خط‌مشی روی درست تنظیم شود، همیشه گزینه‌های «دسترس‌پذیری» در منوی سینی سیستم نمایش داده می‌شود.
-
-          اگر این خط‌مشی روی نادرست تنظیم شود، گزینه‌های دسترس‌پذیری هرگز در منوی سینی سیستم نمایش داده نمی‌شود.
-
-          اگر این خط‌مشی را تنظیم کنید، کاربران نمی‌توانند آن را تغییر دهند یا لغو کنند.
-
-          اگر این خط‌مشی تنظیم نشود، گزینه‌های «دسترس‌پذیری» در منوی سینی سیستم نمایش داده نمی‌شود اما کاربر می‌تواند کاری کند که گزینه‌های «دسترس‌پذیری» ازطریق صفحه «تنظیمات» نمایش داده شود.</translation>
 <translation id="806523868782250975">‏فهرستی از نشانک‌های مدیریت‌شده را پیکربندی می‌کند.
 
       این خط‌مشی شامل فهرستی از نشانک‌ها است، درحالی‌که هر نشانک یک فرهنگ لغت حاوی کلیدهای «<ph name="NAME" />» و «<ph name="URL_LABEL" />» است که نام و هدف نشانک را مشخص می‌کنند. می‌توان با تعریف نشانکی بدون کلید «<ph name="URL_LABEL" />» اما با کلید «<ph name="CHILDREN" />» اضافه‌ای که خود شامل فهرستی از نشانک‌هایی است که در بالا تعریف شده‌اند (ممکن است بعضی از آن‌ها دوباره پوشه‌هایی تشکیل دهند)، یک زیرپوشه پیکربندی کرد. <ph name="PRODUCT_NAME" /> نشانی‌های وب ناکامل را اصلاح می‌کند، درست مثل اینکه که ازطریق Omnibox ارسال شده باشند (برای مثال «<ph name="GOOGLE_COM" />» به «<ph name="HTTPS_GOOGLE_COM" />» تبدیل می‌شود).
diff --git a/components/policy/resources/policy_templates_fi.xtb b/components/policy/resources/policy_templates_fi.xtb
index 5d6ae49..0d6c096 100644
--- a/components/policy/resources/policy_templates_fi.xtb
+++ b/components/policy/resources/policy_templates_fi.xtb
@@ -3721,13 +3721,6 @@
 <translation id="8050080920415773384">Natiivitulostus</translation>
 <translation id="8053580360728293758">Ohittaa tulostuksen oletusväritilan. Jos tila ei ole käytettävissä, tämä käytäntö ohitetaan.</translation>
 <translation id="8059164285174960932">URL-osoite, josta etäkäytön asiakkaiden tulisi saada todennustunnus</translation>
-<translation id="806280865577636339">Jos tämän käytännön arvo on tosi, esteettömyysasetukset näkyvät aina ilmaisinalueen valikossa.
-
-          Jos tämän käytännön arvo on epätosi, esteettömyysasetuksia ei koskaan lisätä ilmaisinalueen valikkoon.
-
-          Jos tämä käytäntö on määritetty, käyttäjät eivät voi muuttaa tai ohittaa sitä.
-
-          Jos tätä käytäntöä ei ole määritetty, esteettömyysasetuksia ei lisätä ilmaisinalueen valikkoon, mutta käyttäjä voi asettaa ne näkyviin Asetukset-sivulta.</translation>
 <translation id="806523868782250975">Määrittää hallinnoitujen kirjanmerkkien luettelon.
 
       Tämä käytäntö sisältää kirjanmerkkiluettelon, jonka jokainen kirjanmerkki on tietohakemisto ja sisältää avaimet <ph name="NAME" /> ja <ph name="URL_LABEL" />. Nämä avaimet sisältävät kirjanmerkin nimen ja kohteen. Voit määrittää alikansion määrittelemällä kirjanmerkin, jolla on avaimen <ph name="URL_LABEL" /> sijaan lisäavain <ph name="CHILDREN" />. Tämä lisäavain sisältää erillisen, yllä kuvatun kaltaisen kirjanmerkkiluettelon (jonka kirjanmerkeistä osa voi olla kansioita). Jos URL-osoitteissa on puutteita, <ph name="PRODUCT_NAME" /> muokkaa niitä samaan tapaan kuin omniboxin kautta lähetettyjä osoitteita. Esimerkiksi <ph name="GOOGLE_COM" /> muutetaan muotoon <ph name="HTTPS_GOOGLE_COM" />.
diff --git a/components/policy/resources/policy_templates_fil.xtb b/components/policy/resources/policy_templates_fil.xtb
index e9626309..6fcdc804 100644
--- a/components/policy/resources/policy_templates_fil.xtb
+++ b/components/policy/resources/policy_templates_fil.xtb
@@ -3821,13 +3821,6 @@
 <translation id="8050080920415773384">Native na Pag-print</translation>
 <translation id="8053580360728293758">Ino-override ang default na printing color mode. Kung hindi available ang mode, binabalewala ang patakarang ito.</translation>
 <translation id="8059164285174960932">URL kung saan dapat makuha ng mga client ng malayuang pag-access ang kanilang token sa pagpapatotoo</translation>
-<translation id="806280865577636339">Kung itatakda ang patakarang ito sa true, palaging lalabas ang mga opsyon sa Accessibility sa menu ng system tray.
-
-          Kung itatakda ang patakarang ito sa false, hindi kailanman lalabas ang mga opsyon sa Accessibility sa menu ng system tray.
-
-          Kung itatakda mo ang patakarang ito, hindi ito mababago o mao-override ng mga user.
-
-          Kung hahayaang hindi nakatakda ang patakarang ito, hindi lalabas sa menu ng system tray ang mga opsyon sa Accessibility, ngunit mapapalabas ng user ang mga opsyon sa Accessibility gamit ang page ng Mga Setting.</translation>
 <translation id="806523868782250975">Nagko-configure ng listahan ng mga pinapamahalaang bookmark.
 
       Binubuo ang patakaran ng isang listahan ng mga bookmark kung saan ang bawat bookmark ay isang diksyunaryong naglalaman ng mga key na "<ph name="NAME" />" at "<ph name="URL_LABEL" />" na naglalaman ng pangalan ng bookmark at ng mga target nito. Maaaring i-configure ang isang subfolder sa pamamagitan ng pagtukoy ng bookmark na walang key na "<ph name="URL_LABEL" />" ngunit may karagdagang key na "<ph name="CHILDREN" />" na naglalaman mismo ng listahan ng mga bookmark tulad ng nakasaad sa itaas (ang ilan sa mga ito ay puwedeng mga folder muli). Inaamyendahan ng <ph name="PRODUCT_NAME" /> ang mga hindi kumpletong URL na para bang isinumite ang mga ito sa pamamagitan ng Omnibox, halimbawa ang "<ph name="GOOGLE_COM" />" ay magiging "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_fr.xtb b/components/policy/resources/policy_templates_fr.xtb
index 7fdf6eff5..f26a0b4 100644
--- a/components/policy/resources/policy_templates_fr.xtb
+++ b/components/policy/resources/policy_templates_fr.xtb
@@ -3818,13 +3818,6 @@
 <translation id="8050080920415773384">Impression en mode natif</translation>
 <translation id="8053580360728293758">Remplace le mode d'impression couleur par défaut. Si le mode est indisponible, cette règle est ignorée.</translation>
 <translation id="8059164285174960932">URL où les clients d'accès à distance doivent obtenir leur jeton d'authentification</translation>
-<translation id="806280865577636339">Si cette règle est définie sur "True", les options d'accessibilité s'affichent en permanence dans le menu de la barre d'état système.
-
-          Si cette règle est définie sur "False", les options d'accessibilité ne s'affichent jamais dans le menu de la barre d'état système.
-
-          Si vous définissez cette règle, les utilisateurs n'ont pas la possibilité de la modifier ni de l'ignorer.
-
-          Si vous ne définissez pas cette règle, les options d'accessibilité ne s'affichent pas dans le menu de la barre d'état système, mais l'utilisateur peut activer leur affichage via la page "Paramètres".</translation>
 <translation id="806523868782250975">Configure une liste de favoris gérés.
 
       Cette règle se compose d'une liste de favoris, et chaque favori est un dictionnaire comprenant les clés "<ph name="NAME" />" et "<ph name="URL_LABEL" />" qui contiennent le nom du favori et son URL cible. Vous pouvez également configurer un sous-dossier en définissant un favori sans clé "<ph name="URL_LABEL" />", mais avec une clé "<ph name="CHILDREN" />" supplémentaire qui contient elle-même une liste de favoris, comme défini ci-dessus (certains peuvent de nouveau être des dossiers). <ph name="PRODUCT_NAME" /> modifie les URL incomplètes comme si elles avaient été envoyées via l'omnibox. Par exemple, "<ph name="GOOGLE_COM" />" devient "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_gu.xtb b/components/policy/resources/policy_templates_gu.xtb
index 376e037..e6c1b82 100644
--- a/components/policy/resources/policy_templates_gu.xtb
+++ b/components/policy/resources/policy_templates_gu.xtb
@@ -3789,13 +3789,6 @@
 <translation id="8050080920415773384">મૂળ પ્રિન્ટિંગ</translation>
 <translation id="8053580360728293758">ડિફૉલ્ટ પ્રિન્ટિંગ કલર મોડને ઓવરરાઇડ કરે છે. મોડ ઉપલબ્ધ ન હોય તો આ નીતિ અવગણવામાં આવે છે.</translation>
 <translation id="8059164285174960932">જ્યાં રીમોટ ઍક્સેસ ક્લાઇન્ટ્સને તેમના પ્રમાણીકરણ ટોકન મળવા જોઈએ તે URL</translation>
-<translation id="806280865577636339">જો આ નીતિને true પર સેટ કરેલી હોય, ઍક્સેસિબિલિટી વિકલ્પો હંમેશાં સિસ્ટમ ટ્રે મેનૂમાં દેખાય છે.
-
-          જો આ નીતિ false પર સેટ કરેલી હોય, તો સિસ્ટમ ટ્રે મેનૂમાં ઍક્સેસિબિલિટી વિકલ્પો ક્યારેય દેખાતાં નથી.
-
-          જો તમે નીતિને સેટ કરો છો, તો વપરાશકર્તાઓ તેને બદલી અથવા ઓવરરાઇડ કરી શકતાં નથી.
-
-          જો આ નીતિ સેટ કર્યા વિનાની રહેવા દીધી છે, પરંતુ સેટિંગ્સ પેજ મારફતે વપરાશકર્તા ઍક્સેસિબિલિટી વિકલ્પો બતાવી શકે છે.</translation>
 <translation id="806523868782250975">મેનેજ બુકમાર્કની સૂચિને ગોઠવે છે.
 
       નીતિમાં બુકમાર્કની એક સૂચિ રહેલ છે જ્યાં દરેક બુકમાર્ક "<ph name="NAME" />" અને "<ph name="URL_LABEL" />" કી ધરાવતો એક શબ્દકોશ છે, જે બુકમાર્કના નામ અને તેના લક્ષ્ય સમાવે છે. પેટાફોલ્ડરને "<ph name="URL_LABEL" />" કી વગરના બુકમાર્કને નિર્ધારિત કરીને પરંતુ વધારાની "<ph name="CHILDREN" />" કી સાથે જે પોતે ઉપર નિર્ધારિત કર્યા પ્રમાણે બુકમાર્કની સૂચિ સમાવે છે (જેમાંના કેટલાક ફરી ફોલ્ડર હોઈ શકે છે) ગોઠવવામાં આવી શકે છે. <ph name="PRODUCT_NAME" /> અધૂરા URLમાં સુધાર કરે છે જે રીતે તેઓ ઑમ્નિબૉક્સ મારફતે સબમિટ કર્યા હોય, ઉદાહરણ તરીકે "<ph name="GOOGLE_COM" />" એ "<ph name="HTTPS_GOOGLE_COM" />" બની જાય છે.
diff --git a/components/policy/resources/policy_templates_hi.xtb b/components/policy/resources/policy_templates_hi.xtb
index aa2bb6d..9f677c9 100644
--- a/components/policy/resources/policy_templates_hi.xtb
+++ b/components/policy/resources/policy_templates_hi.xtb
@@ -3814,13 +3814,6 @@
 <translation id="8050080920415773384">स्थानीय प्रिंटिंग</translation>
 <translation id="8053580360728293758">डिफ़ॉल्ट रूप से कलर मोड में प्रिंट करना ओवरराइड कर देती है. अगर मोड उपलब्ध नहीं है तो नीति को अनदेखा कर दिया जाता है.</translation>
 <translation id="8059164285174960932">वह यूआरएल जहां से दूर से एक्सेस करने वाले क्‍लाइंट को अपनी पहचान करवाने वाला टोकन मिलना चाहिए</translation>
-<translation id="806280865577636339">अगर यह नीति सही पर सेट होती है, तो सिस्टम ट्रे मेनू में हमेशा ही सुलभता विकल्प दिखाई देंगे.
-
-          अगर यह नीति गलत पर सेट होती है, तो सिस्टम ट्रे मेनू में कभी भी सुलभता विकल्प दिखाई नहीं देंगे.
-
-          अगर आप यह नीति सेट करते हैं, तो इस्तेमाल करने वाले लोग न तो इसे बदल सकते हैं, न ही ओवरराइड कर सकते हैं.
-
-          अगर यह नीति सेट नहीं करते हैं, तो सिस्टम ट्रे मेनू में सुलभता विकल्प दिखाई नहीं देंगे, लेकिन इस्तेमाल करने वाले सेटिंग पेज के ज़रिए सुलभता विकल्पों को दिखा सकते हैं.</translation>
 <translation id="806523868782250975">यह नीति प्रबंधित बुकमार्क की एक सूची कॉन्फ़िगर करती है.
 
       इस नीति में उन बुकमार्क की एक सूची होती है जहां हर बुकमार्क एक ऐसी निर्देशिका है जिसमें "<ph name="NAME" />" और "<ph name="URL_LABEL" />" कुंजियां हैं. साथ ही, जिनमें बुकमार्क का नाम और उसके टारगेट होते हैं. किसी "<ph name="URL_LABEL" />" कुंजी के बिना बुकमार्क के बारे में बताने वाला एक सबफ़ोल्डर कॉन्फ़िगर किया जा सकता है लेकिन इसके लिए एक अलग "<ph name="CHILDREN" />" कुंजी बतानी होगी जिसमें ऊपर बताए गए बुकमार्क की सूची शामिल हो (जिनमें से कुछ फ़ोल्डर ही हो सकते हैं). <ph name="PRODUCT_NAME" /> अधूरे यूआरएल में इस तरह बदलाव करता है जैसे कि वे खोज वाली पट्टी (ऑम्निबॉक्स) से सबमिट किए गए हों, उदाहरण के लिए<ph name="GOOGLE_COM" />" बदलकर "<ph name="HTTPS_GOOGLE_COM" />" बन जाता है.
diff --git a/components/policy/resources/policy_templates_hr.xtb b/components/policy/resources/policy_templates_hr.xtb
index 36db6090..4744e48 100644
--- a/components/policy/resources/policy_templates_hr.xtb
+++ b/components/policy/resources/policy_templates_hr.xtb
@@ -3706,13 +3706,6 @@
 <translation id="8050080920415773384">Nativni ispis</translation>
 <translation id="8053580360728293758">Nadjačava način boje zadanog ispisa. Pravilo se zanemaruje ako način nije dostupan.</translation>
 <translation id="8059164285174960932">URL na kojem bi klijenti za daljinski pristup trebali dobiti token za autentifikaciju</translation>
-<translation id="806280865577636339">Ako se to pravilo postavi na true, opcije pristupačnosti uvijek se prikazuju na izborniku trake sustava.
-
-          Ako se pravilo postavi na false, opcije pristupačnosti nikad se neće prikazivati na izborniku trake sustava.
-
-          Ako postavite to pravilo, korisnici ga neće moći promijeniti ni nadjačati.
-
-          Ako ne postavite to pravilo, opcije pristupačnosti neće se prikazivati na izborniku trake sustava, ali ih korisnik može prikazati putem stranice Postavke.</translation>
 <translation id="806523868782250975">Konfigurira popis upravljanih oznaka.
 
       Pravilo obuhvaća popis oznaka u kojem je svaka oznaka rječnik s ključevima "<ph name="NAME" />" i "<ph name="URL_LABEL" />" koji nose naziv i cilj oznake. Moguće je konfigurirati podmapu tako da se definira oznaka bez ključa "<ph name="URL_LABEL" />", ali s dodatnim ključem "<ph name="CHILDREN" />" koji i sam sadrži popis oznaka kako je gore definirano (od kojih neke opet mogu biti mape). <ph name="PRODUCT_NAME" /> dopunjuje nepotpune URL-ove kao da su poslani putem višenamjenskog okvira, npr. "<ph name="GOOGLE_COM" />" postaje "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_hu.xtb b/components/policy/resources/policy_templates_hu.xtb
index c29d270..ee7ff92 100644
--- a/components/policy/resources/policy_templates_hu.xtb
+++ b/components/policy/resources/policy_templates_hu.xtb
@@ -3720,13 +3720,6 @@
 <translation id="8050080920415773384">Natív nyomtatás</translation>
 <translation id="8053580360728293758">Felülbírálja az alapértelmezett színes nyomtatási módot. Ha nem áll rendelkezésre a színes nyomtatási mód, akkor a rendszer figyelmen kívül hagyja ezt a házirendet.</translation>
 <translation id="8059164285174960932">Az URL, ahol a távoli hozzáférésű kliensnek be kell szereznie a hitelesítési tokent</translation>
-<translation id="806280865577636339">Ha a házirend igaz értékre van állítva, a Kisegítő lehetőségek mindig megjelennek a tálca menüjében.
-
-          Ha a házirend hamis értékre van állítva, a Kisegítő lehetőségek sosem jelennek meg a tálca menüjében.
-
-          Ha beállítja ezt a házirendet, a felhasználók nem módosíthatják, vagy bírálhatják felül.
-
-          Ha a házirend nincs beállítva, a Kisegítő lehetőségek nem jelennek meg a tálca menüjében, ám a felhasználók a Beállítások oldal segítségével megjeleníthetik őket.</translation>
 <translation id="806523868782250975">A kezelt könyvjelzők listáját konfigurálja.
 
       A házirend könyvjelzők listájából áll, és mindegyik könyvjelző „<ph name="NAME" />” és „<ph name="URL_LABEL" />” kulcsot tartalmazó szótár; a kulcsok a könyvjelző nevét és URL-jét tárolják. Almappát úgy konfigurálhat, hogy „<ph name="URL_LABEL" />” kulcs nélküli könyvjelzőt határoz meg, amely azonban rendelkezik egy további „<ph name="CHILDREN" />” kulccsal. Ez a kulcs maga tartalmazza a könyvjelzők (amelyek közül néhány mappa is lehet) fentiekben meghatározottak szerinti listáját. A <ph name="PRODUCT_NAME" /> úgy egészíti ki a nem teljes URL-eket, mintha azok a cím- és keresősávon keresztül lettek volna elküldve. Így például a „<ph name="GOOGLE_COM" />” URL-t kiegészíti a következőre: „<ph name="HTTPS_GOOGLE_COM" />”.
diff --git a/components/policy/resources/policy_templates_id.xtb b/components/policy/resources/policy_templates_id.xtb
index b363414..b5952281e 100644
--- a/components/policy/resources/policy_templates_id.xtb
+++ b/components/policy/resources/policy_templates_id.xtb
@@ -3668,13 +3668,6 @@
 <translation id="8050080920415773384">Pencetakan Asli</translation>
 <translation id="8053580360728293758">Mengganti mode warna pencetakan default. Jika mode tersebut tidak tersedia, kebijakan ini akan diabaikan.</translation>
 <translation id="8059164285174960932">URL tempat klien akses jarak jauh seharusnya memperoleh token autentikasi mereka</translation>
-<translation id="806280865577636339">Jika kebijakan ini ditetapkan ke True, opsi Aksesibilitas akan selalu muncul di menu baki sistem.
-
-          Jika kebijakan ini ditetapkan ke False, opsi Aksesibilitas tidak akan muncul di menu baki sistem.
-
-          Jika kebijakan ini ditetapkan, pengguna tidak dapat mengubah atau menggantinya.
-
-          Jika kebijakan ini tidak ditetapkan, opsi Aksesibilitas tidak akan muncul di menu baki sistem, tetapi pengguna dapat menyebabkan munculnya opsi Aksesibilitas melalui halaman Setelan.</translation>
 <translation id="806523868782250975">Mengonfigurasi daftar bookmark terkelola.
 
       Kebijakan ini terdiri dari daftar bookmark, di mana setiap bookmark merupakan kamus yang berisi kunci "<ph name="NAME" />" dan "<ph name="URL_LABEL" />" yang menampung nama bookmark beserta targetnya. Subfolder dapat dikonfigurasi dengan menentukan bookmark tanpa kunci "<ph name="URL_LABEL" />" tetapi dengan kunci "<ph name="CHILDREN" />" tambahan, di mana kunci itu sendiri berisi daftar bookmark seperti dijelaskan di atas (beberapa mungkin berupa folder lagi). <ph name="PRODUCT_NAME" /> mengubah URL tidak lengkap seolah-olah URL tersebut dikirim melalui Omnibox, misalnya "<ph name="GOOGLE_COM" />" menjadi "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_it.xtb b/components/policy/resources/policy_templates_it.xtb
index f46072e40..282369c 100644
--- a/components/policy/resources/policy_templates_it.xtb
+++ b/components/policy/resources/policy_templates_it.xtb
@@ -3658,13 +3658,6 @@
 <translation id="8050080920415773384">Stampa nativa</translation>
 <translation id="8053580360728293758">Consente di eseguire l'override della modalità di stampa a colori predefinita. Se la modalità non è disponibile, questa norma viene ignorata.</translation>
 <translation id="8059164285174960932">URL su cui i client di accesso remoto dovrebbero ottenere il token di autenticazione</translation>
-<translation id="806280865577636339">Se questa norma è impostata su true, le opzioni di accessibilità vengono visualizzate sempre nel menu della barra delle applicazioni.
-
-          Se questa norma è impostata su false, le opzioni di accessibilità non vengono mai visualizzate nel menu della barra delle applicazioni.
-
-          Se imposti questa norma, gli utenti non potranno modificarla o sostituirla.
-
-          Se questa norma non viene impostata, le opzioni di accessibilità non verranno visualizzate nel menu della barra delle applicazioni, ma l'utente potrà attivarne la visualizzazione tramite la pagina Impostazioni.</translation>
 <translation id="806523868782250975">Configura un elenco di preferiti gestiti.
 
       La norma è costituita da un elenco di preferiti, in cui ogni preferito è un dizionario che contiene le chiavi "<ph name="NAME" />" e "<ph name="URL_LABEL" />" indicanti il nome e la destinazione del preferito. È possibile configurare una sottocartella definendo un preferito senza una chiave "<ph name="URL_LABEL" />", ma con una chiave aggiuntiva "<ph name="CHILDREN" />" contenente un elenco di preferiti come definito sopra (alcuni dei quali possono essere a loro volta cartelle). <ph name="PRODUCT_NAME" /> corregge gli URL incompleti come se fossero stati inviati tramite la omnibox, ad esempio "<ph name="GOOGLE_COM" />" diventa "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_iw.xtb b/components/policy/resources/policy_templates_iw.xtb
index 0ddfe82..5d03403 100644
--- a/components/policy/resources/policy_templates_iw.xtb
+++ b/components/policy/resources/policy_templates_iw.xtb
@@ -1695,7 +1695,7 @@
 
       כאשר המדיניות הזו מוגדרת, מערכת <ph name="PRODUCT_NAME" /> תנסה לאתר מדפסת שתואמת לכל המאפיינים שהוגדרו ולבחור אותה כמדפסת ברירת המחדל. נבחרת המדפסת הראשונה שנמצאת ותואמת למדיניות. במקרה של התאמה לא ייחודית, אפשר לבחור כל מדפסת תואמת בהתאם לסדר שבו המדפסות מאותרות.
 
-      אם המדיניות הזו לא מוגדרת או אם לא מאותרת מדפסת תואמת לפני תום הזמן הקצוב, מדפסת ה-PDF המובנית נבחרת כברירת מחדל. אם מדפסת ה-PDF לא זמינה, לא נבחרת מדפסת.
+      אם המדיניות הזו לא מוגדרת, או אם לא מאותרת מדפסת תואמת לפני תום הזמן הקצוב, מדפסת ה-PDF המובנית נבחרת כברירת מחדל. אם מדפסת ה-PDF לא זמינה, לא נבחרת מדפסת.
 
       מדפסות שמחוברות אל <ph name="CLOUD_PRINT_NAME" /> נחשבות כמדפסות מסוג <ph name="PRINTER_TYPE_CLOUD" />. שאר המדפסות מסווגות כמדפסות מסוג <ph name="PRINTER_TYPE_LOCAL" />.
       המשמעות של השמטת שדה היא שכל הערכים תואמים. למשל, אם לא מציינים קישוריות, ההדפסה המקדימה מאתרת מדפסות מכל הסוגים, מקומיות ובענן.
@@ -3701,13 +3701,6 @@
 <translation id="8050080920415773384">הדפסה מקומית</translation>
 <translation id="8053580360728293758">עוקפת את מצב ברירת המחדל של הדפסה בצבע. אם המצב לא זמין, המדיניות הזו לא מובאת בחשבון.</translation>
 <translation id="8059164285174960932">כתובת אתר שבו לקוחות גישה מרחוק אמורים לקבל את אסימון האימות שלהם</translation>
-<translation id="806280865577636339">‏אם המדיניות הזו מקבלת את הערך True, אפשרויות הנגישות תמיד מופיעות בתפריט מגש המערכת.
-
-          אם המדיניות הזו מקבלת את הערך False, אפשרויות הנגישות אף פעם לא מופיעות בתפריט מגש המערכת.
-
-          אם המדיניות הזו מוגדרת על ידך, המשתמשים לא יכולים לשנות או לבטל אותה.
-
-          אם המדיניות הזו לא מוגדרת, אפשרויות הנגישות לא מופיעות בתפריט מגש המערכת, אך המשתמשים יכולים לגרום להצגת אפשרויות הנגישות דרך דף ההגדרות.</translation>
 <translation id="806523868782250975">‏המדיניות הזו מגדירה רשימה של סימניות מנוהלות.
 
       המדיניות כוללת רשימת סימניות שבה כל סימניה היא ספריה המכילה את המפתחות "<ph name="NAME" />" ו-"<ph name="URL_LABEL" />". המפתחות האלו מכילים את השם והיעד של הסימניה. ניתן להגדיר תיקיית משנה על ידי הגדרה של סימניה ללא מפתח "<ph name="URL_LABEL" />" אך עם מפתח "<ph name="CHILDREN" />" נוסף, שבעצמו מכיל רשימת סימניות כפי שמתואר למעלה (גם חלק מהסימניות האלו יכולות להיות תיקיות). כתובת URL שאינן מלאות עוברות השלמה על ידי <ph name="PRODUCT_NAME" /> כאילו הן נשלחו דרך סרגל הכתובות. לדוגמה, הכתובת "<ph name="GOOGLE_COM" />" הופכת ל-"<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_ja.xtb b/components/policy/resources/policy_templates_ja.xtb
index 0dc012fe..f9ae3ff 100644
--- a/components/policy/resources/policy_templates_ja.xtb
+++ b/components/policy/resources/policy_templates_ja.xtb
@@ -1681,10 +1681,10 @@
 
       このポリシーが設定されている場合、<ph name="PRODUCT_NAME" /> は、指定されたすべての属性に一致するプリンタを探して選択し、デフォルト プリンタに設定しようと試みます。最初に見つかったポリシーに一致するプリンタが選択されます。適合するプリンタが複数ある場合は、最初に見つかったプリンタが選択されます。
 
-      このポリシーが設定されていない場合や、タイムアウトするまでに一致するプリンタが見つからない場合、デフォルト プリンタは組み込みの PDF プリンタに設定されますが、PDF プリンタが利用できない場合は、プリンタは設定されません。
+      このポリシーが設定されていない場合や、タイムアウトするまでに一致するプリンタが見つからない場合、デフォルト プリンタは組み込みの PDF プリンタに設定されます。PDF プリンタが利用できない場合は、プリンタは設定されません。
 
       「<ph name="CLOUD_PRINT_NAME" />」に接続されるプリンタは「<ph name="PRINTER_TYPE_CLOUD" />」と見なされ、それ以外のプリンタは「<ph name="PRINTER_TYPE_LOCAL" />」に分類されます。
-      項目の値の指定を省略すると、すべての値に一致することになります。たとえば、接続の種類を指定しない場合、印刷プレビューはローカルとクラウドを問わず、あらゆる種類のプリンタの検出を開始することになります。
+      項目の値の指定を省略すると、すべての値に一致することになります。たとえば、接続の種類を指定しない場合、印刷プレビューは、ローカルかクラウドかを問わず、あらゆる種類のプリンタの検出を開始することになります。
       正規表現のパターンは JavaScript RegExp 構文に従う必要があり、照合の際に大文字と小文字が区別されます。</translation>
 <translation id="4088589230932595924">シークレット モードを適用する</translation>
 <translation id="4088983553732356374">ウェブサイトにローカルデータの設定を許可するかどうかを制御します。ローカルデータの設定をすべてのウェブサイトに対して許可するか、すべてのウェブサイトに対して拒否することができます。
@@ -3678,13 +3678,6 @@
 <translation id="8050080920415773384">ネイティブ印刷</translation>
 <translation id="8053580360728293758">デフォルトのカラー印刷モードをオーバーライドします。このモードを使用できない場合、このポリシーは無視されます。</translation>
 <translation id="8059164285174960932">リモート アクセス クライアントが認証トークンを取得する URL</translation>
-<translation id="806280865577636339">このポリシーが true の場合、通知領域(システムトレイ)メニューにユーザー補助オプションが常に表示されます。
-
-          このポリシーが false の場合、通知領域(システムトレイ)メニューにユーザー補助オプションは表示されません。
-
-          このポリシーを設定した場合、ユーザーが変更したりオーバーライドしたりすることはできません。
-
-          このポリシーが未設定の場合、通知領域(システムトレイ)メニューにユーザー補助オプションは表示されませんが、ユーザーは [設定] ページからユーザー補助オプションの表示を指定できます。</translation>
 <translation id="806523868782250975">管理対象のブックマークのリストを設定します。
 
       このポリシーはブックマークのリストで構成されます。各ブックマークは、ブックマークの名前を定義する「<ph name="NAME" />」キーとブックマークの対象を定義する「<ph name="URL_LABEL" />」キーを含むディクショナリです。サブフォルダを指定する場合は、ブックマークの定義で「<ph name="URL_LABEL" />」キーの代わりに「<ph name="CHILDREN" />」キーを追加し、そのキーを使用して上記の方法でブックマークのリストを指定します(さらにフォルダを指定することもできます)。URL が不完全だった場合は、アドレスバーから不完全な URL が送信されたときと同様に、<ph name="PRODUCT_NAME" /> で URL が修正されます。たとえば、「<ph name="GOOGLE_COM" />」は「<ph name="HTTPS_GOOGLE_COM" />」になります。
diff --git a/components/policy/resources/policy_templates_kn.xtb b/components/policy/resources/policy_templates_kn.xtb
index 90d3c4a4..b62dd16 100644
--- a/components/policy/resources/policy_templates_kn.xtb
+++ b/components/policy/resources/policy_templates_kn.xtb
@@ -3623,13 +3623,6 @@
 <translation id="8050080920415773384">ಸ್ಥಳೀಯ ಮುದ್ರಣ</translation>
 <translation id="8053580360728293758">ಡೀಫಾಲ್ಟ್‌ ಮುದ್ರಣ ಬಣ್ಣದ ಮೋಡ್‌ ಅನ್ನು ಅತಿಕ್ರಮಿಸುತ್ತದೆ. ಮೋಡ್‌ ಲಭ್ಯವಿಲ್ಲದಿದ್ದರೆ, ಈ ಕಾರ್ಯನೀತಿಯನ್ನು ನಿರ್ಲಕ್ಷಿಸಲಾಗುತ್ತದೆ.</translation>
 <translation id="8059164285174960932">ರಿಮೋಟ್ ಪ್ರವೇಶ ಕ್ಲೈಂಟ್‌ಗಳು ತಮ್ಮ ಪ್ರಮಾಣೀಕರಣ ಟೋಕನ್ ಪಡೆದುಕೊಳ್ಳಬೇಕಾದ URL</translation>
-<translation id="806280865577636339">ಈ ನೀತಿಯನ್ನು ಸರಿ ಎಂದು ಹೊಂದಿಸಿದರೆ, ಪ್ರವೇಶಿಸುವಿಕೆ ಆಯ್ಕೆಗಳು ಯಾವಾಗಲೂ ಸಿಸ್ಟಮ್ ಟ್ರೇ ಮೆನುವಿನಲ್ಲಿ ಗೋಚರಿಸುತ್ತವೆ.
-
-          ಈ ನೀತಿಯನ್ನು ತಪ್ಪು ಎಂದು ಹೊಂದಿಸಿದರೆ, ಪ್ರವೇಶಿಸುವಿಕೆ ಆಯ್ಕೆಗಳು ಎಂದಿಗೂ ಸಿಸ್ಟಮ್ ಟ್ರೇ ಮೆನುವಿನಲ್ಲಿ ಗೋಚರಿಸುವುದಿಲ್ಲ.
-
-          ಈ ನೀತಿಗಳನ್ನು ನೀವು ಹೊಂದಿಸಿದರೆ, ಬಳಕೆದಾರರಿಗೆ ಅದನ್ನು ಬದಲಾಯಿಸಲು ಅಥವಾ ಅತಿಕ್ರಮಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ.
-
-          ಈ ನೀತಿಯನ್ನು ಹೊಂದಿಸದೇ ಹಾಗೆ ಬಿಟ್ಟರೆ, ಪ್ರವೇಶಿಸುವಿಕೆ ಆಯ್ಕೆಗಳು ಸಿಸ್ಟಮ್ ಟ್ರೇ ಮೆನುವಿನಲ್ಲಿ ಗೋಚರಿಸುವುದಿಲ್ಲ, ಆದರೆ ಸೆಟ್ಟಿಂಗ್ ಪುಟದ ಮೂಲಕ ಪ್ರವೇಶಿಸುವಿಕೆ ಆಯ್ಕೆಗಳು ಗೋಚರಿಸುವಂತೆ ಬಳಕೆದಾರರು ಮಾಡಬಹುದಾಗಿರುತ್ತದೆ.</translation>
 <translation id="806523868782250975">ನಿರ್ವಹಿಸಿದ ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳ ಪಟ್ಟಿಯನ್ನು ಕಾನ್ಫಿಗರ್ ಮಾಡುತ್ತದೆ.
 
       ನೀತಿಯು ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳ ಪಟ್ಟಿಯನ್ನು ಒಳಗೊಂಡಿರುತ್ತದೆ, ಪ್ರತಿ ಬುಕ್‌ಮಾರ್ಕ್ "<ph name="NAME" />" ಮತ್ತು "<ph name="URL_LABEL" />" ಎಂಬ ಕೀಗಳನ್ನು ಒಳಗೊಂಡಿರುವ ನಿಘಂಟು ಆಗಿದ್ದು, ಇದು ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳ ಹೆಸರು ಮತ್ತು ಗುರಿಯನ್ನು ಹೊಂದಿರುತ್ತದೆ. "<ph name="URL_LABEL" />" ಕೀ ಇಲ್ಲದೆಯೇ ಆದರೆ ಮೇಲೆ ನಿರೂಪಿಸಲಾಗಿರುವಂತೆ ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳ ಪಟ್ಟಿಯನ್ನು ಒಳಗೊಂಡಿರುವ ಹೆಚ್ಚುವರಿ "<ph name="CHILDREN" />" ಕೀ ಜೊತೆಗೆ ಬುಕ್‌ಮಾರ್ಕ್ ಅನ್ನು ನಿರೂಪಿಸುವ ಮೂಲಕ ಉಪಫೋಲ್ಡರ್ ಅನ್ನು ಕಾನ್ಫಿಗರ್ ಮಾಡಬಹುದು (ಅವುಗಳಲ್ಲಿ ಕೆಲವು ಮತ್ತೊಮ್ಮೆ ಫೋಲ್ಡರ್‌ಗಳು ಆಗಿರಬಹುದು). ಆಮ್ನಿಬಾಕ್ಸ್ ಮೂಲಕ ಅವುಗಳನ್ನು ಸಲ್ಲಿಸಲಾಗಿದೆಯೋ ಎಂಬಂತೆ ಅಪೂರ್ಣ URL ಗಳನ್ನು <ph name="PRODUCT_NAME" /> ತಿದ್ದುಪಡಿ ಮಾಡುತ್ತದೆ, ಉದಾಹರಣೆಗೆ "<ph name="GOOGLE_COM" />" ಎನ್ನುವುದು "<ph name="HTTPS_GOOGLE_COM" />" ಎಂಬುದಾಗಿ ಬದಲಾಗುತ್ತದೆ.
diff --git a/components/policy/resources/policy_templates_ko.xtb b/components/policy/resources/policy_templates_ko.xtb
index fe865d6..eb4effc3 100644
--- a/components/policy/resources/policy_templates_ko.xtb
+++ b/components/policy/resources/policy_templates_ko.xtb
@@ -3827,13 +3827,6 @@
 <translation id="8050080920415773384">기본 인쇄</translation>
 <translation id="8053580360728293758">기본 컬러 인쇄 모드를 재정의합니다. 이 모드를 사용할 수 없는 경우 이 정책은 무시됩니다.</translation>
 <translation id="8059164285174960932">원격 액세스 클라이언트가 인증 토큰을 확보해야 하는 URL</translation>
-<translation id="806280865577636339">이 정책을 true로 설정하면 작업 표시줄 메뉴에 접근성 옵션이 항상 표시됩니다.
-
-          이 정책을 false로 설정하면 작업 표시줄 메뉴에 접근성 옵션이 표시되지 않습니다.
-
-          이 정책을 설정하면 사용자는 변경하거나 덮어쓸 수 없습니다.
-
-          이 정책을 설정하지 않으면 작업 표시줄 메뉴에 접근성 옵션이 표시되지 않지만 사용자가 설정 페이지에서 접근성 옵션을 표시하도록 설정할 수 있습니다.</translation>
 <translation id="806523868782250975">관리 북마크 목록을 설정합니다.
 
       이 정책은 북마크의 목록으로 구성되어 있으며 각 북마크는 키 '<ph name="NAME" />' 및 '<ph name="URL_LABEL" />'을 포함하는 사전으로, 북마크의 이름과 대상을 포함합니다. 하위 폴더의 경우 '<ph name="URL_LABEL" />' 키는 없으나 위에 설명된 북마크(이 북마크 중 일부는 폴더일 수 있음) 목록을 자체적으로 포함하는 추가 '<ph name="CHILDREN" />' 키가 있는 북마크를 정의하는 방법으로 설정할 수 있습니다. <ph name="PRODUCT_NAME" />에서는 불완전한 URL이 있을 경우 검색주소창에 입력했을 때처럼 URL을 수정합니다. 예를 들어 '<ph name="GOOGLE_COM" />'은 '<ph name="HTTPS_GOOGLE_COM" />'으로 수정됩니다.
diff --git a/components/policy/resources/policy_templates_lt.xtb b/components/policy/resources/policy_templates_lt.xtb
index 5367ef9..d5eeb33d 100644
--- a/components/policy/resources/policy_templates_lt.xtb
+++ b/components/policy/resources/policy_templates_lt.xtb
@@ -3825,13 +3825,6 @@
 <translation id="8050080920415773384">Vietinis spausdinimas</translation>
 <translation id="8053580360728293758">Nepaisoma numatytojo spausdinimo spalvotu režimu. Jei režimas nepasiekiamas, šios politikos nepaisoma.</translation>
 <translation id="8059164285174960932">URL, kur nuotolinės prieigos klientai turėtų gauti autentifikavimo prieigos raktą</translation>
-<translation id="806280865577636339">Jei ši politika nustatyta kaip „Tiesa“, pasiekiamumo parinktys visada rodomos sistemos juostelės meniu.
-
-          Jei ši politika nustatyta kaip „Netiesa“, pasiekiamumo parinktys niekada nerodomos sistemos juostelės meniu.
-
-          Jei nustatysite šią politiką, naudotojai negalės jos pakeisti ar nepaisyti.
-
-          Jei ši politika nenustatyta, pasiekiamumo parinktys nebus rodomos sistemos juostelės meniu, bet „Nustatymų“ puslapyje naudotojas galės nustatyti, kad pasiekiamumo parinktys būtų rodomos.</translation>
 <translation id="806523868782250975">Konfigūruojamas tvarkomų žymių sąrašas.
 
       Politiką sudaro žymių sąrašas. Kiekviena žymė yra žodynas, kuriame yra raktai „<ph name="NAME" />“ ir „<ph name="URL_LABEL" />“, nurodantys žymės pavadinimą ir taikymą. Poaplankį galima konfigūruoti apibrėžiant žymę be rakto „<ph name="URL_LABEL" />“, bet naudojant papildomą raktą „<ph name="CHILDREN" />“, kuriame yra žymių sąrašas, kaip nurodyta anksčiau (kai kurie jų gali būti aplankai). „<ph name="PRODUCT_NAME" />“ pataiso neužbaigtus URL, lyg jie būtų pateikti per „Omnibox“, pvz., „<ph name="GOOGLE_COM" />“ bus „<ph name="HTTPS_GOOGLE_COM" />“.
diff --git a/components/policy/resources/policy_templates_lv.xtb b/components/policy/resources/policy_templates_lv.xtb
index cd28408..e4dff44c 100644
--- a/components/policy/resources/policy_templates_lv.xtb
+++ b/components/policy/resources/policy_templates_lv.xtb
@@ -3797,13 +3797,6 @@
 <translation id="8050080920415773384">Vietējā drukāšana</translation>
 <translation id="8053580360728293758">Tiek ignorēts noklusējuma krāsainās drukāšanas režīms. Ja režīms nav pieejams, šī politika tiek ignorēta.</translation>
 <translation id="8059164285174960932">URL, kurā attālās piekļuves klientiem ir jāiegūst autentifikācijas pilnvara</translation>
-<translation id="806280865577636339">Ja šai politikai ir iestatīta vērtība “True”, pieejamības opcijas vienmēr tiek rādītas sistēmas teknes izvēlnē.
-
-          Ja šai politikai ir iestatīta vērtība “False”, pieejamības opcijas nekad netiek rādītas sistēmas teknes izvēlnē.
-
-          Ja iestatīsiet šo politiku, lietotāji to nevarēs mainīt vai ignorēt.
-
-          Ja šī politika netiek iestatīta, pieejamības opcijas netiks rādītas sistēmas teknes izvēlnē, taču lietotājs varēs skatīt pieejamības opcijas, izmantojot lapu Iestatījumi.</translation>
 <translation id="806523868782250975">Konfigurē pārvaldīto grāmatzīmju sarakstu.
 
       Politika sastāv no grāmatzīmju saraksta, kurā katra grāmatzīme ir vārdnīca ar atslēgām “<ph name="NAME" />” un “<ph name="URL_LABEL" />”, kas ietver grāmatzīmes nosaukumu un tās mērķi. Lai konfigurētu apakšmapi, var definēt grāmatzīmi bez atslēgas “<ph name="URL_LABEL" />”, bet ar papildu atslēgu “<ph name="CHILDREN" />”, kas ietver grāmatzīmju sarakstu, kā definēts iepriekš (dažas no tām arī var būt mapes). <ph name="PRODUCT_NAME" /> papildina nepilnīgus vietrāžus URL, it kā tie būtu ierakstīti universālajā lodziņā, piemēram, “<ph name="GOOGLE_COM" />” tiek pārveidots par "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_ml.xtb b/components/policy/resources/policy_templates_ml.xtb
index 62894b8a..16c3e265 100644
--- a/components/policy/resources/policy_templates_ml.xtb
+++ b/components/policy/resources/policy_templates_ml.xtb
@@ -3751,13 +3751,6 @@
 <translation id="8050080920415773384">നേറ്റീവ് പ്രിന്റിംഗ്</translation>
 <translation id="8053580360728293758">ഡിഫോൾട്ട് പ്രിന്റിങ് വർണ്ണ മോഡ് അസാധുവാക്കുന്നു. മോഡ് ലഭ്യമല്ലെങ്കിൽ ഈ നയം അവഗണിക്കപ്പെടും.</translation>
 <translation id="8059164285174960932">വിദൂര ആക്‌സസ് ക്ലയന്റുകൾ അവരുടെ പരിശോധിച്ചുറപ്പിക്കൽ ടോക്കൺ നേടേണ്ട URL</translation>
-<translation id="806280865577636339">ഈ നയം ശരി എന്ന് സജ്ജമാക്കിയാൽ, ഉപയോഗസഹായി ഓപ്‌ഷനുകൾ എല്ലായ്‌പ്പോഴും സിസ്‌റ്റം ട്രേ മെനുവിൽ ദൃശ്യമാകും. 
-
-നയം തെറ്റ് എന്ന് സജ്ജമാക്കിയാൽ, ഉപയോഗസഹായി ഓപ്‌ഷനുകൾ ഒരിക്കലും സിസ്‌റ്റം ട്രേ മെനുവിൽ ദൃശ്യമാകില്ല. 
-
-നിങ്ങൾ ഈ നയം സജ്ജമാക്കിയാൽ, ഉപയോക്താക്കൾക്ക് ഇത് മാറ്റാനോ അസാധുവാക്കാനോ കഴിയില്ല. 
-
-ഈ നയം സജ്ജമാക്കാതിരിക്കുകയാണെങ്കിൽ, ഉപയോഗസഹായി ഓപ്‌ഷനുകൾ സിസ്‌റ്റം ട്രേ മെനുവിൽ ദൃശ്യമാകില്ലെങ്കിലും ഉപയോക്താവിന് ഉപയോഗസഹായി ഓപ്‌ഷനുകൾ ക്രമീകരണം പേജ് വഴി ദൃശ്യമാകും.</translation>
 <translation id="806523868782250975">മാനേജ് ചെയ്‌തിരിക്കുന്ന ബുക്ക്‌മാർക്കുകളുടെ ലിസ്‌റ്റ് കോൺഫിഗർ ചെയ്യുന്നു. 
 
 ബുക്ക്‌മാർക്കിന്റെ പേരും അതിന്റെ ടാർഗെറ്റും അടങ്ങിയിരിക്കുന്ന "<ph name="NAME" />", "<ph name="URL_LABEL" />" എന്നിവയുള്ള ഓരോ ബുക്ക്‌മാർക്കുമുള്ള ലിസ്‌റ്റാണ് നയത്തിൽ അടങ്ങിയിട്ടുള്ളത്. ഒരു "<ph name="URL_LABEL" />" കീ ഇല്ലാതെ ബുക്ക്‌മാർക്ക് നിർവ്വചിക്കുന്നതിലൂടെ ഉപഫോൾഡർ കോൺഫിഗർ ചെയ്യാനായേക്കാം, എന്നാൽ അത് മുകളിൽ നിർവ്വചിച്ച ബുക്ക്‌മാർക്കുകളുടെ ലിസ്‌റ്റ് അടങ്ങുന്ന ഒരു അധിക "<ph name="CHILDREN" />" കീ ഉപയോഗിച്ചായിരിക്കും (ഇവയിൽ ചിലത് വീണ്ടും ഫോൾഡറുകളായേക്കാം) ചെയ്‌തിരിക്കുന്നത്. ഒമ്‌നിബോക്‌സ് വഴി സമർപ്പിച്ചതിനാൽ പൂർണ്ണമല്ലാത്ത URL-കളെ <ph name="PRODUCT_NAME" /> ഭേദഗതി വരുത്തുന്നു, ഉദാഹരണത്തിന് "<ph name="GOOGLE_COM" />", "<ph name="HTTPS_GOOGLE_COM" />" എന്നായി മാറുന്നു. 
diff --git a/components/policy/resources/policy_templates_mr.xtb b/components/policy/resources/policy_templates_mr.xtb
index cae1ed5..bbed82cc 100644
--- a/components/policy/resources/policy_templates_mr.xtb
+++ b/components/policy/resources/policy_templates_mr.xtb
@@ -3735,12 +3735,6 @@
 <translation id="8050080920415773384">मुळ प्रिंट</translation>
 <translation id="8053580360728293758">डीफॉल्ट प्रिंटिंग रंगीत मोडला ओव्हरराइड करेल. मोड उपलब्ध नसल्यास या धोरणाकडे दुर्लक्ष केले जाते.</translation>
 <translation id="8059164285174960932">URL जिथे रिमोट अॅक्सेस क्लायंटनी त्यांचे अॉथेंटिकेशन टोकन मिळवावे</translation>
-<translation id="806280865577636339">हे धोरण सत्य वर सेट केले असल्यास, अॅक्सेसयोग्यता पर्याय नेहमी सिस्टम ट्रे मेनू मध्ये दिसतात.
-
-         हे धोरण असत्य वर सेट केले असल्यास, अॅक्सेसयोग्यता पर्याय सिस्टम ट्रे मेनू मध्ये कधीही दिसत नाहीत.
-          तुम्ही हे धोरण सेट केल्यास, वापरकर्ते ते बदलू किंवा ओव्हरराइड करू शकत नाहीत.
-
-          हे धोरण सेट न करता सोडल्यास, अॅक्सेसयोग्यता पर्याय सिस्टम ट्रे मध्ये दिसणार नाहीत परंतु वापरकर्त्यामुळे सेटिंग्ज पेजाद्वारे अॅक्सेसयोग्यता पर्याय दिसू शकतात.</translation>
 <translation id="806523868782250975">व्यवस्थापित बुकमार्कची सूची कॉन्फिगर करते.
 
      पॉलिसीमध्ये बुकमार्क सूचीचा समावेश आहे ज्यातील प्रत्येक बुकमार्क हा एक शब्दकोश आहे ज्यामध्ये "<ph name="NAME" />" आणि "<ph name="URL_LABEL" />" या की आहेत ज्या बुकमार्कचे नाव आणि त्यांचे लक्ष्य धारण करतात. "<ph name="URL_LABEL" />" की शिवाय बुकमार्क परिभाषित करून एक उप फोल्डर कॉन्फिगर केला जाऊ शकतो परंतु एका  अतिरिक्त "<ph name="CHILDREN" />" की सह, ज्यात वर परिभाषित केलेल्या बुकमार्कची सूची आहे (त्यातील काही पुन्हा फोल्डर असू शकतात). <ph name="PRODUCT_NAME" /> अपूर्ण URLना सुधारित करते जशा त्या विविधोपयोगी क्षेत्राद्वारे सबमिट केल्या गेल्या होत्या, उदाहरणार्थ "<ph name="GOOGLE_COM" />" हे "<ph name="HTTPS_GOOGLE_COM" />" बनते.
diff --git a/components/policy/resources/policy_templates_ms.xtb b/components/policy/resources/policy_templates_ms.xtb
index e5afbb4..319ab7a 100644
--- a/components/policy/resources/policy_templates_ms.xtb
+++ b/components/policy/resources/policy_templates_ms.xtb
@@ -3821,13 +3821,6 @@
 <translation id="8050080920415773384">Pencetakan Natif</translation>
 <translation id="8053580360728293758">Mengatasi mod warna pencetakan lalai. Jika mod tidak tersedia, dasar ini akan diabaikan.</translation>
 <translation id="8059164285174960932">URL di mana pelanggan akses jauh harus mendapatkan token pengesahan mereka</translation>
-<translation id="806280865577636339">Jika dasar ini ditetapkan kepada benar, pilihan Kebolehcapaian sentiasa dipaparkan dalam menu dulang sistem.
-
-          Jika dasar ini ditetapkan kepada palsu, pilihan Kebolehcapaian tidak akan dipaparkan dalam menu dulang sistem.
-
-          Jika anda menetapkan dasar ini, pengguna tidak boleh menukar atau membatalkannya.
-
-          Jika dasar ini dibiarkan tanpa ditetapkan, pilihan Kebolehcapaian tidak akan dipaparkan dalam menu dulang sistem, tetapi pengguna boleh menyebabkan pilihan Kebolehcapaian dipaparkan melalui halaman Tetapan.</translation>
 <translation id="806523868782250975">Mengkonfigurasi senarai penanda halaman terurus.
 
       Dasar ini mengandungi senarai penanda halaman, manakala setiap penanda halaman ialah kamus yang mengandungi kunci "<ph name="NAME" />" dan "<ph name="URL_LABEL" />" yang menyimpan nama penanda haaman tersebut dan sasarannya. Subfolder mungkin dikonfigurasi dengan mentakrifkan penanda halaman tanpa kunci "<ph name="URL_LABEL" />" tetapi dengan kunci "<ph name="CHILDREN" />" tambahan yang mengandungi senarai penanda halaman seperti yang ditakrifkan di atas (sesetengah daripadanya mungkin berubah menjadi folder semula). <ph name="PRODUCT_NAME" /> meminda URL tidak lengkap seolah-olah URL itu diserahkan melalui Kotak Omni, contohnya "<ph name="GOOGLE_COM" />" menjadi "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_nl.xtb b/components/policy/resources/policy_templates_nl.xtb
index ee28fee..445f097f 100644
--- a/components/policy/resources/policy_templates_nl.xtb
+++ b/components/policy/resources/policy_templates_nl.xtb
@@ -3789,13 +3789,6 @@
 <translation id="8050080920415773384">Systeemeigen printer</translation>
 <translation id="8053580360728293758">Hiermee overschrijf je de standaard kleurenmodus voor afdrukken. Als de modus niet beschikbaar is, wordt dit beleid genegeerd.</translation>
 <translation id="8059164285174960932">URL waarbij clients met externe toegang hun verificatietoken moeten ophalen</translation>
-<translation id="806280865577636339">Als dit beleid is ingesteld op 'true', worden toegankelijkheidsopties altijd weergegeven in het systeemvakmenu.
-
-          Als dit beleid is ingesteld op 'false', worden toegankelijkheidsopties nooit weergegeven in het systeemvakmenu.
-
-          Als je dit beleid instelt, kunnen gebruikers dit niet wijzigen of overschrijven.
-
-          Als dit beleid niet is ingesteld, worden toegankelijkheidsopties niet weergegeven in het systeemvakmenu, maar kan de gebruiker er via de pagina Instellingen voor zorgen dat de toegankelijkheidsopties worden weergegeven.</translation>
 <translation id="806523868782250975">Hiermee wordt een lijst met beheerde bladwijzers geconfigureerd.
 
       Het beleid bestaat uit een lijst met bladwijzers waarbij elke bladwijzer een woordenboek is met daarin de sleutels '<ph name="NAME" />' en '<ph name="URL_LABEL" />' voor de naam en het doel van de bladwijzers. Er kan een submap worden geconfigureerd door een bladwijzer te definiëren zonder een '<ph name="URL_LABEL" />'-sleutel maar met een extra '<ph name="CHILDREN" />'-sleutel die zelf een lijst met bladwijzers bevat, zoals hierboven gedefinieerd (sommige hiervan kunnen ook weer mappen zijn). <ph name="PRODUCT_NAME" /> vult onvolledige URL's aan alsof ze via de omnibox zijn opgegeven. Voorbeeld: <ph name="GOOGLE_COM" /> wordt <ph name="HTTPS_GOOGLE_COM" />.
diff --git a/components/policy/resources/policy_templates_no.xtb b/components/policy/resources/policy_templates_no.xtb
index b8333c5..b9872fd 100644
--- a/components/policy/resources/policy_templates_no.xtb
+++ b/components/policy/resources/policy_templates_no.xtb
@@ -3754,13 +3754,6 @@
 <translation id="8050080920415773384">Integrert utskrift</translation>
 <translation id="8053580360728293758">Overstyrer fargemodus som standard for utskrift. Hvis modusen ikke er tilgjengelig, ignoreres regelen.</translation>
 <translation id="8059164285174960932">Nettadressen der klienter for ekstern tilgang skal hente autentiseringstokenet sitt fra</translation>
-<translation id="806280865577636339">Hvis denne regelen er satt til «true» (sann), vises alltid tilgjengelighetsalternativer i systemfeltmenyen.
-
-          Hvis denne regelen er satt til «false» (usann), vises aldri tilgjengelighetsalternativer i systemfeltmenyen.
-
-          Hvis du angir denne regelen, kan ikke brukerne endre eller overstyre den.
-
-          Hvis denne regelen ikke er angitt, vises ikke tilgjengelighetsalternativer i systemfeltmenyen. Brukerne kan likevel få tilgjengelighetsalternativer til å vises via innstillingssiden.</translation>
 <translation id="806523868782250975">Angir en liste over administrerte bokmerker.
 
       Denne regelen består av en liste over bokmerker, der hvert bokmerke er en ordliste som inneholder nøklene «<ph name="NAME" />» og «<ph name="URL_LABEL" />». Disse nøklene inneholder de ulike bokmerkenes navn og mål. En undermappe kan konfigureres ved å angi et bokmerke uten «<ph name="URL_LABEL" />»-nøkkel, men med en ekstra «<ph name="CHILDREN" />»-nøkkel som selv inneholder en liste over bokmerker, som definert ovenfor (noen av disse kan igjen være mapper). <ph name="PRODUCT_NAME" /> retter ufullstendige nettadresser som om de er sendt inn via multifunksjonsfeltet. «<ph name="GOOGLE_COM" />» blir for eksempel «<ph name="HTTPS_GOOGLE_COM" />».
diff --git a/components/policy/resources/policy_templates_pl.xtb b/components/policy/resources/policy_templates_pl.xtb
index e358650..5dcf85a 100644
--- a/components/policy/resources/policy_templates_pl.xtb
+++ b/components/policy/resources/policy_templates_pl.xtb
@@ -3691,13 +3691,6 @@
 <translation id="8050080920415773384">Drukowanie natywne</translation>
 <translation id="8053580360728293758">Zastępuje domyślny tryb drukowania kolorów. Jeśli tryb jest niedostępny, zasada jest ignorowana.</translation>
 <translation id="8059164285174960932">Adres URL, dla którego klienty dostępu zdalnego powinny uzyskać token uwierzytelniania</translation>
-<translation id="806280865577636339">Jeśli ta zasada ma wartość Prawda, opcje ułatwień dostępu są zawsze wyświetlane w menu w obszarze powiadomień.
-
-          Jeśli ma wartość Fałsz, opcje ułatwień dostępu nie są wyświetlane w menu w obszarze powiadomień.
-
-          Jeśli skonfigurujesz tę zasadę, użytkownicy nie będą mogli jej zmienić ani zastąpić.
-
-          Jeśli pozostanie ona nieskonfigurowana, opcje ułatwień dostępu nie będą wyświetlane w menu w obszarze powiadomień, ale użytkownik będzie mógł włączyć ich wyświetlanie na stronie Ustawienia.</translation>
 <translation id="806523868782250975">Służy do konfigurowania listy zarządzanych zakładek.
 
       Zasada składa się z listy zakładek, a każda zakładka to słownik zawierający klucze „<ph name="NAME" />” i „<ph name="URL_LABEL" />”, które określają jej nazwę i adres docelowy. Można utworzyć podfolder, definiując zakładkę bez klucza „<ph name="URL_LABEL" />”, ale z dodatkowym kluczem „<ph name="CHILDREN" />”, który zawiera listę zakładek, taką jak opisano powyżej (niektóre z nich same mogą być folderami). <ph name="PRODUCT_NAME" /> uzupełnia niekompletne adresy URL tak samo jak po wpisaniu ich w omniboksie. Na przykład zamienia „<ph name="GOOGLE_COM" />” na „<ph name="HTTPS_GOOGLE_COM" />”.
diff --git a/components/policy/resources/policy_templates_pt-BR.xtb b/components/policy/resources/policy_templates_pt-BR.xtb
index ea95abc..991f4d52 100644
--- a/components/policy/resources/policy_templates_pt-BR.xtb
+++ b/components/policy/resources/policy_templates_pt-BR.xtb
@@ -3680,13 +3680,6 @@
 <translation id="8050080920415773384">Impressão nativa</translation>
 <translation id="8053580360728293758">Modifica o modo de cor de impressão padrão. Se o modo não estiver disponível, esta política será ignorada.</translation>
 <translation id="8059164285174960932">URL no qual clientes de acesso remoto devem receber seu token de autenticação</translation>
-<translation id="806280865577636339">Se esta política for definida como verdadeira, as opções de acessibilidade sempre aparecerão no menu da bandeja do sistema.
-
-          Se esta política for definida como falsa, as opções de acessibilidade nunca aparecerão no menu da bandeja do sistema.
-
-          Se você definir esta política, os usuários não poderão alterá-la ou substituí-la.
-
-          Se esta política não for definida, as opções de acessibilidade não aparecerão no menu da bandeja do sistema, mas o usuário poderá fazer com que elas apareçam usando a página "Configurações".</translation>
 <translation id="806523868782250975">Configura uma lista de favoritos gerenciados.
 
       Esta política consiste em uma lista de favoritos em que cada um deles é um dicionário contendo as chaves "<ph name="NAME" />" e "<ph name="URL_LABEL" />", que definem o nome e o alvo do favorito. Uma subpasta pode ser configurada ao definir um favorito sem uma chave "<ph name="URL_LABEL" />", mas com uma chave "<ph name="CHILDREN" />" adicional, que contém uma lista de favoritos conforme definido acima (alguns deles podem ser pastas). O <ph name="PRODUCT_NAME" /> altera URLs incompletos como se eles tivessem sido enviados pela omnibox. Por exemplo, "<ph name="GOOGLE_COM" />" se torna "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_pt-PT.xtb b/components/policy/resources/policy_templates_pt-PT.xtb
index fc8c1708..1e76f153 100644
--- a/components/policy/resources/policy_templates_pt-PT.xtb
+++ b/components/policy/resources/policy_templates_pt-PT.xtb
@@ -3718,13 +3718,6 @@
 <translation id="8050080920415773384">Impressão nativa</translation>
 <translation id="8053580360728293758">Substitui o modo de impressão a cores padrão. Se o modo não estiver disponível, esta política é ignorada.</translation>
 <translation id="8059164285174960932">URL onde os clientes de acesso remoto devem obter o respetivo símbolo de autenticação</translation>
-<translation id="806280865577636339">Se esta política for definida como Verdadeira, as opções de Acessibilidade aparecem sempre no menu do tabuleiro do sistema.
-
-          Se esta política for definida como Falsa, as opções de Acessibilidade nunca aparecem no menu do tabuleiro do sistema.
-
-          Se definir esta política, os utilizadores não a podem alterar nem substituir.
-
-          Se esta política não for definida, as opções de Acessibilidade não aparecem no menu do tabuleiro do sistema, mas o utilizador pode fazer com que as opções de Acessibilidade apareçam através da página Definições.</translation>
 <translation id="806523868782250975">Configura uma lista de marcadores geridos.
 
       A política consiste numa lista de marcadores, em que cada marcador é um dicionário que contém as chaves "<ph name="NAME" />" e "<ph name="URL_LABEL" />" que, por sua vez, contêm o nome e o destino do marcador. É possível configurar uma subpasta ao definir um marcador sem uma chave "<ph name="URL_LABEL" />", mas com uma chave "<ph name="CHILDREN" />" adicional que contém uma lista de marcadores conforme definido acima (alguns deles também podem ser pastas). O <ph name="PRODUCT_NAME" /> corrige os URLs incompletos como se tivessem sido enviados através da caixa geral, por exemplo, "<ph name="GOOGLE_COM" />" fica "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_ro.xtb b/components/policy/resources/policy_templates_ro.xtb
index e692b15..44b0be1 100644
--- a/components/policy/resources/policy_templates_ro.xtb
+++ b/components/policy/resources/policy_templates_ro.xtb
@@ -3717,13 +3717,6 @@
 <translation id="8050080920415773384">Printare nativă</translation>
 <translation id="8053580360728293758">Modifică modul de culoare prestabilit pentru printare. Dacă modul nu este disponibil, această politică este ignorată.</translation>
 <translation id="8059164285174960932">Adresa URL de unde clienții cu acces la distanță își pot obține indicativul de autentificare</translation>
-<translation id="806280865577636339">Dacă această politică este activată, opțiunile de accesibilitate apar întotdeauna în meniul barei de sistem.
-
-          Dacă politica este dezactivată, opțiunile de accesibilitate nu apar niciodată în meniul barei de sistem.
-
-          Dacă setezi această politică, utilizatorii nu pot să o modifice sau să o anuleze.
-
-          Dacă această politică nu este configurată, opțiunile de accesibilitate nu vor apărea în meniul barei de sistem, dar utilizatorul poate afișa opțiunile de accesibilitate prin intermediul paginii Setări.</translation>
 <translation id="806523868782250975">Configurează o listă de marcaje gestionate.
 
       Politica este o listă de marcaje, în care fiecare marcaj este un dicționar care conține cheile „<ph name="NAME" />” și „<ph name="URL_LABEL" />”, care includ numele și ținta marcajului. Un subdosar poate fi configurat definind un marcaj fără o cheie „<ph name="URL_LABEL" />”, dar cu o cheie „<ph name="CHILDREN" />” suplimentară care la rândul ei include o listă de marcaje așa cum au fost definite mai sus (este posibil ca unele dintre acestea să fie dosare din nou). <ph name="PRODUCT_NAME" /> modifică adresele URL incomplete ca și cum ar fi trimise prin caseta polivalentă, de exemplu, „<ph name="GOOGLE_COM" />” devine „<ph name="HTTPS_GOOGLE_COM" />”.
diff --git a/components/policy/resources/policy_templates_ru.xtb b/components/policy/resources/policy_templates_ru.xtb
index 9b844ef..f337ff90 100644
--- a/components/policy/resources/policy_templates_ru.xtb
+++ b/components/policy/resources/policy_templates_ru.xtb
@@ -3693,13 +3693,6 @@
 <translation id="8050080920415773384">Оригинальная печать</translation>
 <translation id="8053580360728293758">Переопределяет цветную печать по умолчанию. Если этот режим недоступен, правило не действует.</translation>
 <translation id="8059164285174960932">URL, по которому клиенты удаленного доступа должны получать токены аутентификации</translation>
-<translation id="806280865577636339">Если для правила задано значение True, меню специальных возможностей всегда будет видно в области уведомлений.
-
-          Если для правила указано значение False, это меню не появится.
-
-          Если правило настроено, пользователи не могут изменить или перезаписать его.
-
-          Если правило не настроено, меню специальных возможностей не будет показано в области уведомлений, но пользователи смогут добавить его в настройках.</translation>
 <translation id="806523868782250975">Позволяет настроить список управляемых закладок.
 
       Содержит список закладок, каждая из которых представляет собой запись, состоящую из названия "<ph name="NAME" />" и ссылки "<ph name="URL_LABEL" />". Также можно создать подпапку. Для этого вместо ключа "<ph name="URL_LABEL" />" используйте "<ph name="CHILDREN" />", чтобы задать список вложенных закладок или папок. <ph name="PRODUCT_NAME" /> дополняет неполные URL так же, как при их вводе в омнибокс, например адрес "<ph name="GOOGLE_COM" />" будет преобразован в "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_sk.xtb b/components/policy/resources/policy_templates_sk.xtb
index cdd52272..90027a0 100644
--- a/components/policy/resources/policy_templates_sk.xtb
+++ b/components/policy/resources/policy_templates_sk.xtb
@@ -3771,13 +3771,6 @@
 <translation id="8050080920415773384">Natívna tlač</translation>
 <translation id="8053580360728293758">Prepíše predvolený režim farebnej tlače. Ak daný režim nie je k dispozícii, toto pravidlo je ignorované.</translation>
 <translation id="8059164285174960932">Webová adresa, kde by klienti vzdialeného prístupu mali získať token na overenie totožnosti</translation>
-<translation id="806280865577636339">Ak je toto pravidlo nastavené na hodnotu true, v ponuke oblasti oznámení sa vždy zobrazia možnosti dostupnosti.
-
-          Ak je toto pravidlo nastavené na hodnotu false, možnosti dostupnosti sa v ponuke oblasti oznámení nikdy nezobrazia.
-
-          Ak toto pravidlo nastavíte, používatelia ho nebudú môcť zmeniť ani prepísať.
-
-          Ak toto pravidlo nie je nastavené, možnosti dostupnosti sa v ponuke oblasti oznámení nezobrazia, ale používatelia môžu ich zobrazenie aktivovať na stránke Nastavenia.</translation>
 <translation id="806523868782250975">Konfiguruje zoznam spravovaných záložiek.
 
       Toto pravidlo pozostáva zo zoznamu záložiek, zatiaľ čo každá záložka je slovník s kľúčmi „<ph name="NAME" />“ a „<ph name="URL_LABEL" />“, v ktorých je uložený názov a cieľ záložky. Podpriečinok je možné nakonfigurovať definovaním záložky bez kľúča „<ph name="URL_LABEL" />“, ale s ďalším kľúčom „<ph name="CHILDREN" />“, ktorý obsahuje zoznam záložiek podľa určenia vyššie (niektoré z nich môžu byť znova priečinky). <ph name="PRODUCT_NAME" /> opravuje neúplné webové adresy tak, ako by boli odoslané prostredníctvom omniboxu, napríklad <ph name="GOOGLE_COM" /> zmení na <ph name="HTTPS_GOOGLE_COM" />.
diff --git a/components/policy/resources/policy_templates_sl.xtb b/components/policy/resources/policy_templates_sl.xtb
index fb3dbfa..57fa8ee 100644
--- a/components/policy/resources/policy_templates_sl.xtb
+++ b/components/policy/resources/policy_templates_sl.xtb
@@ -3828,13 +3828,6 @@
 <translation id="8050080920415773384">Izvorno tiskanje</translation>
 <translation id="8053580360728293758">Preglasi privzeti barvni način tiskanja. Če način ni na voljo, je ta pravilnik prezrt.</translation>
 <translation id="8059164285174960932">URL, na katerem morajo odjemalci za oddaljeni dostop pridobiti žeton za preverjanje pristnosti</translation>
-<translation id="806280865577636339">Če je pravilnik nastavljen na »true«, so možnosti funkcij za ljudi s posebnimi potrebami vedno prikazane v meniju sistemske vrstice.
-
-          Če je pravilnik nastavljen na »false«, možnosti funkcij za ljudi s posebnimi potrebami niso prikazane v meniju sistemske vrstice.
-
-          Če nastavite ta pravilnik, ga uporabniki ne morejo spremeniti ali preglasiti.
-
-          Če ta pravilnik ni nastavljen, možnosti funkcij za ljudi s posebnimi potrebami niso prikazane v meniju sistemske vrstice, vendar lahko uporabniki nastavijo, da so možnosti funkcij za ljudi s posebnimi potrebami prikazane prek strani z nastavitvami.</translation>
 <translation id="806523868782250975">Konfigurira seznam upravljanih zaznamkov.
 
       Pravilnik sestavlja seznam zaznamkov, pri čemer je vsak zaznamek slovar s ključema »<ph name="NAME" />« in »<ph name="URL_LABEL" />«, v katerih sta shranjena ime in cilj zaznamka. Mogoče je konfigurirati podmapo tako, da definirate zaznamek brez ključa »<ph name="URL_LABEL" />«, vendar z dodatnim ključem »<ph name="CHILDREN" />«, ki vsebuje seznam zaznamkov, kot so opredeljeni zgoraj (nekateri so prav tako lahko mape). Brskalnik <ph name="PRODUCT_NAME" /> dopolni nepopolne URL-je, kot bi bili poslani prek iskalne vrstice. »<ph name="GOOGLE_COM" />« na primer postane »<ph name="HTTPS_GOOGLE_COM" />«.
diff --git a/components/policy/resources/policy_templates_sr.xtb b/components/policy/resources/policy_templates_sr.xtb
index 91a3fc9c..1f51f05 100644
--- a/components/policy/resources/policy_templates_sr.xtb
+++ b/components/policy/resources/policy_templates_sr.xtb
@@ -3776,13 +3776,6 @@
 <translation id="8050080920415773384">Матично штампање</translation>
 <translation id="8053580360728293758">Замењују подразумевани режим штампања у боји. Ако режим није доступан, ове смернице се занемарују.</translation>
 <translation id="8059164285174960932">URL на ком клијенти за даљински приступ треба да добију токене за потврду аутентичности</translation>
-<translation id="806280865577636339">Ако подесите ове смернице на Тачно, опције приступачности ће се увек приказивати у менију системске палете.
-
-          Ако подесите ове смернице на Нетачно, опције приступачности се никада неће приказивати у менију системске палете.
-
-          Ако подесите ове смернице, корисници не могу да их промене нити замене.
-
-          Ако не подесите ове смернице, опције приступачности се неће приказивати у менију системске палете, али корисник може да подеси да се опције приступачности приказују преко странице Подешавања.</translation>
 <translation id="806523868782250975">Конфигуришу листу обележивача којима се управља.
 
       Смернице се састоје од листе обележивача, а сваки обележивач је речник који садржи вредности „<ph name="NAME" />“ и „<ph name="URL_LABEL" />“ које представљају назив и одредиште обележивача. Можете да конфигуришете поддиректоријум тако што ћете дефинисати обележивач без вредности „<ph name="URL_LABEL" />“, али са додатном вредношћу „<ph name="CHILDREN" />“ која садржи листу обележивача као што је претходно дефинисано (а неки од тих обележивача могу и сами да буду директоријуми). <ph name="PRODUCT_NAME" /> допуњује непотпуне URL-ове да би изгледали као да су послати из омнибокса. На пример, „<ph name="GOOGLE_COM" />“ постаје „<ph name="HTTPS_GOOGLE_COM" />“. Ови обележивачи су стављени у директоријум који корисник не може да мења (али корисник може да одабере да га сакрије на траци са обележивачима). Подразумевани назив директоријума је „Обележивачи којима се управља“, али он може да се прилагоди ако на листу обележивача додате речник који садржи вредност „<ph name="TOPLEVEL_NAME" />“, где је вредност жељени назив директоријума.
diff --git a/components/policy/resources/policy_templates_sv.xtb b/components/policy/resources/policy_templates_sv.xtb
index ea69493..e1cfab9 100644
--- a/components/policy/resources/policy_templates_sv.xtb
+++ b/components/policy/resources/policy_templates_sv.xtb
@@ -3819,13 +3819,6 @@
 <translation id="8050080920415773384">Integrerad utskrift</translation>
 <translation id="8053580360728293758">Åsidosätter standardinställningen för färgutskrift. Om alternativet inte är tillgängligt ignoreras principen.</translation>
 <translation id="8059164285174960932">Webbadress där fjärråtkomstklienter erhåller sin autentiseringstoken</translation>
-<translation id="806280865577636339">Om principen är inställd på sant visas alltid tillgänglighetsalternativ i systemfältsmenyn.
-
-          Om principen är inställd på falskt visas aldrig tillgänglighetsalternativ i systemfältsmenyn.
-
-          Om du ställer in den här principen kan användare inte ändra eller åsidosätta den.
-
-          Om du inte ställer in principen visas inte tillgänglighetsalternativ i systemfältsmenyn, men användaren kan välja att tillgänglighetsalternativen ska visas via sidan för inställningar.</translation>
 <translation id="806523868782250975">Konfigurerar en lista över hanterade bokmärken.
 
       Principen är en lista över bokmärken och varje bokmärke är en ordlista som innehåller nycklarna <ph name="NAME" /> och <ph name="URL_LABEL" /> som har bokmärkets namn och mål som värden. Det går att konfigurera en undermapp genom att ange ett bokmärke utan nyckeln <ph name="URL_LABEL" /> men med en extra nyckel, <ph name="CHILDREN" />, som i sig innehåller en lista över bokmärken enligt definitionen ovan (varav några kan vara mappar, och så vidare). <ph name="PRODUCT_NAME" /> kompletterar ofullständiga webbadresser som om de skickats via adressfältet – <ph name="GOOGLE_COM" /> blir till exempel <ph name="HTTPS_GOOGLE_COM" />.
diff --git a/components/policy/resources/policy_templates_sw.xtb b/components/policy/resources/policy_templates_sw.xtb
index b9d6ee0e..e563d91e 100644
--- a/components/policy/resources/policy_templates_sw.xtb
+++ b/components/policy/resources/policy_templates_sw.xtb
@@ -3778,13 +3778,6 @@
 <translation id="8050080920415773384">Uchapishaji Asilia</translation>
 <translation id="8053580360728293758">Inabatilisha hali chaguomsingi ya rangi ya kuchapisha. Ikiwa hali haipatikani, sera hii haitazingatiwa.</translation>
 <translation id="8059164285174960932">URL ambapo seva teja za ufikiaji wa mbali zinapaswa kupata tokeni za uthibitishaji</translation>
-<translation id="806280865577636339">Sera hii ikiwekwa kuwa ndivyo, chaguo za Zana za walio na matatizo ya kuona na kusikia zitaonekana kila wakati katika menyu ya ubao wa aikoni.
-
-          Sera hii ikiwekwa kuwa sivyo, chaguo za Zana za walio na matatizo ya kuona na kusikia hazitawahi kuonekana katika menyu ya ubao wa aikoni.
-
-          Ukiweka sera hii, watumiaji hawawezi kuibadilisha au kuibatilisha.
-
-          Sera hii isipowekwa, chaguo za Zana za walio na matatizo ya kuona na kusikia hazitaonekana katika menyu ya ubao wa aikoni, lakini mtumiaji anaweza kufanya chaguo za Zana za walio na matatizo ya kuona na kusikia zionekane kupitia ukurasa wa Mipangilio.</translation>
 <translation id="806523868782250975">Huweka mipangilio ya orodha ya alamisho zinazodhibitiwa.
 
       Sera hii inajumuisha orodha ya alamisho na kila alamisho ni kamusi yenye funguo za "<ph name="NAME" />" na "<ph name="URL_LABEL" />" ambazo zina jina la alamisho na lengo lake. Folda ndogo inaweza kuwekewa mipangilio kwa kufafanua alamisho bila ufunguo wa "<ph name="URL_LABEL" />" lakini yenye ufunguo wa ziada wa "<ph name="CHILDREN" />" ambao una orodha ya alamisho kama ilivyofafanuliwa hapo juu (baadhi yazo zinaweza kuwa folda tena). <ph name="PRODUCT_NAME" /> inaweza kurekebisha URL ambazo hazijakamilika kama kwamba ziliwasilishwa kupitia Sanduku kuu, kwa mfano, "<ph name="GOOGLE_COM" />" huwa "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_ta.xtb b/components/policy/resources/policy_templates_ta.xtb
index 54074d6..bf28224 100644
--- a/components/policy/resources/policy_templates_ta.xtb
+++ b/components/policy/resources/policy_templates_ta.xtb
@@ -3666,13 +3666,6 @@
 <translation id="8050080920415773384">இயல்நிலை அச்சிடல்</translation>
 <translation id="8053580360728293758">இயல்புநிலையில் வண்ணப் பயன்முறையில் அச்சிடுவதை மேலெழுதும். பயன்முறை கிடைக்காவிட்டால், இந்தக் கொள்கை புறக்கணிக்கப்படும்.</translation>
 <translation id="8059164285174960932">தொலைநிலை அணுகல் கிளையன்ட்கள் தங்கள் அங்கீகரிப்பு டோக்கனைப் பெற வேண்டிய URL</translation>
-<translation id="806280865577636339">இந்தக் கொள்கை 'சரி' என அமைக்கப்பட்டால் சிஸ்டம் ட்ரேயில் அணுகல்தன்மை விருப்பங்கள் எப்போதும் தோன்றும்.
-
-          இந்தக் கொள்கை 'தவறு' என அமைக்கப்பட்டால் சிஸ்டம் ட்ரேயில் அணுகல்தன்மை விருப்பங்கள் தோன்றாது.
-
-          இந்தக் கொள்கையை அமைத்தால் பயனரால் இதை மாற்றவோ மீறிச் செயல்படவோ முடியாது.
-
-          இந்தக் கொள்கை அமைக்கப்படவில்லை எனில் சிஸ்டம் ட்ரே மெனுவில் அணுகல்தன்மை விருப்பங்கள் தோன்றாது, ஆனால் பயனர்கள் 'அமைப்புகள்' பக்கத்திற்குச் சென்று அணுகல்தன்மை விருப்பங்கள் தோன்றும்படி செய்யலாம்.</translation>
 <translation id="806523868782250975">நிர்வகிக்கப்படும் புக்மார்க்குகளின் பட்டியலை உள்ளமைக்கும்.
 
       இந்தக் கொள்கை புக்மார்க்குகளின் பட்டியலைக் கொண்டிருக்கும். இதில் உள்ள ஒவ்வொரு புக்மார்க்கும் "<ph name="NAME" />" (புக்மார்க் பெயர்), "<ph name="URL_LABEL" />" (இலக்கு) ஆகிய விசைகளைக் கொண்ட அகராதியாகும். "<ph name="URL_LABEL" />" விசையின்றி, ஆனால் கூடுதல் "<ph name="CHILDREN" />" விசையுடன் புக்மார்க்கை வரையறுப்பதன் மூலம் ஒரு துணைக் கோப்புறையை உள்ளமைக்கக்கூடும். மேலே விளக்கப்பட்டுள்ளது போல இந்த விசை புக்மார்க்குகளின் பட்டியலைக் கொண்டிருக்கும் (அவற்றில் சில, மீண்டும் கோப்புறைகளாக இருக்கக்கூடும்). முழுமையற்ற URLகளை ஆம்னிபாக்ஸ் மூலம் சமர்ப்பிக்கப்பட்டது போல <ph name="PRODUCT_NAME" /> மாற்றும். எடுத்துக்காட்டாக, "<ph name="GOOGLE_COM" />" என்பது "<ph name="HTTPS_GOOGLE_COM" />" என மாறும்.
diff --git a/components/policy/resources/policy_templates_te.xtb b/components/policy/resources/policy_templates_te.xtb
index 4ca945b..1a597325 100644
--- a/components/policy/resources/policy_templates_te.xtb
+++ b/components/policy/resources/policy_templates_te.xtb
@@ -3691,14 +3691,6 @@
 <translation id="8050080920415773384">స్థానిక ముద్రణ</translation>
 <translation id="8053580360728293758">డిఫాల్ట్ ముద్రణ రంగు మోడ్‌ను భర్తీ చేస్తుంది. మోడ్ అందుబాటులో లేనట్లయితే ఈ విధానం విస్మరించబడుతుంది.</translation>
 <translation id="8059164285174960932">రిమోట్ యాక్సెస్ క్లయింట్‌లు వారి ప్రామాణీకరణ టోకెన్‌ను పొందే URL</translation>
-<translation id="806280865577636339">ఈ విధానాన్ని 'ఒప్పు'గా సెట్ చేస్తే, యాక్సెస్ ఎంపికలు ఎప్పుడూ సిస్టమ్ ట్రే మెనులో కనిపిస్తాయి.
-
-ఈ విధానాన్ని 'తప్పు'గా సెట్ చేస్తే, యాక్సెస్ ఎంపికలు ఎప్పటికీ సిస్టమ్ ట్రే మెనులో కనిపించవు.
-
-          
-మీరు ఈ విధానాన్ని సెట్ చేస్తే, వినియోగదారులు దీనిని మార్చలేరు లేదా భర్తీ చేయలేరు.
-
-ఈ విధానాన్ని సెట్ చేయకుండా వదిలేస్తే, యాక్సెస్ ఎంపికలు సిస్టమ్ ట్రే మెనులో కనిపించవు, కానీ వినియోగదారు సెట్టింగ్‌ల పేజీ ద్వారా యాక్సెస్ ఎంపికలను కనిపించేలా చేయవచ్చు.</translation>
 <translation id="806523868782250975">నిర్వహించబడే బుక్‌మార్క్‌ల జాబితాను కాన్ఫిగర్ చేస్తుంది.
 
 ఈ విధానంలో బుక్‌మార్క్‌ల జాబితా ఉంటుంది. దీనిలోని ప్రతి బుక్‌మార్క్ కూడా "<ph name="NAME" />" మరియు "<ph name="URL_LABEL" />" కీలను కలిగి ఉండే నిఘంటువు. వీటిలో బుక్‌మార్క్ పేరు, దాని లక్ష్యం ఉంటాయి. "<ph name="URL_LABEL" />" కీ లేని, కానీ అదనపు "<ph name="CHILDREN" />" కీ కలిగి ఉండే బుక్‌మార్క్‌ను నిర్వచించడం ద్వారా ఉపఫోల్డర్‌ను కాన్ఫిగర్ చేయవచ్చు. అయితే ఈ ఉపఫోల్డర్ ఎగువ నిర్వచించినట్లు బుక్‌మార్క్‌లను కలిగి ఉంటుంది (వీటిలో కొన్ని మళ్లీ ఫోల్డర్‌లుగా ఉండవచ్చు). ఓమ్నిబాక్స్‌ ద్వారా అసంపూర్ణ URLలు సమర్పించబడితే <ph name="PRODUCT_NAME" /> వాటిని సవరిస్తుంది, ఉదాహరణకు "<ph name="GOOGLE_COM" />" అనేది "<ph name="HTTPS_GOOGLE_COM" />" లాగా మారుతుంది.
diff --git a/components/policy/resources/policy_templates_th.xtb b/components/policy/resources/policy_templates_th.xtb
index 0530cc37..6d4aa479 100644
--- a/components/policy/resources/policy_templates_th.xtb
+++ b/components/policy/resources/policy_templates_th.xtb
@@ -3693,13 +3693,6 @@
 <translation id="8050080920415773384">การพิมพ์ดั้งเดิม</translation>
 <translation id="8053580360728293758">ลบล้างโหมดสีการพิมพ์เริ่มต้น ระบบจะเพิกเฉยนโยบายนี้หากไม่มีโหมด</translation>
 <translation id="8059164285174960932">URL ที่ไคลเอ็นต์การเข้าถึงระยะไกลควรรับโทเค็นการตรวจสอบสิทธิ์</translation>
-<translation id="806280865577636339">หากตั้งค่านโยบายนี้เป็น "จริง" ตัวเลือกการช่วยเหลือพิเศษจะแสดงในเมนูถาดระบบเสมอ
-
-          หากตั้งค่านโยบายนี้เป็น "เท็จ" ตัวเลือกการช่วยเหลือพิเศษจะไม่แสดงในเมนูถาดระบบ
-
-          หากคุณตั้งค่านโยบายนี้ ผู้ใช้จะแก้ไขหรือลบล้างนโยบายไม่ได้
-
-          หากไม่ได้ตั้งค่านโยบายนี้ ตัวเลือกการช่วยเหลือพิเศษจะไม่แสดงในเมนูถาดระบบ แต่ผู้ใช้ทำให้ตัวเลือกการช่วยเหลือพิเศษปรากฏได้จากหน้าการตั้งค่า</translation>
 <translation id="806523868782250975">กำหนดค่ารายการบุ๊กมาร์กที่มีการจัดการ
 
       นโยบายประกอบด้วยรายการบุ๊กมาร์ก ส่วนบุ๊กมาร์กแต่ละรายการเป็นพจนานุกรมที่มีคีย์ "<ph name="NAME" />" และ "<ph name="URL_LABEL" />" ที่เก็บชื่อและเป้าหมายของบุ๊กมาร์กไว้ คุณอาจกำหนดค่าโฟลเดอร์ย่อยโดยกำหนดบุ๊กมาร์กที่ไม่มีคีย์ "<ph name="URL_LABEL" />" แต่มีคีย์ "<ph name="CHILDREN" />" เพิ่มเติมซึ่งมีรายการบุ๊กมาร์กอยู่ในตัวตามที่กำหนดไว้ข้างต้น (ซึ่งบางส่วนอาจเป็นโฟลเดอร์เหมือนกัน) <ph name="PRODUCT_NAME" /> จะแก้ไข URL ที่ไม่สมบูรณ์ให้เหมือนว่าส่งผ่านทางแถบอเนกประสงค์ เช่น "<ph name="GOOGLE_COM" />" จะกลายเป็น "<ph name="HTTPS_GOOGLE_COM" />"
diff --git a/components/policy/resources/policy_templates_tr.xtb b/components/policy/resources/policy_templates_tr.xtb
index 75ff9d1..20545489 100644
--- a/components/policy/resources/policy_templates_tr.xtb
+++ b/components/policy/resources/policy_templates_tr.xtb
@@ -3790,13 +3790,6 @@
 <translation id="8050080920415773384">Yerel Yazdırma</translation>
 <translation id="8053580360728293758">Varsayılan renkli yazdırma modunu geçersiz kılar. Mod mevcut değilse bu politika yoksayılır.</translation>
 <translation id="8059164285174960932">Uzaktan erişim istemcilerinin kimlik doğrulama jetonunu edinmesi gerektiği URL</translation>
-<translation id="806280865577636339">Bu politika true (doğru) değerine ayarlanırsa, Erişilebilirlik seçenekleri her zaman sistem tepsisi menüsünde görüntülenir.
-
-          Bu politika false (yanlış) değerine ayarlanırsa, Erişilebilirlik seçenekleri hiçbir zaman sistem tepsisi menüsünde görüntülenmez.
-
-          Bu politikayı ayarlarsanız kullanıcılar bunu değiştiremez veya geçersiz kılamazlar.
-
-          Bu politika ayarlanmadan bırakılırsa, Erişilebilirlik seçenekleri sistem tepsisi menüsünde görüntülenmez, ancak kullanıcı, Ayarlar sayfasını kullanarak Erişilebilirlik seçeneklerinin görüntülenmesini sağlayabilir.</translation>
 <translation id="806523868782250975">Yönetilen yer işaretleri listesini yapılandırır.
 
       Politika, yer işaretleri listesinden oluşur; bu yer işaretlerinden her biri "<ph name="NAME" />" ve "<ph name="URL_LABEL" />" anahtarlarını içeren ve yer işaretinin adı ile hedefini barındıran bir sözlüktür. Bir "<ph name="URL_LABEL" />" anahtarı içermeyen, ancak yukarıda tanımlandığı gibi yer işareti listesini (bunlardan bazıları yine klasör olabilir) barındıran ek bir "<ph name="CHILDREN" />" anahtarına sahip yer işareti tanımlanarak bir alt klasör yapılandırılabilir. <ph name="PRODUCT_NAME" />, eksik URL'leri, Çok Amaçlı Adres Çubuğu kullanılarak gönderilmiş gibi düzeltir; örneğin, "<ph name="GOOGLE_COM" />" URL'si "<ph name="HTTPS_GOOGLE_COM" />" olur.
diff --git a/components/policy/resources/policy_templates_uk.xtb b/components/policy/resources/policy_templates_uk.xtb
index d35483311..865c06a71 100644
--- a/components/policy/resources/policy_templates_uk.xtb
+++ b/components/policy/resources/policy_templates_uk.xtb
@@ -3726,13 +3726,6 @@
 <translation id="8050080920415773384">Власний друк</translation>
 <translation id="8053580360728293758">Замінює стандартні параметри кольорового друку. Якщо такий режим недоступний, це правило ігнорується.</translation>
 <translation id="8059164285174960932">URL-адреса, за якою клієнти віддаленого доступу мають отримати маркер автентифікації</translation>
-<translation id="806280865577636339">Якщо для цього правила вибрано значення true, параметри спеціальних можливостей завжди відображаються в меню панелі завдань.
-
-          Якщо для цього правила вибрано значення false, параметри спеціальних можливостей ніколи не відображаються в меню панелі завдань.
-
-          Якщо це правило налаштовано, користувачі не можуть змінювати або замінювати його.
-
-          Якщо це правило не налаштовано, параметри спеціальних можливостей не відображаються в меню панелі завдань, але користувачі можуть увімкнути їх показ на сторінці налаштувань.</translation>
 <translation id="806523868782250975">Налаштовує список керованих закладок.
 
       Це правило складається зі списку закладок, кожна з яких є каталогом із ключами "<ph name="NAME" />" і "<ph name="URL_LABEL" />", що містять її назву та ціль. Щоб налаштувати підпапку, потрібно вказати закладку без ключа "<ph name="URL_LABEL" />", але з додатковим ключем "<ph name="CHILDREN" />", у якому міститься вказаний вище список закладок (деякі з них можуть бути папками). <ph name="PRODUCT_NAME" /> змінює неповні URL-адреси так, ніби їх було надіслано з універсального вікна пошуку. Наприклад, <ph name="GOOGLE_COM" /> змінюється на <ph name="HTTPS_GOOGLE_COM" />.
diff --git a/components/policy/resources/policy_templates_vi.xtb b/components/policy/resources/policy_templates_vi.xtb
index b88600bb..66c3ca5 100644
--- a/components/policy/resources/policy_templates_vi.xtb
+++ b/components/policy/resources/policy_templates_vi.xtb
@@ -3805,13 +3805,6 @@
 <translation id="8050080920415773384">In bản gốc</translation>
 <translation id="8053580360728293758">Ghi đè chế độ in màu mặc định. Nếu không có chế độ này thì chính sách này sẽ bị bỏ qua.</translation>
 <translation id="8059164285174960932">URL nơi ứng dụng truy cập từ xa sẽ lấy mã thông báo xác thực</translation>
-<translation id="806280865577636339">Nếu bạn đặt chính sách này thành true, tùy chọn Hỗ trợ tiếp cận sẽ luôn xuất hiện trong menu khay hệ thống.
-
-          Nếu bạn đặt chính sách này thành false, tùy chọn Hỗ trợ tiếp cận sẽ không bao giờ xuất hiện trong menu khay hệ thống.
-
-          Nếu bạn đặt chính sách này, người dùng sẽ không thể thay đổi hay ghi đè chính sách.
-
-          Nếu bạn không đặt chính sách này, tùy chọn Hỗ trợ tiếp cận sẽ không xuất hiện trong menu khay hệ thống nhưng người dùng có thể đặt để tùy chọn Hỗ trợ tiếp cận hiển thị qua trang Cài đặt.</translation>
 <translation id="806523868782250975">Định cấu hình danh sách dấu trang được quản lý.
 
       Chính sách này bao gồm danh sách các dấu trang trong đó mỗi dấu trang là một từ điển chứa khóa "<ph name="NAME" />" và "<ph name="URL_LABEL" />". Các khóa này lưu giữ tên và URL đích của dấu trang. Có thể định cấu hình thư mục con bằng cách xác định một dấu trang không có khóa "<ph name="URL_LABEL" />" nhưng có khóa "<ph name="CHILDREN" />" bổ sung chứa danh sách dấu trang được xác định ở trên (một số dấu trang trong đó có thể lại là thư mục). <ph name="PRODUCT_NAME" /> sửa đổi các URL chưa hoàn chỉnh như thể các URL đó được gửi qua Thanh địa chỉ, ví dụ như "<ph name="GOOGLE_COM" />" trở thành "<ph name="HTTPS_GOOGLE_COM" />".
diff --git a/components/policy/resources/policy_templates_zh-CN.xtb b/components/policy/resources/policy_templates_zh-CN.xtb
index 3f09f13..6df4512c 100644
--- a/components/policy/resources/policy_templates_zh-CN.xtb
+++ b/components/policy/resources/policy_templates_zh-CN.xtb
@@ -3612,13 +3612,6 @@
 <translation id="8050080920415773384">本机打印</translation>
 <translation id="8053580360728293758">替换默认打印颜色模式。如果这种模式不适用,则忽略此政策。</translation>
 <translation id="8059164285174960932">远程访问客户端获取身份验证令牌的网址</translation>
-<translation id="806280865577636339">如果将此政策设置为 true,系统任务栏菜单中将始终显示无障碍选项。
-
-          如果将此政策设置为 false,系统任务栏菜单中绝对不会显示无障碍选项。
-
-          如果您设置了此政策,用户将无法对其进行更改或替换。
-
-          如果未设置此政策,系统任务栏菜单中将不会显示无障碍选项,但用户可以通过“设置”页面使无障碍选项显示在系统任务栏菜单中。</translation>
 <translation id="806523868782250975">配置受管理的书签列表。
 
       此政策包含一系列书签,其中每个书签都是一个包含“<ph name="NAME" />”和“<ph name="URL_LABEL" />”键(这些键分别指定书签的名称和目标网址)的字典条目。您也可配置一个子文件夹,方法是:定义一个不含“<ph name="URL_LABEL" />”键但另含一个“<ph name="CHILDREN" />”键的书签,并使后面这个键自含一个按上述方法指定的书签列表(其中某些书签仍可以是文件夹)。<ph name="PRODUCT_NAME" /> 会自动修正不完整的网址(就像这些网址是通过多功能框进行提交一样);例如,自动将“<ph name="GOOGLE_COM" />”改为“<ph name="HTTPS_GOOGLE_COM" />”。
diff --git a/components/policy/resources/policy_templates_zh-TW.xtb b/components/policy/resources/policy_templates_zh-TW.xtb
index 3255d7c..6a197bd 100644
--- a/components/policy/resources/policy_templates_zh-TW.xtb
+++ b/components/policy/resources/policy_templates_zh-TW.xtb
@@ -3633,13 +3633,6 @@
 <translation id="8050080920415773384">原生列印</translation>
 <translation id="8053580360728293758">覆寫預設列印色彩模式。如果設定的模式不適用,系統會忽略這項政策。</translation>
 <translation id="8059164285174960932">遠端存取用戶端取得驗證憑證的來源網址</translation>
-<translation id="806280865577636339">如果將這項政策設定為 True,系統將一律在系統匣選單中顯示無障礙選項。
-
-          如果將這項政策設定為 False,系統匣選單中就永遠不會顯示無障礙選項。
-
-          設定這項政策後,使用者無法變更或覆寫這項政策。
-
-          如果不設定這項政策,系統匣選單中不會顯示無障礙選項,但使用者可以在「設定」頁面中,將無障礙選項設為顯示。</translation>
 <translation id="806523868782250975">設定受管理書籤清單。
 
       這項政策內含一份書籤清單;每個書籤都是一個包含「<ph name="NAME" />」和「<ph name="URL_LABEL" />」索引鍵的條目,前者指定書籤的名稱,後者指定書籤的目標網址。如要設定子資料夾,請定義一個書籤,但不要指定「<ph name="URL_LABEL" />」索引鍵,並額外指定「<ph name="CHILDREN" />」索引鍵,當中應包含一份符合上述定義的書籤清單 (其中某些書籤也可能是資料夾)。<ph name="PRODUCT_NAME" /> 會將有缺漏的網址修補完整,就像你在網址列中輸入網址時一樣。舉例來說,系統會自動將 <ph name="GOOGLE_COM" /> 改成 <ph name="HTTPS_GOOGLE_COM" />。
diff --git a/components/policy/tools/generate_policy_source.py b/components/policy/tools/generate_policy_source.py
index 8411d46..280c49ca 100755
--- a/components/policy/tools/generate_policy_source.py
+++ b/components/policy/tools/generate_policy_source.py
@@ -84,9 +84,16 @@
         raise RuntimeError(
             'is_device_only is only allowed for Chrome OS: "%s"' % p)
       if platform not in [
-          'chrome_frame', 'chrome_os', 'android', 'webview_android',
-          'chrome.win', 'chrome.linux', 'chrome.mac', 'chrome.fuchsia',
-          'chrome.*'
+          'chrome_frame',
+          'chrome_os',
+          'android',
+          'webview_android',
+          'chrome.win',
+          'chrome.linux',
+          'chrome.mac',
+          'chrome.fuchsia',
+          'chrome.*',
+          'chrome.win7',
       ]:
         raise RuntimeError('Platform "%s" is not supported' % platform)
 
@@ -106,6 +113,8 @@
         platform_sub = platform[7:]
         if platform_sub == '*':
           self.platforms.extend(['win', 'mac', 'linux', 'fuchsia'])
+        elif platform_sub == 'win7':
+          self.platforms.append('win')
         else:
           self.platforms.append(platform_sub)
       else:
diff --git a/components/policy/tools/template_writers/policy_template_generator_unittest.py b/components/policy/tools/template_writers/policy_template_generator_unittest.py
index c827fe0..f80c3e6 100755
--- a/components/policy/tools/template_writers/policy_template_generator_unittest.py
+++ b/components/policy/tools/template_writers/policy_template_generator_unittest.py
@@ -432,6 +432,37 @@
 
     self.do_test(policy_data_mock, LocalMockWriter())
 
+  def testWin7OnlyPolicy(self):
+    # Test that Win7 only policy is marked as windows policy with speicial flag.
+    policy_data_mock = {
+        'policy_definitions': [{
+            'name':
+                'Policy1',
+            'type':
+                'string-enum-list',
+            'caption':
+                '',
+            'desc':
+                '',
+            'supported_on': ['chrome.win7:2-'],
+            'items': [{
+                'name': 'item1',
+                'value': 'one',
+                'caption': 'string1',
+                'desc': ''
+            },]
+        }]
+    }
+
+    class LocalMockWriter(mock_writer.MockWriter):
+
+      def WritePolicy(self, policy):
+        self.tester.assertEquals(policy['supported_on'][0]['platforms'],
+                                 ['win7'])
+
+    self.do_test(policy_data_mock, LocalMockWriter())
+
+
   def testPolicyFiltering(self):
     # Test that policies are filtered correctly based on their annotations.
     policy_data_mock = {
diff --git a/components/policy/tools/template_writers/writer_configuration.py b/components/policy/tools/template_writers/writer_configuration.py
index 92cdcec9..4ff09b4 100755
--- a/components/policy/tools/template_writers/writer_configuration.py
+++ b/components/policy/tools/template_writers/writer_configuration.py
@@ -131,6 +131,7 @@
   if 'version' in defines:
     config['version'] = defines['version']
   config['win_supported_os'] = 'SUPPORTED_WIN7'
+  config['win_supported_os_win7'] = 'SUPPORTED_WIN7_ONLY'
   if 'mac_bundle_id' in defines:
     config['mac_bundle_id'] = defines['mac_bundle_id']
   config['android_webview_restriction_prefix'] = 'com.android.browser:'
diff --git a/components/policy/tools/template_writers/writers/adm_writer.py b/components/policy/tools/template_writers/writers/adm_writer.py
index 2f7a90dd..6af5e1d 100755
--- a/components/policy/tools/template_writers/writers/adm_writer.py
+++ b/components/policy/tools/template_writers/writers/adm_writer.py
@@ -15,7 +15,7 @@
   See the constructor of TemplateWriter for description of
   arguments.
   '''
-  return AdmWriter(['win'], config)
+  return AdmWriter(['win', 'win7'], config)
 
 
 class IndentedStringBuilder:
@@ -93,9 +93,11 @@
       line = '%s="%s"' % (name, value)
       self.strings.AddLine(line)
 
-  def _WriteSupported(self, builder):
+  def _WriteSupported(self, builder, is_win7_only):
     builder.AddLine('#if version >= 4', 1)
-    builder.AddLine('SUPPORTED !!SUPPORTED_WIN7')
+    key = 'win_supported_os_win7' if is_win7_only else 'win_supported_os'
+    supported_on_text = self.config[key]
+    builder.AddLine('SUPPORTED !!' + supported_on_text)
     builder.AddLine('#endif', -1)
 
   def _WritePart(self, policy, key_name, builder):
@@ -158,7 +160,7 @@
     policy_name = self._Escape(policy['name'] + '_Policy')
     self._AddGuiString(policy_name, policy['caption'])
     builder.AddLine('POLICY !!%s' % policy_name, 1)
-    self._WriteSupported(builder)
+    self._WriteSupported(builder, self.IsPolicyOnWin7Only(policy))
     policy_explain_name = self._Escape(policy['name'] + '_Explain')
     policy_explain = self._GetPolicyExplanation(policy)
     self._AddGuiString(policy_explain_name, policy_explain)
@@ -249,6 +251,8 @@
       self.WriteComment(self.config['build'] + ' version: ' + \
           self._GetChromiumVersionString())
     self._AddGuiString(self.config['win_supported_os'],
+                       self.messages['win_supported_all']['text'])
+    self._AddGuiString(self.config['win_supported_os_win7'],
                        self.messages['win_supported_win7']['text'])
     categories = self.winconfig['mandatory_category_path'] + \
                  self.winconfig['recommended_category_path']
@@ -298,7 +302,7 @@
     # String buffer for building the recommended policies of the ADM file.
     self.recommended_policies = IndentedStringBuilder()
     # Shortcut to platform-specific ADMX/ADM specific configuration.
-    assert len(self.platforms) == 1
+    assert len(self.platforms) == 2
     self.winconfig = self.config['win_config'][self.platforms[0]]
 
   def GetTemplateText(self):
diff --git a/components/policy/tools/template_writers/writers/adm_writer_unittest.py b/components/policy/tools/template_writers/writers/adm_writer_unittest.py
index e381ded..4bcd50f 100755
--- a/components/policy/tools/template_writers/writers/adm_writer_unittest.py
+++ b/components/policy/tools/template_writers/writers/adm_writer_unittest.py
@@ -15,9 +15,13 @@
 
 MESSAGES = '''
   {
-    'win_supported_win7': {
+    'win_supported_all': {
       'text': 'Microsoft Windows 7 or later', 'desc': 'blah'
     },
+    'win_supported_win7': {
+      'text': 'Microsoft Windows 7', 'desc': 'blah'
+    },
+
     'doc_recommended': {
       'text': 'Recommended', 'desc': 'bleh'
     },
@@ -87,6 +91,7 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 chromium="Chromium"
 chromium_recommended="Chromium - Recommended"''')
     self.CompareOutputs(output, expected_output)
@@ -118,6 +123,7 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 chromium="Chromium"
 chromium_recommended="Chromium - Recommended"''')
     self.CompareOutputs(output, expected_output)
@@ -178,6 +184,7 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 Google:Cat_Google="Google"
 googlechrome="Google Chrome"
 googlechrome_recommended="Google Chrome - Recommended"
@@ -235,6 +242,7 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 Google:Cat_Google="Google"
 googlechrome="Google Chrome"
 googlechrome_recommended="Google Chrome - Recommended"
@@ -300,6 +308,7 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 chromium="Chromium"
 chromium_recommended="Chromium - Recommended"
 StringPolicy_Policy="Caption of policy."
@@ -310,7 +319,7 @@
     self.CompareOutputs(output, expected_output)
 
   def testIntPolicy(self):
-    # Tests a policy group with a single policy of type 'string'.
+    # Tests a policy group with a single policy of type 'int'.
     policy_json = '''
       {
         'policy_definitions': [
@@ -365,6 +374,74 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"
+IntPolicy_Policy="Caption of policy."
+IntPolicy_Explain="Description of policy.\\n\\n\
+Reference: https://www.chromium.org/administrators/policy-list-3#IntPolicy"
+IntPolicy_Part="Caption of policy."
+''')
+    self.CompareOutputs(output, expected_output)
+
+  def testIntPolicyWithWin7(self):
+    # Tests a policy group with a single policy of type 'int' that is supported
+    # on Windows 7 only.
+    policy_json = '''
+      {
+        'policy_definitions': [
+          {
+            'name': 'IntPolicy',
+            'type': 'int',
+            'caption': 'Caption of policy.',
+            'features': { 'can_be_recommended': True },
+            'desc': 'Description of policy.',
+            'supported_on': ['chrome.win7:8-'],
+          },
+        ],
+        'placeholders': [],
+        'messages': %s
+      }''' % MESSAGES
+    output = self.GetOutput(policy_json, {'_chromium': '1'}, 'adm')
+    expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+  CATEGORY !!chromium
+    KEYNAME "Software\\Policies\\Chromium"
+
+    POLICY !!IntPolicy_Policy
+      #if version >= 4
+        SUPPORTED !!SUPPORTED_WIN7_ONLY
+      #endif
+      EXPLAIN !!IntPolicy_Explain
+
+      PART !!IntPolicy_Part  NUMERIC
+        VALUENAME "IntPolicy"
+        MIN 0 MAX 2000000000
+      END PART
+    END POLICY
+
+  END CATEGORY
+
+  CATEGORY !!chromium_recommended
+    KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+    POLICY !!IntPolicy_Policy
+      #if version >= 4
+        SUPPORTED !!SUPPORTED_WIN7_ONLY
+      #endif
+      EXPLAIN !!IntPolicy_Explain
+
+      PART !!IntPolicy_Part  NUMERIC
+        VALUENAME "IntPolicy"
+        MIN 0 MAX 2000000000
+      END PART
+    END POLICY
+
+  END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 chromium="Chromium"
 chromium_recommended="Chromium - Recommended"
 IntPolicy_Policy="Caption of policy."
@@ -452,6 +529,7 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 Google:Cat_Google="Google"
 googlechrome="Google Chrome"
 googlechrome_recommended="Google Chrome - Recommended"
@@ -536,6 +614,7 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 Google:Cat_Google="Google"
 googlechrome="Google Chrome"
 googlechrome_recommended="Google Chrome - Recommended"
@@ -606,6 +685,7 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 chromium="Chromium"
 chromium_recommended="Chromium - Recommended"
 ListPolicy_Policy="Caption of list policy."
@@ -679,6 +759,7 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 chromium="Chromium"
 chromium_recommended="Chromium - Recommended"
 ListPolicy_Policy="Caption of list policy."
@@ -744,6 +825,7 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 chromium="Chromium"
 chromium_recommended="Chromium - Recommended"
 DictionaryPolicy_Policy="Caption of policy."
@@ -810,6 +892,7 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 chromium="Chromium"
 chromium_recommended="Chromium - Recommended"
 ExternalPolicy_Policy="Caption of policy."
@@ -858,6 +941,7 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 chromium="Chromium"
 chromium_recommended="Chromium - Recommended"
 ''')
@@ -908,6 +992,7 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 Google:Cat_Google="Google"
 googlechrome="Google Chrome"
 googlechrome_recommended="Google Chrome - Recommended"
@@ -1006,6 +1091,7 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 chromium="Chromium"
 chromium_recommended="Chromium - Recommended"
 Group1_Category="Caption of group."
@@ -1097,6 +1183,7 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 Google:Cat_Google="Google"
 googlechrome="Google Chrome"
 googlechrome_recommended="Google Chrome - Recommended"
@@ -1176,6 +1263,7 @@
 
 ''', '''[Strings]
 SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
 chromium="Chromium"
 chromium_recommended="Chromium - Recommended"
 DeprecatedPolicies_Category="Deprecated policies"
diff --git a/components/policy/tools/template_writers/writers/adml_writer.py b/components/policy/tools/template_writers/writers/adml_writer.py
index 4415ad1..6379a93 100755
--- a/components/policy/tools/template_writers/writers/adml_writer.py
+++ b/components/policy/tools/template_writers/writers/adml_writer.py
@@ -15,7 +15,7 @@
   GetWriter method because the TemplateFormatter uses this method to
   instantiate a Writer.
   '''
-  return ADMLWriter(['win'], config)
+  return ADMLWriter(['win', 'win7'], config)
 
 
 class ADMLWriter(xml_formatted_writer.XMLFormattedWriter,
@@ -161,6 +161,8 @@
     the ADMX file but not related to any specific Policy-Group or Policy.
     '''
     self._AddString(self.config['win_supported_os'],
+                    self.messages['win_supported_all']['text'])
+    self._AddString(self.config['win_supported_os_win7'],
                     self.messages['win_supported_win7']['text'])
     categories = self.winconfig['mandatory_category_path'] + \
                   self.winconfig['recommended_category_path']
@@ -252,7 +254,7 @@
     # Map of all strings seen.
     self.strings_seen = {}
     # Shortcut to platform-specific ADMX/ADM specific configuration.
-    assert len(self.platforms) == 1
+    assert len(self.platforms) <= 2
     self.winconfig = self.config['win_config'][self.platforms[0]]
 
   def GetTemplateText(self):
diff --git a/components/policy/tools/template_writers/writers/adml_writer_unittest.py b/components/policy/tools/template_writers/writers/adml_writer_unittest.py
index 0d94fb1..a9448ff 100755
--- a/components/policy/tools/template_writers/writers/adml_writer_unittest.py
+++ b/components/policy/tools/template_writers/writers/adml_writer_unittest.py
@@ -21,6 +21,7 @@
         'app_name': 'test',
         'build': 'test',
         'win_supported_os': 'SUPPORTED_TESTOS',
+        'win_supported_os_win7': 'SUPPORTED_TESTOS_2',
         'win_config': {
             'win': {
                 'mandatory_category_path': ['test_category'],
@@ -44,10 +45,14 @@
     }
     self.writer = self._GetWriter(config)
     self.writer.messages = {
-        'win_supported_win7': {
+        'win_supported_all': {
             'text': 'Supported on Test OS or higher',
             'desc': 'blah'
         },
+        'win_supported_win7': {
+            'text': 'Supported on Test OS',
+            'desc': 'blah'
+        },
         'doc_recommended': {
             'text': 'Recommended',
             'desc': 'bleh'
@@ -109,6 +114,7 @@
         ' revision="1.0" schemaVersion="1.0"><displayName/><description/>'
         '<resources><stringTable><string id="SUPPORTED_TESTOS">Supported on'
         ' Test OS or higher</string>'
+        '<string id="SUPPORTED_TESTOS_2">Supported on Test OS</string>'
         '<string id="' + self.GetCategory() + '">' + \
           self.GetCategoryString() + '</string>'
         '<string id="' + self.GetCategory() + '_recommended">' + \
@@ -128,6 +134,7 @@
         '<displayName/><description/><resources><stringTable>'
         '<string id="SUPPORTED_TESTOS">Supported on'
         ' Test OS or higher</string>'
+        '<string id="SUPPORTED_TESTOS_2">Supported on Test OS</string>'
         '<string id="' + self.GetCategory() + '">' + \
           self.GetCategoryString() + '</string>'
         '<string id="' + self.GetCategory() + '_recommended">' + \
@@ -162,7 +169,8 @@
     output = self.GetXMLOfChildren(self.writer._string_table_elem)
     expected_output = (
         '<string id="SUPPORTED_TESTOS">'
-        'Supported on Test OS or higher</string>\n'
+        'Supported on Test OS or higher</string>\n' + \
+        '<string id="SUPPORTED_TESTOS_2">Supported on Test OS</string>\n' + \
         '<string id="' + self.GetCategory() + '">' + \
           self.GetCategoryString() + '</string>\n'
         '<string id="' + self.GetCategory() + '_recommended">' + \
diff --git a/components/policy/tools/template_writers/writers/admx_writer.py b/components/policy/tools/template_writers/writers/admx_writer.py
index 64486b62ee..1236699 100755
--- a/components/policy/tools/template_writers/writers/admx_writer.py
+++ b/components/policy/tools/template_writers/writers/admx_writer.py
@@ -82,7 +82,7 @@
   GetWriter method because the TemplateFormatter uses this method to
   instantiate a Writer.
   '''
-  return ADMXWriter(['win'], config)
+  return ADMXWriter(['win', 'win7'], config)
 
 
 class ADMXWriter(xml_formatted_writer.XMLFormattedWriter,
@@ -100,7 +100,7 @@
 
   def Init(self):
     # Shortcut to platform-specific ADMX/ADM specific configuration.
-    assert len(self.platforms) == 1
+    assert len(self.platforms) <= 2
     self.winconfig = self.config['win_config'][self.platforms[0]]
 
   def _AdmlString(self, name):
@@ -220,7 +220,7 @@
         self._AddCategory(self._categories_elem, category_name,
                           self._AdmlString(category_name), parent_category_name)
 
-  def _AddSupportedOn(self, parent, supported_os):
+  def _AddSupportedOn(self, parent, supported_os_list):
     '''Generates the "supportedOn" ADMX element and adds it to the passed
     parent node. The "supportedOn" element contains information about supported
     Windows OS versions. The following code snippet contains an example of a
@@ -228,8 +228,8 @@
 
     <supportedOn>
       <definitions>
-        <definition name="SUPPORTED_WIN7"
-                    displayName="$(string.SUPPORTED_WIN7)"/>
+        <definition name="$(supported_os)"
+                    displayName="$(string.$(supported_os))"/>
         </definitions>
         ...
     </supportedOn>
@@ -240,11 +240,12 @@
     '''
     supported_on_elem = self.AddElement(parent, 'supportedOn')
     definitions_elem = self.AddElement(supported_on_elem, 'definitions')
-    attributes = {
-        'name': supported_os,
-        'displayName': self._AdmlString(supported_os)
-    }
-    self.AddElement(definitions_elem, 'definition', attributes)
+    for supported_os in supported_os_list:
+      attributes = {
+          'name': supported_os,
+          'displayName': self._AdmlString(supported_os)
+      }
+      self.AddElement(definitions_elem, 'definition', attributes)
 
   def _AddStringPolicy(self, parent, name, id=None):
     '''Generates ADMX elements for a String-Policy and adds them to the
@@ -379,13 +380,16 @@
         'presentation': self._AdmlPresentation(policy_name),
         'key': key,
     }
+    is_win7_only = self.IsPolicyOnWin7Only(policy)
+    supported_key = ('win_supported_os_win7'
+                     if is_win7_only else 'win_supported_os')
+    supported_on_text = self.config[supported_key]
 
     # Store the current "policy" AMDX element in self for later use by the
     # WritePolicy method.
     policy_elem = self.AddElement(policies_elem, 'policy', attributes)
     self.AddElement(policy_elem, 'parentCategory', {'ref': parent})
-    self.AddElement(policy_elem, 'supportedOn',
-                    {'ref': self.config['win_supported_os']})
+    self.AddElement(policy_elem, 'supportedOn', {'ref': supported_on_text})
 
     element_type = self._GetAdmxElementType(policy)
     if element_type == AdmxElementType.MAIN:
@@ -472,8 +476,9 @@
                               self.winconfig['namespace'])
     self.AddElement(policy_definitions_elem, 'resources',
                     {'minRequiredRevision': '1.0'})
-    self._AddSupportedOn(policy_definitions_elem,
-                         self.config['win_supported_os'])
+    self._AddSupportedOn(
+        policy_definitions_elem,
+        [self.config['win_supported_os'], self.config['win_supported_os_win7']])
     self._categories_elem = self.AddElement(policy_definitions_elem,
                                             'categories')
     self._AddCategories(self.winconfig['mandatory_category_path'])
diff --git a/components/policy/tools/template_writers/writers/admx_writer_unittest.py b/components/policy/tools/template_writers/writers/admx_writer_unittest.py
index 9e5cf94..cecbb013 100755
--- a/components/policy/tools/template_writers/writers/admx_writer_unittest.py
+++ b/components/policy/tools/template_writers/writers/admx_writer_unittest.py
@@ -27,6 +27,7 @@
     # Writer
     config = {
         'win_supported_os': 'SUPPORTED_TESTOS',
+        'win_supported_os_win7': 'SUPPORTED_TESTOS_2',
         'win_config': {
             'win': {
                 'reg_mandatory_key_name':
@@ -108,6 +109,8 @@
         '    <definitions>\n'
         '      <definition displayName="'
         '$(string.SUPPORTED_TESTOS)" name="SUPPORTED_TESTOS"/>\n'
+        '      <definition displayName="'
+        '$(string.SUPPORTED_TESTOS_2)" name="SUPPORTED_TESTOS_2"/>\n'
         '    </definitions>\n'
         '  </supportedOn>\n'
         '  <categories>\n'
@@ -140,6 +143,8 @@
         '    <definitions>\n'
         '      <definition displayName="'
         '$(string.SUPPORTED_TESTOS)" name="SUPPORTED_TESTOS"/>\n'
+        '      <definition displayName="'
+        '$(string.SUPPORTED_TESTOS_2)" name="SUPPORTED_TESTOS_2"/>\n'
         '    </definitions>\n'
         '  </supportedOn>\n'
         '  <categories>\n'
@@ -379,6 +384,35 @@
         '</policy>')
     self.AssertXMLEquals(output, expected_output)
 
+  def testIntPolicyWithWin7Only(self):
+    int_policy = {
+        'name': 'SampleIntPolicy',
+        'type': 'int',
+        'supported_on': [{
+            'platforms': ['win7'],
+        }]
+    }
+    self._initWriterForPolicy(self.writer, int_policy)
+
+    self.writer.WritePolicy(int_policy)
+    output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+    expected_output = (
+        '<policy class="' + self.writer.GetClass(int_policy) + '"'
+        ' displayName="$(string.SampleIntPolicy)"'
+        ' explainText="$(string.SampleIntPolicy_Explain)"'
+        ' key="Software\\Policies\\' + self._GetKey() + '"'
+        ' name="SampleIntPolicy"'
+        ' presentation="$(presentation.SampleIntPolicy)">\n'
+        '  <parentCategory ref="PolicyGroup"/>\n'
+        '  <supportedOn ref="SUPPORTED_TESTOS_2"/>\n'
+        '  <elements>\n'
+        '    <decimal id="SampleIntPolicy" maxValue="2000000000" minValue="0" '
+        'valueName="SampleIntPolicy"/>\n'
+        '  </elements>\n'
+        '</policy>')
+    self.AssertXMLEquals(output, expected_output)
+
+
   def testIntEnumPolicy(self):
     enum_policy = {
         'name':
diff --git a/components/policy/tools/template_writers/writers/doc_writer.py b/components/policy/tools/template_writers/writers/doc_writer.py
index 2d09058f..2f283d5 100755
--- a/components/policy/tools/template_writers/writers/doc_writer.py
+++ b/components/policy/tools/template_writers/writers/doc_writer.py
@@ -265,7 +265,7 @@
       policy: The data structure of a policy.
     '''
     examples = self._AddStyledElement(parent, 'dl', ['dd dl'])
-    if self.IsPolicySupportedOnPlatform(policy, 'win'):
+    if self.IsPolicySupportedOnWindows(policy):
       self._AddListExampleWindowsChromeOS(examples, policy, True)
     if self.IsPolicySupportedOnPlatform(
         policy, 'chrome_os', management='active_directory'):
@@ -388,7 +388,7 @@
       policy: The data structure of a policy.
     '''
     examples = self._AddStyledElement(parent, 'dl', ['dd dl'])
-    if self.IsPolicySupportedOnPlatform(policy, 'win'):
+    if self.IsPolicySupportedOnWindows(policy):
       self._AddDictionaryExampleWindowsChromeOS(examples, policy, True)
     if self.IsPolicySupportedOnPlatform(
         policy, 'chrome_os', management='active_directory'):
@@ -418,7 +418,7 @@
     policy_type = policy['type']
     if policy_type == 'main':
       pieces = []
-      if self.IsPolicySupportedOnPlatform(policy, 'win') or \
+      if self.IsPolicySupportedOnWindows(policy) or \
          self.IsPolicySupportedOnPlatform(policy, 'chrome_os',
                                           management='active_directory'):
         value = '0x00000001' if example_value else '0x00000000'
@@ -437,7 +437,7 @@
       self.AddText(parent, '"%s"' % example_value)
     elif policy_type in ('int', 'int-enum'):
       pieces = []
-      if self.IsPolicySupportedOnPlatform(policy, 'win') or \
+      if self.IsPolicySupportedOnWindows(policy) or \
          self.IsPolicySupportedOnPlatform(policy, 'chrome_os',
                                           management='active_directory'):
         pieces.append('0x%08x (Windows)' % example_value)
@@ -548,7 +548,7 @@
           'Android:%s' % self._RESTRICTION_TYPE_MAP[policy['type']])
       if policy['type'] in ('dict', 'external', 'list'):
         is_complex_policy = True
-    if ((self.IsPolicySupportedOnPlatform(policy, 'win') or
+    if ((self.IsPolicySupportedOnWindows(policy) or
          self.IsPolicySupportedOnPlatform(
              policy, 'chrome_os', management='active_directory')) and
         self._REG_TYPE_MAP.get(policy['type'], None)):
@@ -561,7 +561,7 @@
         data_type.append(
             '(%s)' % self.GetLocalizedMessage('complex_policies_on_windows'))
     self._AddPolicyAttribute(dl, 'data_type', ' '.join(data_type))
-    if self.IsPolicySupportedOnPlatform(policy, 'win'):
+    if self.IsPolicySupportedOnWindows(policy):
       key_name = self._GetRegistryKeyName(policy, True)
       self._AddPolicyAttribute(dl, 'win_reg_loc',
                                key_name + '\\' + policy['name'], ['.monospace'])
@@ -604,7 +604,7 @@
     if 'url_schema' in policy:
       dd = self._AddPolicyAttribute(dl, 'url_schema')
       self._AddTextWithLinks(dd, policy['url_schema'])
-    if (self.IsPolicySupportedOnPlatform(policy, 'win') or
+    if (self.IsPolicySupportedOnWindows(policy) or
         self.IsPolicySupportedOnPlatform(policy, 'linux') or
         self.IsPolicySupportedOnPlatform(policy, 'android') or
         self.IsPolicySupportedOnPlatform(policy, 'mac') or
@@ -755,6 +755,7 @@
         'linux': 'Linux',
         'chrome_os': self.config['os_name'],
         'android': 'Android',
+        'win7': 'Windows 7',
     }
     # Human-readable names of supported products.
     self._PRODUCT_MAP = {
diff --git a/components/policy/tools/template_writers/writers/doc_writer_unittest.py b/components/policy/tools/template_writers/writers/doc_writer_unittest.py
index 0af1a1c..872dbed 100755
--- a/components/policy/tools/template_writers/writers/doc_writer_unittest.py
+++ b/components/policy/tools/template_writers/writers/doc_writer_unittest.py
@@ -1086,6 +1086,56 @@
         '</div>'
         '</root>')
 
+  def testAddPolicySectionForWindows7Only(self):
+    policy = {
+        'name':
+            'PolicyName',
+        'caption':
+            'PolicyCaption',
+        'desc':
+            'PolicyDesc',
+        'type':
+            'int',
+        'supported_on': [{
+            'product': 'chrome',
+            'platforms': ['win7'],
+            'since_version': '33',
+            'until_version': '',
+        }],
+        'features': {
+            'dynamic_refresh': False
+        },
+        'example_value':
+            123
+    }
+    self.writer._AddPolicySection(self.doc_root, policy)
+    self.assertEquals(
+        self.doc_root.toxml(), '<root>'
+        '<div style="margin-left: 0px">'
+        '<h3><a name="PolicyName"/>PolicyName</h3>'
+        '<span>PolicyCaption</span>'
+        '<dl>'
+        '<dt style="style_dt;">_test_data_type</dt>'
+        '<dd>Integer [Windows:REG_DWORD]</dd>'
+        '<dt style="style_dt;">_test_win_reg_loc</dt>'
+        '<dd style="style_.monospace;">MockKey\\PolicyName</dd>'
+        '<dt style="style_dt;">_test_supported_on</dt>'
+        '<dd>'
+        '<ul style="style_ul;">'
+        '<li>Chrome (Windows 7) ..33..</li>'
+        '</ul>'
+        '</dd>'
+        '<dt style="style_dt;">_test_supported_features</dt>'
+        '<dd>_test_feature_dynamic_refresh: _test_not_supported</dd>'
+        '<dt style="style_dt;">_test_description</dt>'
+        '<dd><p>PolicyDesc</p></dd>'
+        '<dt style="style_dt;">_test_example_value</dt>'
+        '<dd>0x0000007b (Windows)</dd>'
+        '</dl>'
+        '<a href="#top">_test_back_to_top</a>'
+        '</div>'
+        '</root>')
+
   def testAddPolicySectionForMacOnly(self):
     policy = {
         'name':
diff --git a/components/policy/tools/template_writers/writers/gpo_editor_writer.py b/components/policy/tools/template_writers/writers/gpo_editor_writer.py
index 8aa50099..bbb3e28 100755
--- a/components/policy/tools/template_writers/writers/gpo_editor_writer.py
+++ b/components/policy/tools/template_writers/writers/gpo_editor_writer.py
@@ -23,13 +23,21 @@
     # Include deprecated policies in the 'DeprecatedPolicies' group, even if
     # they aren't supported anymore.
     #
-    # TODO(crbug.com/463990): Eventually exclude some policies, e.g. if they were
-    # deprecated a long time ago.
+    # TODO(crbug.com/463990): Eventually exclude some policies, e.g. if they
+    # were deprecated a long time ago.
     if policy.get('deprecated', False):
       return True
 
     return super(GpoEditorWriter, self).IsVersionSupported(policy, supported_on)
 
+  def IsPolicyOnWin7Only(self, policy):
+    ''' Returns true if the policy is supported on win7 only.'''
+    for suppported_on in policy.get('supported_on', []):
+      if 'win7' in suppported_on.get('platforms', []):
+        return True
+    return False
+
+
   def _FindDeprecatedPolicies(self, policy_list):
     deprecated_policies = []
     for policy in policy_list:
diff --git a/components/policy/tools/template_writers/writers/reg_writer.py b/components/policy/tools/template_writers/writers/reg_writer.py
index ed73ce5..2e27978 100755
--- a/components/policy/tools/template_writers/writers/reg_writer.py
+++ b/components/policy/tools/template_writers/writers/reg_writer.py
@@ -13,7 +13,7 @@
   See the constructor of TemplateWriter for description of
   arguments.
   '''
-  return RegWriter(['win'], config)
+  return RegWriter(['win', 'win7'], config)
 
 
 class RegWriter(template_writer.TemplateWriter):
diff --git a/components/policy/tools/template_writers/writers/template_writer.py b/components/policy/tools/template_writers/writers/template_writer.py
index 4c5a0f7..d883355 100755
--- a/components/policy/tools/template_writers/writers/template_writer.py
+++ b/components/policy/tools/template_writers/writers/template_writer.py
@@ -115,6 +115,17 @@
         return True
     return False
 
+  def IsPolicySupportedOnWindows(self, policy, product=None):
+    ''' Checks if |policy| is supported on any Windows platform.
+
+    Args:
+      policy: The dictionary of the policy.
+      product: Optional product to check; one of
+        'chrome', 'chrome_frame', 'chrome_os', 'webview'
+    '''
+    return (self.IsPolicySupportedOnPlatform(policy, 'win', product) or
+            self.IsPolicySupportedOnPlatform(policy, 'win7', product))
+
   def IsCrOSManagementSupported(self, policy, management):
     '''Checks whether |policy| supports the Chrome OS |management| type.
 
diff --git a/components/policy_strings.grdp b/components/policy_strings.grdp
index 3db0004..706b3a9 100644
--- a/components/policy_strings.grdp
+++ b/components/policy_strings.grdp
@@ -483,4 +483,13 @@
     List entry "<ph name="LANGUAGE_ID">$1<ex>en-GB</ex></ph>": Entry ignored because it is also included in the SpellcheckLanguage policy.
     </message>
   </if>
+  <message name="IDS_POLICY_LABEL_IS_AFFILIATED" desc="Label to indicate if the user is affiliated or not.">
+    Is affiliated:
+  </message>
+  <message name="IDS_POLICY_IS_AFFILIATED_YES" desc="Indicates the user is affiliated.">
+    Yes
+  </message>
+  <message name="IDS_POLICY_IS_AFFILIATED_NO" desc="Indicates the user is not affiliated.">
+    No
+  </message>
 </grit-part>
diff --git a/components/sessions/core/session_service_commands.cc b/components/sessions/core/session_service_commands.cc
index 159accb..99a3301 100644
--- a/components/sessions/core/session_service_commands.cc
+++ b/components/sessions/core/session_service_commands.cc
@@ -12,6 +12,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/pickle.h"
+#include "base/token.h"
 #include "components/sessions/core/base_session_service_commands.h"
 #include "components/sessions/core/base_session_service_delegate.h"
 #include "components/sessions/core/session_command.h"
@@ -56,6 +57,7 @@
 // static const SessionCommand::id_type kCommandSetWindowWorkspace = 22;
 static const SessionCommand::id_type kCommandSetWindowWorkspace2 = 23;
 static const SessionCommand::id_type kCommandTabNavigationPathPruned = 24;
+static const SessionCommand::id_type kCommandSetTabGroup = 25;
 
 namespace {
 
@@ -110,6 +112,18 @@
   int32_t count;
 };
 
+struct SerializedTabGroupId {
+  // These fields correspond to the high and low fields of |base::Token|.
+  uint64_t id_high;
+  uint64_t id_low;
+};
+
+struct TabGroupPayload {
+  SessionID::id_type tab_id;
+  SerializedTabGroupId maybe_group;
+  bool has_group;
+};
+
 struct PinnedStatePayload {
   SessionID::id_type tab_id;
   bool pinned_state;
@@ -544,6 +558,21 @@
         break;
       }
 
+      case kCommandSetTabGroup: {
+        TabGroupPayload payload;
+        if (!command->GetPayload(&payload, sizeof(payload))) {
+          DVLOG(1) << "Failed reading command " << command->id();
+          return true;
+        }
+        SessionTab* session_tab =
+            GetTab(SessionID::FromSerializedValue(payload.tab_id), tabs);
+        const base::Token token(payload.maybe_group.id_high,
+                                payload.maybe_group.id_low);
+        session_tab->group =
+            payload.has_group ? base::make_optional(token) : base::nullopt;
+        break;
+      }
+
       case kCommandSetPinnedState: {
         PinnedStatePayload payload;
         if (!command->GetPayload(&payload, sizeof(payload))) {
@@ -745,6 +774,20 @@
   return CreateSessionCommandForPayload(kCommandSetWindowType, payload);
 }
 
+std::unique_ptr<SessionCommand> CreateTabGroupCommand(
+    const SessionID& tab_id,
+    base::Optional<base::Token> group) {
+  TabGroupPayload payload = {0};
+  payload.tab_id = tab_id.id();
+  if (group.has_value()) {
+    DCHECK(!group.value().is_zero());
+    payload.maybe_group.id_high = group.value().high();
+    payload.maybe_group.id_low = group.value().low();
+    payload.has_group = true;
+  }
+  return CreateSessionCommandForPayload(kCommandSetTabGroup, payload);
+}
+
 std::unique_ptr<SessionCommand> CreatePinnedStateCommand(
     const SessionID& tab_id,
     bool is_pinned) {
diff --git a/components/sessions/core/session_service_commands.h b/components/sessions/core/session_service_commands.h
index 0ed6fa72..ce0a0c6 100644
--- a/components/sessions/core/session_service_commands.h
+++ b/components/sessions/core/session_service_commands.h
@@ -11,7 +11,9 @@
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/task/cancelable_task_tracker.h"
+#include "base/token.h"
 #include "components/sessions/core/base_session_service.h"
 #include "components/sessions/core/session_types.h"
 #include "components/sessions/core/sessions_export.h"
@@ -44,6 +46,9 @@
 SESSIONS_EXPORT std::unique_ptr<SessionCommand> CreateSetWindowTypeCommand(
     const SessionID& window_id,
     SessionWindow::WindowType type);
+SESSIONS_EXPORT std::unique_ptr<SessionCommand> CreateTabGroupCommand(
+    const SessionID& tab_id,
+    base::Optional<base::Token> group);
 SESSIONS_EXPORT std::unique_ptr<SessionCommand> CreatePinnedStateCommand(
     const SessionID& tab_id,
     bool is_pinned);
diff --git a/components/sessions/core/session_types.h b/components/sessions/core/session_types.h
index b161ca5..b160f2e9 100644
--- a/components/sessions/core/session_types.h
+++ b/components/sessions/core/session_types.h
@@ -11,8 +11,10 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
+#include "base/token.h"
 #include "components/sessions/core/serialized_navigation_entry.h"
 #include "components/sessions/core/session_id.h"
 #include "components/sessions/core/sessions_export.h"
@@ -65,6 +67,9 @@
   // checking must be performed before indexing into |navigations|.
   int current_navigation_index;
 
+  // The tab's group ID, if any.
+  base::Optional<base::Token> group;
+
   // True if the tab is pinned.
   bool pinned;
 
diff --git a/components/signin/core/browser/fake_profile_oauth2_token_service.cc b/components/signin/core/browser/fake_profile_oauth2_token_service.cc
index 4d2899e..a5a0ce1 100644
--- a/components/signin/core/browser/fake_profile_oauth2_token_service.cc
+++ b/components/signin/core/browser/fake_profile_oauth2_token_service.cc
@@ -161,7 +161,7 @@
 }
 
 void FakeProfileOAuth2TokenService::CancelRequestsForAccount(
-    const std::string& account_id) {
+    const CoreAccountId& account_id) {
   CompleteRequests(
       account_id, true, ScopeSet(),
       GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED),
@@ -170,7 +170,7 @@
 
 void FakeProfileOAuth2TokenService::FetchOAuth2Token(
     RequestImpl* request,
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& client_id,
     const std::string& client_secret,
@@ -197,7 +197,7 @@
 }
 
 void FakeProfileOAuth2TokenService::InvalidateAccessTokenImpl(
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     const std::string& client_id,
     const ScopeSet& scopes,
     const std::string& access_token) {
diff --git a/components/signin/core/browser/fake_profile_oauth2_token_service.h b/components/signin/core/browser/fake_profile_oauth2_token_service.h
index be71e20..420b36f 100644
--- a/components/signin/core/browser/fake_profile_oauth2_token_service.h
+++ b/components/signin/core/browser/fake_profile_oauth2_token_service.h
@@ -103,17 +103,17 @@
   // OAuth2TokenService overrides.
   void CancelAllRequests() override;
 
-  void CancelRequestsForAccount(const std::string& account_id) override;
+  void CancelRequestsForAccount(const CoreAccountId& account_id) override;
 
   void FetchOAuth2Token(
       RequestImpl* request,
-      const std::string& account_id,
+      const CoreAccountId& account_id,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& client_id,
       const std::string& client_secret,
       const ScopeSet& scopes) override;
 
-  void InvalidateAccessTokenImpl(const std::string& account_id,
+  void InvalidateAccessTokenImpl(const CoreAccountId& account_id,
                                  const std::string& client_id,
                                  const ScopeSet& scopes,
                                  const std::string& access_token) override;
diff --git a/components/signin/core/browser/oauth_multilogin_helper_unittest.cc b/components/signin/core/browser/oauth_multilogin_helper_unittest.cc
index 0de493c6b..ce0059202 100644
--- a/components/signin/core/browser/oauth_multilogin_helper_unittest.cc
+++ b/components/signin/core/browser/oauth_multilogin_helper_unittest.cc
@@ -21,8 +21,8 @@
 
 namespace {
 
-const char kAccountId[] = "account_id_1";
-const char kAccountId2[] = "account_id_2";
+const CoreAccountId kAccountId("account_id_1");
+const CoreAccountId kAccountId2("account_id_2");
 const char kAccessToken[] = "access_token_1";
 const char kAccessToken2[] = "access_token_2";
 
@@ -121,7 +121,7 @@
 class MockTokenService : public FakeOAuth2TokenService {
  public:
   MOCK_METHOD2(InvalidateTokenForMultilogin,
-               void(const std::string& account_id, const std::string& token));
+               void(const CoreAccountId& account_id, const std::string& token));
 };
 
 }  // namespace
diff --git a/components/strings/components_strings_fa.xtb b/components/strings/components_strings_fa.xtb
index d964a61..605cc9f 100644
--- a/components/strings/components_strings_fa.xtb
+++ b/components/strings/components_strings_fa.xtb
@@ -1150,7 +1150,7 @@
 <translation id="7298195798382681320">توصیه می‌شود</translation>
 <translation id="7300012071106347854">آبی پررنگ</translation>
 <translation id="7302712225291570345">«<ph name="TEXT" />»</translation>
-<translation id="7303701124147721189">‏برای اینکه دفعه بعد پرداخت سریع‌تری داشته باشید، اطلاعات کارت، نشانی صورت‌حسابتان را در حساب Google خود و این دستگاه ذخیره کنید.</translation>
+<translation id="7303701124147721189">‏برای اینکه دفعه بعد پرداخت سریع‌تری داشته باشید، اطلاعات کارت و نشانی صورت‌حسابتان را در حساب Google خود و این دستگاه ذخیره کنید.</translation>
 <translation id="7309308571273880165">گزارش خرابی ثبت‌شده در <ph name="CRASH_TIME" /> (کاربر درخواست بارگذاری کرده است، هنوز بارگذاری نشده است)</translation>
 <translation id="7313965965371928911">هشدار <ph name="BEGIN_LINK" />مرور ایمن<ph name="END_LINK" /></translation>
 <translation id="7319430975418800333">A3</translation>
@@ -1379,7 +1379,7 @@
 <translation id="8428213095426709021">تنظیمات</translation>
 <translation id="8433057134996913067">با این کار از سیستم بیشتر وب‌سایت‌ها خارج می‌شوید.</translation>
 <translation id="8437238597147034694">&amp;واگرد انتقال</translation>
-<translation id="8440508849222143646">‏برای اینکه دفعه بعد پرداخت سریع‌تری داشته باشید، اطلاعات کارت، نشانی صورت‌حسابتان را در حساب Google خود ذخیره کنید.</translation>
+<translation id="8440508849222143646">‏برای اینکه دفعه بعد پرداخت سریع‌تری داشته باشید، اطلاعات کارت و نشانی صورت‌حسابتان را در حساب Google خود ذخیره کنید.</translation>
 <translation id="8461694314515752532">رمزگذاری داده‌های همگام‌سازی‌شده با عبارت عبور همگام‌سازی خودتان</translation>
 <translation id="8466379296835108687">{COUNT,plural, =1{۱ کارت اعتباری}one{# کارت اعتباری}other{# کارت اعتباری}}</translation>
 <translation id="8473863474539038330">نشانی‌ها و سایر موارد</translation>
diff --git a/components/strings/components_strings_id.xtb b/components/strings/components_strings_id.xtb
index 3bcc7917..f24198d 100644
--- a/components/strings/components_strings_id.xtb
+++ b/components/strings/components_strings_id.xtb
@@ -178,7 +178,7 @@
 <translation id="1800473098294731951">B9</translation>
 <translation id="1803264062614276815">Nama Pemegang Kartu</translation>
 <translation id="1821930232296380041">Permintaan atau parameter permintaan tidak valid</translation>
-<translation id="1822370876374026111">Agar selanjutnya dapat melakukan pembayaran lebih cepat, tambahkan kartu dan alamat penagihan ke Akun Google Anda dan perangkat ini.</translation>
+<translation id="1822370876374026111">Agar selanjutnya dapat melakukan pembayaran lebih cepat, tambahkan informasi kartu dan alamat penagihan ke Akun Google Anda dan perangkat ini.</translation>
 <translation id="1822540298136254167">Situs yang Anda kunjungi dan waktu yang dihabiskan untuk mengunjunginya</translation>
 <translation id="1826516787628120939">Memeriksa</translation>
 <translation id="1834321415901700177">Situs ini berisi program yang berbahaya</translation>
@@ -1147,7 +1147,7 @@
 <translation id="7298195798382681320">Direkomendasikan</translation>
 <translation id="7300012071106347854">Biru Kobalt</translation>
 <translation id="7302712225291570345">"<ph name="TEXT" />"</translation>
-<translation id="7303701124147721189">Agar selanjutnya dapat melakukan pembayaran lebih cepat, tambahkan kartu dan alamat penagihan ke Akun Google Anda dan perangkat ini.</translation>
+<translation id="7303701124147721189">Agar selanjutnya dapat melakukan pembayaran lebih cepat, tambahkan info kartu dan alamat penagihan ke Akun Google Anda dan perangkat ini.</translation>
 <translation id="7309308571273880165">Laporan kerusakan diambil pada <ph name="CRASH_TIME" /> (upload yang diminta pengguna, belum diupload)</translation>
 <translation id="7313965965371928911">Peringatan <ph name="BEGIN_LINK" />Safe Browsing<ph name="END_LINK" /></translation>
 <translation id="7319430975418800333">A3</translation>
@@ -1374,7 +1374,7 @@
 <translation id="8428213095426709021">Setelan</translation>
 <translation id="8433057134996913067">Tindakan ini akan mengeluarkan Anda dari sebagian besar situs web.</translation>
 <translation id="8437238597147034694">&amp;Urungkan pemindahan</translation>
-<translation id="8440508849222143646">Agar selanjutnya dapat melakukan pembayaran lebih cepat, tambahkan kartu dan alamat penagihan ke Akun Google Anda dan perangkat ini.</translation>
+<translation id="8440508849222143646">Agar selanjutnya dapat melakukan pembayaran lebih cepat, tambahkan informasi kartu dan alamat penagihan ke Akun Google Anda dan perangkat ini.</translation>
 <translation id="8461694314515752532">Enkripsikan data yang disinkronkan dengan frasa sandi sinkronisasi Anda sendiri</translation>
 <translation id="8466379296835108687">{COUNT,plural, =1{1 kartu kredit}other{# kartu kredit}}</translation>
 <translation id="8473863474539038330">Alamat dan lain-lain</translation>
diff --git a/components/strings/components_strings_th.xtb b/components/strings/components_strings_th.xtb
index 557729b..22791021 100644
--- a/components/strings/components_strings_th.xtb
+++ b/components/strings/components_strings_th.xtb
@@ -178,7 +178,7 @@
 <translation id="1800473098294731951">B9</translation>
 <translation id="1803264062614276815">ชื่อผู้ถือบัตร</translation>
 <translation id="1821930232296380041">คำขอหรือพารามิเตอร์คำขอไม่ถูกต้อง</translation>
-<translation id="1822370876374026111">เพื่อให้ชำระเงินได้เร็วขึ้นในครั้งถัดไป โปรดเพิ่มบัตรและที่อยู่สำหรับการเรียกเก็บเงินไว้ในบัญชี Google และในอุปกรณ์นี้</translation>
+<translation id="1822370876374026111">โปรดเพิ่มบัตรและที่อยู่สำหรับการเรียกเก็บเงินไว้ในบัญชี Google และในอุปกรณ์นี้เพื่อให้ชำระเงินได้เร็วขึ้นในครั้งถัดไป</translation>
 <translation id="1822540298136254167">เว็บไซต์ที่คุณเข้าชมและเวลาที่ใช้ไป</translation>
 <translation id="1826516787628120939">กำลังตรวจสอบ</translation>
 <translation id="1834321415901700177">เว็บไซต์นี้มีโปรแกรมอันตราย</translation>
@@ -595,7 +595,7 @@
 <translation id="4173315687471669144">กระดาษฟุลสแก๊ป</translation>
 <translation id="4173827307318847180">{MORE_ITEMS,plural, =1{อีก <ph name="ITEM_COUNT" /> รายการ}other{อีก <ph name="ITEM_COUNT" /> รายการ}}</translation>
 <translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4193750898749511769">เพื่อให้ชำระเงินได้เร็วขึ้นในครั้งถัดไป โปรดเพิ่มบัตรและที่อยู่สำหรับการเรียกเก็บเงินไว้ในบัญชี Google</translation>
+<translation id="4193750898749511769">โปรดเพิ่มบัตรและที่อยู่สำหรับการเรียกเก็บเงินไว้ในบัญชี Google เพื่อให้ชำระเงินได้เร็วขึ้นในครั้งถัดไป</translation>
 <translation id="4196861286325780578">&amp;ทำซ้ำการย้าย</translation>
 <translation id="4203896806696719780"><ph name="BEGIN_LINK" />ตรวจสอบไฟร์วอลล์และการกำหนดค่าการป้องกันไวรัส<ph name="END_LINK" /></translation>
 <translation id="4215751373031079683">7x9 (ซองจดหมาย)</translation>
@@ -1149,7 +1149,7 @@
 <translation id="7298195798382681320">แนะนำ</translation>
 <translation id="7300012071106347854">น้ำเงินเข้ม</translation>
 <translation id="7302712225291570345">"<ph name="TEXT" />"</translation>
-<translation id="7303701124147721189">เพื่อให้ชำระเงินได้เร็วขึ้นในครั้งถัดไป โปรดเก็บข้อมูลบัตรและที่อยู่สำหรับการเรียกเก็บเงินไว้ในบัญชี Google และในอุปกรณ์นี้</translation>
+<translation id="7303701124147721189">โปรดเก็บข้อมูลบัตรและที่อยู่สำหรับการเรียกเก็บเงินไว้ในบัญชี Google และในอุปกรณ์นี้เพื่อให้ชำระเงินได้เร็วขึ้นในครั้งถัดไป</translation>
 <translation id="7309308571273880165">รายงานข้อขัดข้องเมื่อ <ph name="CRASH_TIME" /> (ผู้ใช้ขอการอัปโหลด ยังไม่ได้อัปโหลด)</translation>
 <translation id="7313965965371928911">คำเตือน <ph name="BEGIN_LINK" />Google Safe Browsing<ph name="END_LINK" /></translation>
 <translation id="7319430975418800333">A3</translation>
@@ -1378,7 +1378,7 @@
 <translation id="8428213095426709021">การตั้งค่า</translation>
 <translation id="8433057134996913067">วิธีนี้จะทำให้คุณออกจากระบบของเว็บไซต์ส่วนใหญ่</translation>
 <translation id="8437238597147034694">&amp;เลิกทำการย้าย</translation>
-<translation id="8440508849222143646">เพื่อให้ชำระเงินได้เร็วขึ้นในครั้งถัดไป โปรดเก็บข้อมูลบัตรและที่อยู่สำหรับการเรียกเก็บเงินไว้ในบัญชี Google</translation>
+<translation id="8440508849222143646">โปรดเก็บข้อมูลบัตรและที่อยู่สำหรับการเรียกเก็บเงินไว้ในบัญชี Google เพื่อให้ชำระเงินได้เร็วขึ้นในครั้งถัดไป</translation>
 <translation id="8461694314515752532">เข้ารหัสลับข้อมูลที่ซิงค์ด้วยรหัสผ่านการซิงค์ของคุณเอง</translation>
 <translation id="8466379296835108687">{COUNT,plural, =1{บัตรเครดิต 1 ใบ}other{บัตรเครดิต # ใบ}}</translation>
 <translation id="8473863474539038330">ที่อยู่และอื่นๆ</translation>
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index a8b0637..bb7a171 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -449,16 +449,12 @@
     "engine/mock_sync_engine.h",
     "engine/sync_engine_host_stub.cc",
     "engine/sync_engine_host_stub.h",
-    "engine/sync_manager_factory_for_profile_sync_test.cc",
-    "engine/sync_manager_factory_for_profile_sync_test.h",
     "engine/test_engine_components_factory.cc",
     "engine/test_engine_components_factory.h",
     "engine_impl/cycle/mock_debug_info_getter.cc",
     "engine_impl/cycle/mock_debug_info_getter.h",
     "engine_impl/cycle/test_util.cc",
     "engine_impl/cycle/test_util.h",
-    "engine_impl/sync_manager_for_profile_sync_test.cc",
-    "engine_impl/sync_manager_for_profile_sync_test.h",
     "engine_impl/test_entry_factory.cc",
     "engine_impl/test_entry_factory.h",
     "syncable/test_user_share.cc",
diff --git a/components/sync/driver/BUILD.gn b/components/sync/driver/BUILD.gn
index b2458d25..ca04b2c 100644
--- a/components/sync/driver/BUILD.gn
+++ b/components/sync/driver/BUILD.gn
@@ -164,8 +164,6 @@
     "sync_client_mock.h",
     "sync_user_settings_mock.cc",
     "sync_user_settings_mock.h",
-    "test_profile_sync_service.cc",
-    "test_profile_sync_service.h",
     "test_sync_service.cc",
     "test_sync_service.h",
     "test_sync_user_settings.cc",
diff --git a/components/sync/driver/data_type_manager_impl.cc b/components/sync/driver/data_type_manager_impl.cc
index a8c9a81..fbc39b6 100644
--- a/components/sync/driver/data_type_manager_impl.cc
+++ b/components/sync/driver/data_type_manager_impl.cc
@@ -77,6 +77,24 @@
       weak_ptr_factory_(this) {
   DCHECK(configurer_);
   DCHECK(observer_);
+
+  // Check if any of the controllers are already in a FAILED state, and if so,
+  // mark them accordingly in the status table.
+  DataTypeStatusTable::TypeErrorMap existing_errors;
+  for (const auto& kv : *controllers_) {
+    ModelType type = kv.first;
+    const DataTypeController* controller = kv.second.get();
+    DataTypeController::State state = controller->state();
+    DCHECK(state == DataTypeController::NOT_RUNNING ||
+           state == DataTypeController::STOPPING ||
+           state == DataTypeController::FAILED);
+    if (state == DataTypeController::FAILED) {
+      existing_errors[type] =
+          SyncError(FROM_HERE, SyncError::DATATYPE_ERROR,
+                    "Preexisting controller error on Sync startup", type);
+    }
+  }
+  data_type_status_table_.UpdateFailedDataTypes(existing_errors);
 }
 
 DataTypeManagerImpl::~DataTypeManagerImpl() {}
diff --git a/components/sync/driver/data_type_manager_impl_unittest.cc b/components/sync/driver/data_type_manager_impl_unittest.cc
index 4e94aec..532af2b72 100644
--- a/components/sync/driver/data_type_manager_impl_unittest.cc
+++ b/components/sync/driver/data_type_manager_impl_unittest.cc
@@ -251,7 +251,9 @@
   ~SyncDataTypeManagerImplTest() override {}
 
  protected:
-  void SetUp() override {
+  void SetUp() override { RecreateDataTypeManager(); }
+
+  void RecreateDataTypeManager() {
     dtm_ = std::make_unique<TestDataTypeManager>(
         ModelTypeSet(), WeakHandle<DataTypeDebugInfoListener>(), &controllers_,
         &encryption_handler_, &configurer_, &observer_);
@@ -1555,6 +1557,45 @@
   EXPECT_EQ(0U, configurer_.activated_types().Size());
 }
 
+// Checks that DTM handles the case when a controller is already in a FAILED
+// state at the time the DTM is created. Regression test for crbug.com/967344.
+TEST_F(SyncDataTypeManagerImplTest, ErrorBeforeStartup) {
+  AddController(BOOKMARKS);
+  AddController(PREFERENCES);
+
+  // Produce an error (FAILED) state in the BOOKMARKS controller.
+  GetController(BOOKMARKS)->SetModelLoadError(SyncError(
+      FROM_HERE, SyncError::DATATYPE_ERROR, "bookmarks error", BOOKMARKS));
+  SetConfigureStartExpectation();
+  Configure({BOOKMARKS});
+  GetController(BOOKMARKS)->SimulateModelLoadFinishing();
+  ASSERT_EQ(GetController(BOOKMARKS)->state(), DataTypeController::FAILED);
+
+  // Now create a fresh DTM, simulating a Sync restart.
+  RecreateDataTypeManager();
+
+  ASSERT_EQ(GetController(BOOKMARKS)->state(), DataTypeController::FAILED);
+
+  // Now a configuration attempt for both types should complete successfully,
+  // but exclude the failed type.
+  SetConfigureStartExpectation();
+  Configure({BOOKMARKS, PREFERENCES});
+
+  SetConfigureDoneExpectation(
+      DataTypeManager::OK, BuildStatusTable(/*crypto_errors=*/{},
+                                            /*association_errors=*/{BOOKMARKS},
+                                            /*unready_errore=*/{},
+                                            /*unrecoverable_errors=*/{}));
+  FinishDownload(/*types_to_configure=*/{},
+                 /*failed_download_types=*/{});
+  FinishDownload(/*types_to_configure=*/{PREFERENCES},
+                 /*failed_download_types=*/{});
+  GetController(PREFERENCES)->FinishStart(DataTypeController::OK);
+
+  EXPECT_TRUE(dtm_->GetActiveDataTypes().Has(PREFERENCES));
+  EXPECT_FALSE(dtm_->GetActiveDataTypes().Has(BOOKMARKS));
+}
+
 TEST_F(SyncDataTypeManagerImplTest, AssociationNeverCompletes) {
   AddController(BOOKMARKS);
 
diff --git a/components/sync/driver/model_association_manager.cc b/components/sync/driver/model_association_manager.cc
index b80d0437..5d5ce05 100644
--- a/components/sync/driver/model_association_manager.cc
+++ b/components/sync/driver/model_association_manager.cc
@@ -123,8 +123,14 @@
   // Only keep types that have controllers.
   desired_types_.Clear();
   for (ModelType type : desired_types) {
-    if (controllers_->find(type) != controllers_->end())
+    auto dtc_iter = controllers_->find(type);
+    if (dtc_iter != controllers_->end()) {
+      DataTypeController* dtc = dtc_iter->second.get();
+      // Controllers in a FAILED state should have been filtered out by the
+      // DataTypeManager.
+      DCHECK_NE(dtc->state(), DataTypeController::FAILED);
       desired_types_.Put(type);
+    }
   }
 
   DVLOG(1) << "ModelAssociationManager: Initializing for "
diff --git a/components/sync/driver/profile_sync_service.h b/components/sync/driver/profile_sync_service.h
index eb371806..21a5decbc 100644
--- a/components/sync/driver/profile_sync_service.h
+++ b/components/sync/driver/profile_sync_service.h
@@ -258,8 +258,6 @@
   SyncClient* GetSyncClientForTest();
 
  private:
-  friend class TestProfileSyncService;
-
   // Passed as an argument to StopImpl to control whether or not the sync
   // engine should clear its data directory when it shuts down. See StopImpl
   // for more information.
diff --git a/components/sync/driver/test_profile_sync_service.cc b/components/sync/driver/test_profile_sync_service.cc
deleted file mode 100644
index d9fccd3..0000000
--- a/components/sync/driver/test_profile_sync_service.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/driver/test_profile_sync_service.h"
-
-#include <utility>
-
-#include "base/run_loop.h"
-
-namespace syncer {
-
-syncer::WeakHandle<syncer::JsEventHandler>
-TestProfileSyncService::GetJsEventHandler() {
-  return syncer::WeakHandle<syncer::JsEventHandler>();
-}
-
-TestProfileSyncService::TestProfileSyncService(
-    ProfileSyncService::InitParams init_params)
-    : ProfileSyncService(std::move(init_params)) {}
-
-TestProfileSyncService::~TestProfileSyncService() {}
-
-void TestProfileSyncService::OnConfigureDone(
-    const syncer::DataTypeManager::ConfigureResult& result) {
-  ProfileSyncService::OnConfigureDone(result);
-  base::RunLoop::QuitCurrentWhenIdleDeprecated();
-}
-
-bool TestProfileSyncService::IsAuthenticatedAccountPrimary() const {
-  return true;
-}
-
-syncer::UserShare* TestProfileSyncService::GetUserShare() const {
-  return engine_->GetUserShare();
-}
-
-}  // namespace syncer
diff --git a/components/sync/driver/test_profile_sync_service.h b/components/sync/driver/test_profile_sync_service.h
deleted file mode 100644
index 091a91e..0000000
--- a/components/sync/driver/test_profile_sync_service.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_DRIVER_TEST_PROFILE_SYNC_SERVICE_H_
-#define COMPONENTS_SYNC_DRIVER_TEST_PROFILE_SYNC_SERVICE_H_
-
-#include "base/macros.h"
-#include "components/sync/base/weak_handle.h"
-#include "components/sync/driver/data_type_manager.h"
-#include "components/sync/driver/profile_sync_service.h"
-#include "components/sync/js/js_event_handler.h"
-
-namespace syncer {
-
-class SyncPrefs;
-
-class TestProfileSyncService : public syncer::ProfileSyncService {
- public:
-  explicit TestProfileSyncService(InitParams init_params);
-
-  ~TestProfileSyncService() override;
-
-  void OnConfigureDone(
-      const syncer::DataTypeManager::ConfigureResult& result) override;
-
-  // TODO(crbug.com/871221): This is overridden here to return true by default,
-  // as a workaround for tests not setting up an authenticated account, and
-  // IsSyncFeatureEnabled() therefore returning false.
-  bool IsAuthenticatedAccountPrimary() const override;
-
-  // We implement our own version to avoid some DCHECKs.
-  syncer::UserShare* GetUserShare() const override;
-
-  // Raise visibility to ease testing.
-  using ProfileSyncService::NotifyObservers;
-
-  syncer::SyncPrefs* sync_prefs() { return &sync_prefs_; }
-
- protected:
-  // Return null handle to use in backend initialization to avoid receiving
-  // js messages on UI loop when it's being destroyed, which are not deleted
-  // and cause memory leak in test.
-  syncer::WeakHandle<syncer::JsEventHandler> GetJsEventHandler() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TestProfileSyncService);
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_SYNC_DRIVER_TEST_PROFILE_SYNC_SERVICE_H_
diff --git a/components/sync/engine/sync_manager_factory_for_profile_sync_test.cc b/components/sync/engine/sync_manager_factory_for_profile_sync_test.cc
deleted file mode 100644
index 4d4bc58a..0000000
--- a/components/sync/engine/sync_manager_factory_for_profile_sync_test.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/engine/sync_manager_factory_for_profile_sync_test.h"
-
-#include "components/sync/engine_impl/sync_manager_for_profile_sync_test.h"
-
-namespace syncer {
-
-SyncManagerFactoryForProfileSyncTest::SyncManagerFactoryForProfileSyncTest(
-    base::OnceClosure init_callback,
-    network::NetworkConnectionTracker* network_connection_tracker)
-    : SyncManagerFactory(network_connection_tracker),
-      init_callback_(std::move(init_callback)) {}
-
-SyncManagerFactoryForProfileSyncTest::~SyncManagerFactoryForProfileSyncTest() {}
-
-std::unique_ptr<SyncManager>
-SyncManagerFactoryForProfileSyncTest::CreateSyncManager(
-    const std::string& name) {
-  return std::unique_ptr<SyncManager>(
-      new SyncManagerForProfileSyncTest(name, std::move(init_callback_)));
-}
-
-}  // namespace syncer
diff --git a/components/sync/engine/sync_manager_factory_for_profile_sync_test.h b/components/sync/engine/sync_manager_factory_for_profile_sync_test.h
deleted file mode 100644
index e9d8cdf..0000000
--- a/components/sync/engine/sync_manager_factory_for_profile_sync_test.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_ENGINE_SYNC_MANAGER_FACTORY_FOR_PROFILE_SYNC_TEST_H_
-#define COMPONENTS_SYNC_ENGINE_SYNC_MANAGER_FACTORY_FOR_PROFILE_SYNC_TEST_H_
-
-#include <memory>
-#include <string>
-
-#include "base/callback.h"
-#include "components/sync/engine/sync_manager_factory.h"
-
-namespace syncer {
-
-class SyncManagerFactoryForProfileSyncTest : public SyncManagerFactory {
- public:
-  SyncManagerFactoryForProfileSyncTest(
-      base::OnceClosure init_callback,
-      network::NetworkConnectionTracker* network_connection_tracker);
-  ~SyncManagerFactoryForProfileSyncTest() override;
-  std::unique_ptr<SyncManager> CreateSyncManager(
-      const std::string& name) override;
-
- private:
-  base::OnceClosure init_callback_;
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_SYNC_ENGINE_SYNC_MANAGER_FACTORY_FOR_PROFILE_SYNC_TEST_H_
diff --git a/components/sync/engine_impl/sync_manager_for_profile_sync_test.cc b/components/sync/engine_impl/sync_manager_for_profile_sync_test.cc
deleted file mode 100644
index 49bd633..0000000
--- a/components/sync/engine_impl/sync_manager_for_profile_sync_test.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/engine_impl/sync_manager_for_profile_sync_test.h"
-
-#include "components/sync/syncable/directory.h"
-#include "components/sync/syncable/test_user_share.h"
-#include "components/sync/syncable/user_share.h"
-#include "services/network/test/test_network_connection_tracker.h"
-
-namespace syncer {
-
-SyncManagerForProfileSyncTest::SyncManagerForProfileSyncTest(
-    std::string name,
-    base::OnceClosure init_callback)
-    : SyncManagerImpl(name,
-                      network::TestNetworkConnectionTracker::GetInstance()),
-      init_callback_(std::move(init_callback)) {}
-
-SyncManagerForProfileSyncTest::~SyncManagerForProfileSyncTest() {}
-
-void SyncManagerForProfileSyncTest::NotifyInitializationSuccess() {
-  UserShare* user_share = GetUserShare();
-  syncable::Directory* directory = user_share->directory.get();
-
-  if (!init_callback_.is_null())
-    std::move(init_callback_).Run();
-
-  ModelTypeSet early_download_types;
-  early_download_types.PutAll(ControlTypes());
-  early_download_types.PutAll(PriorityUserTypes());
-  for (ModelType type : early_download_types) {
-    if (!directory->InitialSyncEndedForType(type)) {
-      TestUserShare::CreateRoot(type, user_share);
-    }
-  }
-
-  SyncManagerImpl::NotifyInitializationSuccess();
-}
-
-}  // namespace syncer
diff --git a/components/sync/engine_impl/sync_manager_for_profile_sync_test.h b/components/sync/engine_impl/sync_manager_for_profile_sync_test.h
deleted file mode 100644
index f1399f8..0000000
--- a/components/sync/engine_impl/sync_manager_for_profile_sync_test.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_ENGINE_IMPL_SYNC_MANAGER_FOR_PROFILE_SYNC_TEST_H_
-#define COMPONENTS_SYNC_ENGINE_IMPL_SYNC_MANAGER_FOR_PROFILE_SYNC_TEST_H_
-
-#include <string>
-
-#include "base/callback.h"
-#include "base/compiler_specific.h"
-#include "components/sync/engine_impl/sync_manager_impl.h"
-
-namespace syncer {
-
-// This class is used to help implement the TestProfileSyncService.
-// Those tests try to test sync without instantiating a real backend.
-class SyncManagerForProfileSyncTest : public SyncManagerImpl {
- public:
-  SyncManagerForProfileSyncTest(std::string name,
-                                base::OnceClosure init_callback);
-  ~SyncManagerForProfileSyncTest() override;
-  void NotifyInitializationSuccess() override;
-
- private:
-  base::OnceClosure init_callback_;
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_SYNC_ENGINE_IMPL_SYNC_MANAGER_FOR_PROFILE_SYNC_TEST_H_
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h
index 0054fd7..e759920 100644
--- a/components/sync/protocol/proto_visitors.h
+++ b/components/sync/protocol/proto_visitors.h
@@ -896,6 +896,7 @@
   VISIT_REP(navigation_redirect);
   VISIT(last_navigation_redirect_url);
   VISIT(correct_referrer_policy);
+  VISIT(page_language);
   VISIT_ENUM(password_state);
   VISIT(task_id);
   VISIT_REP(ancestor_task_id);
diff --git a/components/sync/protocol/session_specifics.proto b/components/sync/protocol/session_specifics.proto
index fdae362..12cdd28 100644
--- a/components/sync/protocol/session_specifics.proto
+++ b/components/sync/protocol/session_specifics.proto
@@ -173,6 +173,10 @@
   // an entry is replaced multiple times, it represents data prior to the
   // *first* replace.
   optional ReplacedNavigation replaced_navigation = 29;
+
+  // The page language as determined by its textual content. An ISO 639 language
+  // code (two letters, except for Chinese where a localization is necessary).
+  optional string page_language = 30;
 }
 
 // Navigation information for a single redirection within a single navigation.
diff --git a/components/sync_bookmarks/bookmark_change_processor.cc b/components/sync_bookmarks/bookmark_change_processor.cc
index 35a73a07..b12124d 100644
--- a/components/sync_bookmarks/bookmark_change_processor.cc
+++ b/components/sync_bookmarks/bookmark_change_processor.cc
@@ -250,12 +250,9 @@
     } else {
       const BookmarkNode* parent = node->parent();
       int index = parent->GetIndexOf(node);
-      sync_id = CreateSyncNode(parent,
-                               bookmark_model_,
-                               index,
-                               &trans,
-                               model_associator_,
-                               error_handler());
+      DCHECK_NE(-1, index);
+      sync_id = CreateSyncNode(parent, bookmark_model_, size_t{index}, &trans,
+                               model_associator_, error_handler());
     }
   }
 
@@ -281,9 +278,9 @@
 
 void BookmarkChangeProcessor::BookmarkNodeAdded(BookmarkModel* model,
                                                 const BookmarkNode* parent,
-                                                int index) {
+                                                size_t index) {
   DCHECK(share_handle());
-  const BookmarkNode* node = parent->GetChild(index);
+  const BookmarkNode* node = parent->children()[index].get();
   if (CanSyncNode(node))
     CreateOrUpdateSyncNode(node);
 }
@@ -292,11 +289,11 @@
 int64_t BookmarkChangeProcessor::CreateSyncNode(
     const BookmarkNode* parent,
     BookmarkModel* model,
-    int index,
+    size_t index,
     syncer::WriteTransaction* trans,
     BookmarkModelAssociator* associator,
     syncer::DataTypeErrorHandler* error_handler) {
-  const BookmarkNode* child = parent->GetChild(index);
+  const BookmarkNode* child = parent->children()[index].get();
   DCHECK(child);
 
   // Create a WriteNode container to hold the new node.
@@ -321,7 +318,7 @@
 
 void BookmarkChangeProcessor::OnWillRemoveBookmarks(BookmarkModel* model,
                                                     const BookmarkNode* parent,
-                                                    int old_index,
+                                                    size_t old_index,
                                                     const BookmarkNode* node) {
   if (CanSyncNode(node))
     RemoveSyncNodeHierarchy(node);
@@ -330,7 +327,7 @@
 void BookmarkChangeProcessor::BookmarkNodeRemoved(
     BookmarkModel* model,
     const BookmarkNode* parent,
-    int old_index,
+    size_t old_index,
     const BookmarkNode* node,
     const std::set<GURL>& no_longer_bookmarked) {
   // All the work should have already been done in OnWillRemoveBookmarks.
@@ -387,9 +384,11 @@
 }
 
 void BookmarkChangeProcessor::BookmarkNodeMoved(BookmarkModel* model,
-      const BookmarkNode* old_parent, int old_index,
-      const BookmarkNode* new_parent, int new_index) {
-  const BookmarkNode* child = new_parent->GetChild(new_index);
+                                                const BookmarkNode* old_parent,
+                                                size_t old_index,
+                                                const BookmarkNode* new_parent,
+                                                size_t new_index) {
+  const BookmarkNode* child = new_parent->children()[new_index].get();
 
   if (!CanSyncNode(child))
     return;
@@ -473,8 +472,8 @@
 
     // The given node's children got reordered. We need to reorder all the
     // children of the corresponding sync node.
-    for (int i = 0; i < node->child_count(); ++i) {
-      const BookmarkNode* child = node->GetChild(i);
+    for (size_t i = 0; i < node->children().size(); ++i) {
+      const BookmarkNode* child = node->children()[i].get();
       children.push_back(child);
 
       syncer::WriteNode sync_child(&trans);
@@ -507,9 +506,13 @@
 }
 
 // static
-bool BookmarkChangeProcessor::PlaceSyncNode(MoveOrCreate operation,
-      const BookmarkNode* parent, int index, syncer::WriteTransaction* trans,
-      syncer::WriteNode* dst, BookmarkModelAssociator* associator) {
+bool BookmarkChangeProcessor::PlaceSyncNode(
+    MoveOrCreate operation,
+    const BookmarkNode* parent,
+    size_t index,
+    syncer::WriteTransaction* trans,
+    syncer::WriteNode* dst,
+    BookmarkModelAssociator* associator) {
   syncer::ReadNode sync_parent(trans);
   if (!associator->InitSyncNodeFromChromeId(parent->id(), &sync_parent)) {
     LOG(WARNING) << "Parent lookup failed";
@@ -529,7 +532,7 @@
     }
   } else {
     // Find the bookmark model predecessor, and insert after it.
-    const BookmarkNode* prev = parent->GetChild(index - 1);
+    const BookmarkNode* prev = parent->children()[index - 1].get();
     syncer::ReadNode sync_prev(trans);
     if (!associator->InitSyncNodeFromChromeId(prev->id(), &sync_prev)) {
       LOG(WARNING) << "Predecessor lookup failed";
@@ -614,7 +617,7 @@
     if (!dst->children().empty()) {
       if (!foster_parent) {
         foster_parent = model->AddFolder(model->other_node(),
-                                         model->other_node()->child_count(),
+                                         model->other_node()->children().size(),
                                          base::string16());
         if (!foster_parent) {
           syncer::SyncError error(FROM_HERE,
@@ -627,7 +630,7 @@
       }
       for (int i = dst->child_count() - 1; i >= 0; --i) {
         model->Move(dst->GetChild(i), foster_parent,
-                    foster_parent->child_count());
+                    foster_parent->children().size());
       }
     }
     DCHECK_EQ(dst->child_count(), 0) << "Node being deleted has children";
@@ -702,14 +705,14 @@
                                  model_associator_->GetFaviconService());
 
       // Move all modified entries to the right.  We'll fix it later.
-      model->Move(dst, parent, parent->child_count());
+      model->Move(dst, parent, parent->children().size());
     } else {
       DCHECK(it->action == ChangeRecord::ACTION_ADD)
           << "ACTION_ADD should be seen if and only if the node is unknown.";
 
       dst = CreateBookmarkNode(&src, parent, model,
                                model_associator_->GetFaviconService(),
-                               parent->child_count());
+                               parent->children().size());
       if (!dst) {
         // We ignore bookmarks we can't add. Chances are this is caused by
         // a bookmark that was not fully associated.
@@ -730,7 +733,7 @@
   // sync order, left to right, moving them into their proper positions.
   for (auto it = to_reposition.begin(); it != to_reposition.end(); ++it) {
     const BookmarkNode* parent = it->second->parent();
-    model->Move(it->second, parent, it->first);
+    model->Move(it->second, parent, size_t{it->first});
   }
 
   // Clean up the temporary node.
@@ -798,7 +801,7 @@
     const BookmarkNode* parent,
     BookmarkModel* model,
     favicon::FaviconService* favicon_service,
-    int index) {
+    size_t index) {
   return CreateBookmarkNode(base::UTF8ToUTF16(sync_node->GetTitle()),
                             GURL(sync_node->GetBookmarkSpecifics().url()),
                             sync_node, parent, model, favicon_service, index);
@@ -814,7 +817,7 @@
     const BookmarkNode* parent,
     BookmarkModel* model,
     favicon::FaviconService* favicon_service,
-    int index) {
+    size_t index) {
   DCHECK(parent);
 
   const BookmarkNode* node;
diff --git a/components/sync_bookmarks/bookmark_change_processor.h b/components/sync_bookmarks/bookmark_change_processor.h
index e11a2dd..ab33d10 100644
--- a/components/sync_bookmarks/bookmark_change_processor.h
+++ b/components/sync_bookmarks/bookmark_change_processor.h
@@ -54,19 +54,19 @@
   void BookmarkModelBeingDeleted(bookmarks::BookmarkModel* model) override;
   void BookmarkNodeMoved(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* old_parent,
-                         int old_index,
+                         size_t old_index,
                          const bookmarks::BookmarkNode* new_parent,
-                         int new_index) override;
+                         size_t new_index) override;
   void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* parent,
-                         int index) override;
+                         size_t index) override;
   void OnWillRemoveBookmarks(bookmarks::BookmarkModel* model,
                              const bookmarks::BookmarkNode* parent,
-                             int old_index,
+                             size_t old_index,
                              const bookmarks::BookmarkNode* node) override;
   void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
                            const bookmarks::BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const bookmarks::BookmarkNode* node,
                            const std::set<GURL>& no_longer_bookmarked) override;
   void BookmarkAllUserNodesRemoved(bookmarks::BookmarkModel* model,
@@ -108,7 +108,7 @@
       const bookmarks::BookmarkNode* parent,
       bookmarks::BookmarkModel* model,
       favicon::FaviconService* favicon_service,
-      int index);
+      size_t index);
 
   // Overload of CreateBookmarkNode function above that helps to avoid
   // converting / parsing the bookmark title and URL multiple times.
@@ -119,7 +119,7 @@
       const bookmarks::BookmarkNode* parent,
       bookmarks::BookmarkModel* model,
       favicon::FaviconService* favicon_service,
-      int index);
+      size_t index);
 
   // Sets the favicon of the given bookmark node from the given sync node.
   static void SetBookmarkFavicon(const syncer::BaseNode* sync_node,
@@ -147,7 +147,7 @@
   // the ID of the just-created node, or if creation fails, kInvalidID.
   static int64_t CreateSyncNode(const bookmarks::BookmarkNode* parent,
                                 bookmarks::BookmarkModel* model,
-                                int index,
+                                size_t index,
                                 syncer::WriteTransaction* trans,
                                 BookmarkModelAssociator* associator,
                                 syncer::DataTypeErrorHandler* error_handler);
@@ -199,7 +199,7 @@
   // false on failure.
   static bool PlaceSyncNode(MoveOrCreate operation,
                             const bookmarks::BookmarkNode* parent,
-                            int index,
+                            size_t index,
                             syncer::WriteTransaction* trans,
                             syncer::WriteNode* dst,
                             BookmarkModelAssociator* associator);
diff --git a/components/sync_bookmarks/bookmark_model_associator.cc b/components/sync_bookmarks/bookmark_model_associator.cc
index 06c1a31..4c5c0d0d 100644
--- a/components/sync_bookmarks/bookmark_model_associator.cc
+++ b/components/sync_bookmarks/bookmark_model_associator.cc
@@ -669,7 +669,7 @@
     Context* context) {
   BookmarkNodeFinder node_finder(parent_node);
 
-  int index = 0;
+  size_t index = 0;
   for (auto it = sync_ids.begin(); it != sync_ids.end(); ++it) {
     int64_t sync_child_id = *it;
     syncer::ReadNode sync_child_node(trans);
@@ -689,8 +689,8 @@
       // the node is already associated and in the right position.
       bool is_in_sync = (context->native_model_sync_state() == IN_SYNC) &&
                         (child_node->id() == external_id) &&
-                        (index < parent_node->child_count()) &&
-                        (parent_node->GetChild(index) == child_node);
+                        (index < parent_node->children().size()) &&
+                        (parent_node->children()[index].get() == child_node);
       if (!is_in_sync) {
         BookmarkChangeProcessor::UpdateBookmarkWithSyncData(
             sync_child_node, bookmark_model_, child_node, favicon_service_);
@@ -726,7 +726,7 @@
   // the right positions: from 0 to index - 1.
   // So the children starting from index in the parent bookmark node are the
   // ones that are not present in the parent sync node. So create them.
-  for (int i = index; i < parent_node->child_count(); ++i) {
+  for (size_t i = index; i < parent_node->children().size(); ++i) {
     int64_t sync_child_id = BookmarkChangeProcessor::CreateSyncNode(
         parent_node, bookmark_model_, i, trans, this,
         unrecoverable_error_handler_.get());
@@ -747,12 +747,12 @@
 
 const BookmarkNode* BookmarkModelAssociator::CreateBookmarkNode(
     const BookmarkNode* parent_node,
-    int bookmark_index,
+    size_t bookmark_index,
     const syncer::BaseNode* sync_child_node,
     const GURL& url,
     Context* context,
     syncer::SyncError* error) {
-  DCHECK_LE(bookmark_index, parent_node->child_count());
+  DCHECK_LE(bookmark_index, parent_node->children().size());
 
   const std::string& sync_title = sync_child_node->GetTitle();
 
diff --git a/components/sync_bookmarks/bookmark_model_associator.h b/components/sync_bookmarks/bookmark_model_associator.h
index 1f4388a..db7c8b8b 100644
--- a/components/sync_bookmarks/bookmark_model_associator.h
+++ b/components/sync_bookmarks/bookmark_model_associator.h
@@ -279,7 +279,7 @@
   // Helper method for creating a new native bookmark node.
   const bookmarks::BookmarkNode* CreateBookmarkNode(
       const bookmarks::BookmarkNode* parent_node,
-      int bookmark_index,
+      size_t bookmark_index,
       const syncer::BaseNode* sync_child_node,
       const GURL& url,
       Context* context,
diff --git a/components/sync_bookmarks/bookmark_model_merger.cc b/components/sync_bookmarks/bookmark_model_merger.cc
index a4137db..6394150 100644
--- a/components/sync_bookmarks/bookmark_model_merger.cc
+++ b/components/sync_bookmarks/bookmark_model_merger.cc
@@ -273,15 +273,16 @@
   }
   // At this point all the children nodes of the parent sync node have
   // corresponding children in the parent bookmark node and they are all in the
-  // right positions: from 0 to |updates_tree_.at(remote_update).size() - 1|. So
-  // the children starting from index |updates_tree_.at(remote_update).size()|
-  // in the parent bookmark node are the ones that are not present in the parent
+  // right positions: from 0 to updates_tree_.at(remote_update).size() - 1. So
+  // the children starting from index updates_tree_.at(remote_update).size() in
+  // the parent bookmark node are the ones that are not present in the parent
   // sync node and tracked yet. So create all of the remaining local nodes.
-  const int index_of_new_local_nodes =
+  const size_t index_of_new_local_nodes =
       updates_tree_.count(remote_update) > 0
           ? updates_tree_.at(remote_update).size()
           : 0;
-  for (int i = index_of_new_local_nodes; i < local_node->child_count(); ++i) {
+  for (size_t i = index_of_new_local_nodes; i < local_node->children().size();
+       ++i) {
     ProcessLocalCreation(local_node, i);
   }
 }
@@ -289,7 +290,7 @@
 void BookmarkModelMerger::ProcessRemoteCreation(
     const UpdateResponseData* remote_update,
     const bookmarks::BookmarkNode* local_parent,
-    int index) {
+    size_t index) {
   const EntityData& remote_update_entity = *remote_update->entity;
   const bookmarks::BookmarkNode* bookmark_node =
       CreateBookmarkNodeFromSpecifics(
@@ -321,8 +322,7 @@
 
 void BookmarkModelMerger::ProcessLocalCreation(
     const bookmarks::BookmarkNode* parent,
-    int index) {
-  DCHECK_GT(index, -1);
+    size_t index) {
   const SyncedBookmarkTracker::Entity* parent_entity =
       bookmark_tracker_->GetEntityForBookmarkNode(parent);
   // Since we are merging top down, parent entity must be tracked.
@@ -345,21 +345,21 @@
   } else {
     const SyncedBookmarkTracker::Entity* predecessor_entity =
         bookmark_tracker_->GetEntityForBookmarkNode(
-            parent->GetChild(index - 1));
+            parent->children()[index - 1].get());
     pos = syncer::UniquePosition::After(
         syncer::UniquePosition::FromProto(
             predecessor_entity->metadata()->unique_position()),
         suffix);
   }
 
-  const bookmarks::BookmarkNode* node = parent->GetChild(index);
+  const bookmarks::BookmarkNode* node = parent->children()[index].get();
   const sync_pb::EntitySpecifics specifics = CreateSpecificsFromBookmarkNode(
       node, bookmark_model_, /*force_favicon_load=*/true);
   bookmark_tracker_->Add(sync_id, node, server_version, creation_time,
                          pos.ToProto(), specifics);
   // Mark the entity that it needs to be committed.
   bookmark_tracker_->IncrementSequenceNumber(sync_id);
-  for (int i = 0; i < node->child_count(); ++i) {
+  for (size_t i = 0; i < node->children().size(); ++i) {
     // If a local node hasn't matched with any remote entity, its descendants
     // will neither.
     ProcessLocalCreation(node, i);
diff --git a/components/sync_bookmarks/bookmark_model_merger.h b/components/sync_bookmarks/bookmark_model_merger.h
index 322e3fc..207881bd 100644
--- a/components/sync_bookmarks/bookmark_model_merger.h
+++ b/components/sync_bookmarks/bookmark_model_merger.h
@@ -60,13 +60,14 @@
   // |bookmark_tracker_| accordingly.
   void ProcessRemoteCreation(const syncer::UpdateResponseData* remote_update,
                              const bookmarks::BookmarkNode* local_parent,
-                             int index);
+                             size_t index);
 
   // Creates a server counter-part for the local node at position |index|
   // under |parent|. If the local node has children, corresponding server nodes
   // are created recursively. It updates the |bookmark_tracker_| accordingly and
   // new nodes are marked to be committed.
-  void ProcessLocalCreation(const bookmarks::BookmarkNode* parent, int index);
+  void ProcessLocalCreation(const bookmarks::BookmarkNode* parent,
+                            size_t index);
 
   // Gets the bookmark node corresponding to a permanent folder.
   // |update_entity| must contain server_defined_unique_tag that is used to
diff --git a/components/sync_bookmarks/bookmark_model_observer_impl.cc b/components/sync_bookmarks/bookmark_model_observer_impl.cc
index 69f418f..efb08761 100644
--- a/components/sync_bookmarks/bookmark_model_observer_impl.cc
+++ b/components/sync_bookmarks/bookmark_model_observer_impl.cc
@@ -45,10 +45,10 @@
 void BookmarkModelObserverImpl::BookmarkNodeMoved(
     bookmarks::BookmarkModel* model,
     const bookmarks::BookmarkNode* old_parent,
-    int old_index,
+    size_t old_index,
     const bookmarks::BookmarkNode* new_parent,
-    int new_index) {
-  const bookmarks::BookmarkNode* node = new_parent->GetChild(new_index);
+    size_t new_index) {
+  const bookmarks::BookmarkNode* node = new_parent->children()[new_index].get();
 
   // We shouldn't see changes to the top-level nodes.
   DCHECK(!model->is_permanent_node(node));
@@ -78,8 +78,8 @@
 void BookmarkModelObserverImpl::BookmarkNodeAdded(
     bookmarks::BookmarkModel* model,
     const bookmarks::BookmarkNode* parent,
-    int index) {
-  const bookmarks::BookmarkNode* node = parent->GetChild(index);
+    size_t index) {
+  const bookmarks::BookmarkNode* node = parent->children()[index].get();
   if (!model->client()->CanSyncNode(node)) {
     return;
   }
@@ -113,7 +113,7 @@
 void BookmarkModelObserverImpl::OnWillRemoveBookmarks(
     bookmarks::BookmarkModel* model,
     const bookmarks::BookmarkNode* parent,
-    int old_index,
+    size_t old_index,
     const bookmarks::BookmarkNode* node) {
   if (!model->client()->CanSyncNode(node)) {
     return;
@@ -126,7 +126,7 @@
 void BookmarkModelObserverImpl::BookmarkNodeRemoved(
     bookmarks::BookmarkModel* model,
     const bookmarks::BookmarkNode* parent,
-    int old_index,
+    size_t old_index,
     const bookmarks::BookmarkNode* node,
     const std::set<GURL>& removed_urls) {
   // All the work should have already been done in OnWillRemoveBookmarks.
@@ -283,7 +283,7 @@
 
 syncer::UniquePosition BookmarkModelObserverImpl::ComputePosition(
     const bookmarks::BookmarkNode& parent,
-    int index,
+    size_t index,
     const std::string& sync_id) {
   const std::string& suffix = syncer::GenerateSyncableBookmarkHash(
       bookmark_tracker_->model_type_state().cache_guid(), sync_id);
@@ -292,8 +292,9 @@
   const SyncedBookmarkTracker::Entity* successor_entity = nullptr;
 
   // Look for the first tracked predecessor.
-  for (int i = index - 1; i >= 0; i--) {
-    const bookmarks::BookmarkNode* predecessor_node = parent.GetChild(i);
+  for (auto i = parent.children().crend() - index;
+       i != parent.children().crend(); ++i) {
+    const bookmarks::BookmarkNode* predecessor_node = i->get();
     predecessor_entity =
         bookmark_tracker_->GetEntityForBookmarkNode(predecessor_node);
     if (predecessor_entity) {
@@ -302,8 +303,9 @@
   }
 
   // Look for the first tracked successor.
-  for (int i = index + 1; i < parent.child_count(); i++) {
-    const bookmarks::BookmarkNode* successor_node = parent.GetChild(i);
+  for (auto i = parent.children().cbegin() + index + 1;
+       i != parent.children().cend(); ++i) {
+    const bookmarks::BookmarkNode* successor_node = i->get();
     successor_entity =
         bookmark_tracker_->GetEntityForBookmarkNode(successor_node);
     if (successor_entity) {
diff --git a/components/sync_bookmarks/bookmark_model_observer_impl.h b/components/sync_bookmarks/bookmark_model_observer_impl.h
index f3fe156..4fdaa92c 100644
--- a/components/sync_bookmarks/bookmark_model_observer_impl.h
+++ b/components/sync_bookmarks/bookmark_model_observer_impl.h
@@ -39,19 +39,19 @@
   void BookmarkModelBeingDeleted(bookmarks::BookmarkModel* model) override;
   void BookmarkNodeMoved(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* old_parent,
-                         int old_index,
+                         size_t old_index,
                          const bookmarks::BookmarkNode* new_parent,
-                         int new_index) override;
+                         size_t new_index) override;
   void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* parent,
-                         int index) override;
+                         size_t index) override;
   void OnWillRemoveBookmarks(bookmarks::BookmarkModel* model,
                              const bookmarks::BookmarkNode* parent,
-                             int old_index,
+                             size_t old_index,
                              const bookmarks::BookmarkNode* node) override;
   void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
                            const bookmarks::BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const bookmarks::BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override;
   void OnWillRemoveAllUserBookmarks(bookmarks::BookmarkModel* model) override;
@@ -69,7 +69,7 @@
 
  private:
   syncer::UniquePosition ComputePosition(const bookmarks::BookmarkNode& parent,
-                                         int index,
+                                         size_t index,
                                          const std::string& sync_id);
 
   // Processes the deletion of a bookmake node and updates the
diff --git a/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc b/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc
index 85f246068..6a74391 100644
--- a/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc
+++ b/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc
@@ -252,7 +252,7 @@
   const bookmarks::BookmarkNode* bookmark_bar_node =
       bookmark_model()->bookmark_bar_node();
   std::vector<const bookmarks::BookmarkNode*> nodes;
-  for (int i = 0; i < 4; ++i) {
+  for (size_t i = 0; i < 4; ++i) {
     nodes.push_back(bookmark_model()->AddURL(
         /*parent=*/bookmark_bar_node, /*index=*/i, base::UTF8ToUTF16(kTitle),
         GURL(kUrl)));
@@ -600,7 +600,7 @@
   //  |- folder4
 
   const bookmarks::BookmarkNode* nodes[5];
-  for (int i = 0; i < 5; i++) {
+  for (size_t i = 0; i < 5; i++) {
     nodes[i] = bookmark_model()->AddFolder(
         /*parent=*/bookmark_bar_node, /*index=*/i,
         base::UTF8ToUTF16("folder" + std::to_string(i)));
diff --git a/components/sync_bookmarks/bookmark_remote_updates_handler.cc b/components/sync_bookmarks/bookmark_remote_updates_handler.cc
index 17157de8..c768faf 100644
--- a/components/sync_bookmarks/bookmark_remote_updates_handler.cc
+++ b/components/sync_bookmarks/bookmark_remote_updates_handler.cc
@@ -49,13 +49,13 @@
   }
 }
 
-int ComputeChildNodeIndex(const bookmarks::BookmarkNode* parent,
-                          const sync_pb::UniquePosition& unique_position,
-                          const SyncedBookmarkTracker* bookmark_tracker) {
+size_t ComputeChildNodeIndex(const bookmarks::BookmarkNode* parent,
+                             const sync_pb::UniquePosition& unique_position,
+                             const SyncedBookmarkTracker* bookmark_tracker) {
   const syncer::UniquePosition position =
       syncer::UniquePosition::FromProto(unique_position);
-  for (int i = 0; i < parent->child_count(); ++i) {
-    const bookmarks::BookmarkNode* child = parent->GetChild(i);
+  for (size_t i = 0; i < parent->children().size(); ++i) {
+    const bookmarks::BookmarkNode* child = parent->children()[i].get();
     const SyncedBookmarkTracker::Entity* child_entity =
         bookmark_tracker->GetEntityForBookmarkNode(child);
     DCHECK(child_entity);
@@ -66,7 +66,7 @@
       return i;
     }
   }
-  return parent->child_count();
+  return parent->children().size();
 }
 
 void ApplyRemoteUpdate(
@@ -98,8 +98,8 @@
   UpdateBookmarkNodeFromSpecifics(update_entity.specifics.bookmark(), node,
                                   model, favicon_service);
   // Compute index information before updating the |tracker|.
-  const int old_index = old_parent->GetIndexOf(node);
-  const int new_index =
+  const size_t old_index = size_t{old_parent->GetIndexOf(node)};
+  const size_t new_index =
       ComputeChildNodeIndex(new_parent, update_entity.unique_position, tracker);
   tracker->Update(update_entity.id, update.response_version,
                   update_entity.modification_time,
diff --git a/components/sync_bookmarks/bookmark_specifics_conversions.cc b/components/sync_bookmarks/bookmark_specifics_conversions.cc
index cbb4399..2be0e484 100644
--- a/components/sync_bookmarks/bookmark_specifics_conversions.cc
+++ b/components/sync_bookmarks/bookmark_specifics_conversions.cc
@@ -160,7 +160,7 @@
 const bookmarks::BookmarkNode* CreateBookmarkNodeFromSpecifics(
     const sync_pb::BookmarkSpecifics& specifics,
     const bookmarks::BookmarkNode* parent,
-    int index,
+    size_t index,
     bool is_folder,
     bookmarks::BookmarkModel* model,
     favicon::FaviconService* favicon_service) {
diff --git a/components/sync_bookmarks/bookmark_specifics_conversions.h b/components/sync_bookmarks/bookmark_specifics_conversions.h
index e0badc5..da67a05f 100644
--- a/components/sync_bookmarks/bookmark_specifics_conversions.h
+++ b/components/sync_bookmarks/bookmark_specifics_conversions.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_SPECIFICS_CONVERSIONS_H_
 #define COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_SPECIFICS_CONVERSIONS_H_
 
+#include <stddef.h>
+
 namespace bookmarks {
 class BookmarkModel;
 class BookmarkNode;
@@ -32,7 +34,7 @@
 const bookmarks::BookmarkNode* CreateBookmarkNodeFromSpecifics(
     const sync_pb::BookmarkSpecifics& specifics,
     const bookmarks::BookmarkNode* parent,
-    int index,
+    size_t index,
     bool is_folder,
     bookmarks::BookmarkModel* model,
     favicon::FaviconService* favicon_service);
diff --git a/components/sync_bookmarks/bookmark_specifics_conversions_unittest.cc b/components/sync_bookmarks/bookmark_specifics_conversions_unittest.cc
index b644be9..2cf5eed 100644
--- a/components/sync_bookmarks/bookmark_specifics_conversions_unittest.cc
+++ b/components/sync_bookmarks/bookmark_specifics_conversions_unittest.cc
@@ -96,7 +96,7 @@
 
   const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
   const std::vector<std::string> illegal_titles = {"", ".", ".."};
-  int index = 0;
+  size_t index = 0;
   for (const std::string& illegal_title : illegal_titles) {
     const bookmarks::BookmarkNode* node = model->AddURL(
         /*parent=*/bookmark_bar_node, index++, base::UTF8ToUTF16(illegal_title),
@@ -222,7 +222,7 @@
 
   const std::vector<std::string> illegal_titles = {"", ".", ".."};
 
-  int index = 0;
+  size_t index = 0;
   for (const std::string& illegal_title : illegal_titles) {
     sync_pb::EntitySpecifics specifics;
     sync_pb::BookmarkSpecifics* bm_specifics = specifics.mutable_bookmark();
diff --git a/components/sync_sessions/local_session_event_handler_impl.cc b/components/sync_sessions/local_session_event_handler_impl.cc
index 7557ec7..6480ebab 100644
--- a/components/sync_sessions/local_session_event_handler_impl.cc
+++ b/components/sync_sessions/local_session_event_handler_impl.cc
@@ -444,6 +444,10 @@
     sync_pb::TabNavigation* navigation = specifics.add_navigation();
     SessionNavigationToSyncData(serialized_entry).Swap(navigation);
 
+    const std::string page_language = tab_delegate.GetPageLanguageAtIndex(i);
+    if (!page_language.empty())
+      navigation->set_page_language(page_language);
+
     if (is_supervised) {
       navigation->set_blocked_state(
           sync_pb::TabNavigation_BlockedState_STATE_ALLOWED);
diff --git a/components/sync_sessions/local_session_event_handler_impl_unittest.cc b/components/sync_sessions/local_session_event_handler_impl_unittest.cc
index 65b4985..79fe3ed 100644
--- a/components/sync_sessions/local_session_event_handler_impl_unittest.cc
+++ b/components/sync_sessions/local_session_event_handler_impl_unittest.cc
@@ -143,6 +143,9 @@
   TestSyncedTabDelegate* tab = AddTabWithTime(kWindowId1, kFoo1, kTime1);
   tab->Navigate(kBar1, kTime2);
   tab->Navigate(kBaz1, kTime3);
+  tab->SetPageLanguageAtIndex(0, "en");
+  tab->SetPageLanguageAtIndex(1, "fr");
+  tab->SetPageLanguageAtIndex(2, "in");
   InitHandler();
 
   const sync_pb::SessionTab session_tab =
@@ -171,6 +174,9 @@
   EXPECT_FALSE(session_tab.navigation(0).has_blocked_state());
   EXPECT_FALSE(session_tab.navigation(1).has_blocked_state());
   EXPECT_FALSE(session_tab.navigation(2).has_blocked_state());
+  EXPECT_EQ("en", session_tab.navigation(0).page_language());
+  EXPECT_EQ("fr", session_tab.navigation(1).page_language());
+  EXPECT_EQ("in", session_tab.navigation(2).page_language());
 }
 
 // Ensure the current_navigation_index gets set properly when the navigation
diff --git a/components/sync_sessions/synced_tab_delegate.h b/components/sync_sessions/synced_tab_delegate.h
index d253ee17..e756fe0 100644
--- a/components/sync_sessions/synced_tab_delegate.h
+++ b/components/sync_sessions/synced_tab_delegate.h
@@ -50,6 +50,7 @@
   virtual GURL GetVirtualURLAtIndex(int i) const = 0;
   virtual GURL GetFaviconURLAtIndex(int i) const = 0;
   virtual ui::PageTransition GetTransitionAtIndex(int i) const = 0;
+  virtual std::string GetPageLanguageAtIndex(int i) const = 0;
   virtual void GetSerializedNavigationAtIndex(
       int i,
       sessions::SerializedNavigationEntry* serialized_entry) const = 0;
diff --git a/components/sync_sessions/test_synced_window_delegates_getter.cc b/components/sync_sessions/test_synced_window_delegates_getter.cc
index 9b45cc6..4b599e9 100644
--- a/components/sync_sessions/test_synced_window_delegates_getter.cc
+++ b/components/sync_sessions/test_synced_window_delegates_getter.cc
@@ -45,6 +45,7 @@
                                                                    entry.get());
 
   entries_.push_back(std::move(entry));
+  page_language_per_index_.push_back(std::string());
   set_current_entry_index(GetCurrentEntryIndex() + 1);
   notify_cb_.Run(this);
 }
@@ -62,6 +63,12 @@
   }
 }
 
+void TestSyncedTabDelegate::SetPageLanguageAtIndex(
+    int i,
+    const std::string& language) {
+  page_language_per_index_[i] = language;
+}
+
 bool TestSyncedTabDelegate::IsInitialBlankNavigation() const {
   // This differs from NavigationControllerImpl, which has an initial blank
   // NavigationEntry.
@@ -88,6 +95,11 @@
   return entries_[i]->transition_type();
 }
 
+std::string TestSyncedTabDelegate::GetPageLanguageAtIndex(int i) const {
+  DCHECK(static_cast<size_t>(i) < page_language_per_index_.size());
+  return page_language_per_index_[i];
+}
+
 void TestSyncedTabDelegate::GetSerializedNavigationAtIndex(
     int i,
     sessions::SerializedNavigationEntry* serialized_entry) const {
@@ -229,6 +241,11 @@
   return ui::PageTransition();
 }
 
+std::string PlaceholderTabDelegate::GetPageLanguageAtIndex(int i) const {
+  NOTREACHED();
+  return std::string();
+}
+
 void PlaceholderTabDelegate::GetSerializedNavigationAtIndex(
     int i,
     sessions::SerializedNavigationEntry* serialized_entry) const {
diff --git a/components/sync_sessions/test_synced_window_delegates_getter.h b/components/sync_sessions/test_synced_window_delegates_getter.h
index 7df5b203b..5e84190 100644
--- a/components/sync_sessions/test_synced_window_delegates_getter.h
+++ b/components/sync_sessions/test_synced_window_delegates_getter.h
@@ -39,12 +39,15 @@
       const std::vector<std::unique_ptr<sessions::SerializedNavigationEntry>>&
           navs);
 
+  void SetPageLanguageAtIndex(int i, const std::string& language);
+
   // SyncedTabDelegate overrides.
   bool IsInitialBlankNavigation() const override;
   int GetCurrentEntryIndex() const override;
   GURL GetVirtualURLAtIndex(int i) const override;
   GURL GetFaviconURLAtIndex(int i) const override;
   ui::PageTransition GetTransitionAtIndex(int i) const override;
+  std::string GetPageLanguageAtIndex(int i) const override;
   void GetSerializedNavigationAtIndex(
       int i,
       sessions::SerializedNavigationEntry* serialized_entry) const override;
@@ -75,6 +78,7 @@
       blocked_navigations_;
   std::vector<std::unique_ptr<const sessions::SerializedNavigationEntry>>
       entries_;
+  std::vector<std::string> page_language_per_index_;
 
   DISALLOW_COPY_AND_ASSIGN(TestSyncedTabDelegate);
 };
@@ -101,6 +105,7 @@
   GURL GetVirtualURLAtIndex(int i) const override;
   GURL GetFaviconURLAtIndex(int i) const override;
   ui::PageTransition GetTransitionAtIndex(int i) const override;
+  std::string GetPageLanguageAtIndex(int i) const override;
   void GetSerializedNavigationAtIndex(
       int i,
       sessions::SerializedNavigationEntry* serialized_entry) const override;
diff --git a/components/tracing/BUILD.gn b/components/tracing/BUILD.gn
index d73d2fd..b8d8cea 100644
--- a/components/tracing/BUILD.gn
+++ b/components/tracing/BUILD.gn
@@ -15,12 +15,10 @@
 
 component("tracing") {
   sources = [
-    "child/child_trace_message_filter.cc",
-    "child/child_trace_message_filter.h",
+    "child/background_tracing_agent_impl.cc",
+    "child/background_tracing_agent_impl.h",
     "common/graphics_memory_dump_provider_android.cc",
     "common/graphics_memory_dump_provider_android.h",
-    "common/tracing_messages.cc",
-    "common/tracing_messages.h",
     "common/tracing_sampler_profiler.cc",
     "common/tracing_sampler_profiler.h",
     "tracing_export.h",
@@ -30,6 +28,7 @@
 
   deps = [
     "//base",
+    "//components/tracing/common:interfaces",
     "//ipc",
   ]
 
@@ -70,7 +69,7 @@
   testonly = true
 
   sources = [
-    "child/child_trace_message_filter_unittest.cc",
+    "child/background_tracing_agent_impl_unittest.cc",
     "common/graphics_memory_dump_provider_android_unittest.cc",
     "common/tracing_sampler_profiler_unittest.cc",
   ]
@@ -79,6 +78,7 @@
     ":startup_tracing",
     ":tracing",
     "//base/test:test_support",
+    "//components/tracing/common:interfaces",
     "//ipc",
     "//testing/gmock:gmock",
     "//testing/gtest",
diff --git a/components/tracing/child/DEPS b/components/tracing/child/DEPS
new file mode 100644
index 0000000..0669117
--- /dev/null
+++ b/components/tracing/child/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+mojo",
+]
diff --git a/components/tracing/child/background_tracing_agent_impl.cc b/components/tracing/child/background_tracing_agent_impl.cc
new file mode 100644
index 0000000..3bffae0
--- /dev/null
+++ b/components/tracing/child/background_tracing_agent_impl.cc
@@ -0,0 +1,158 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/tracing/child/background_tracing_agent_impl.h"
+
+#include <memory>
+
+#include "base/metrics/statistics_recorder.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/trace_event.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+
+using base::trace_event::MemoryDumpManager;
+using base::trace_event::TraceLog;
+
+namespace tracing {
+
+namespace {
+
+constexpr base::TimeDelta kMinTimeBetweenHistogramChanges =
+    base::TimeDelta::FromSeconds(10);
+
+}  // namespace
+
+// static
+void BackgroundTracingAgentImpl::Create(
+    mojo::PendingReceiver<mojom::BackgroundTracingAgent> receiver) {
+  mojo::MakeSelfOwnedReceiver(std::make_unique<BackgroundTracingAgentImpl>(),
+                              std::move(receiver));
+}
+
+void BackgroundTracingAgentImpl::CreateFromRequest(
+    mojo::InterfaceRequest<mojom::BackgroundTracingAgent> request) {
+  Create(std::move(request));
+}
+
+BackgroundTracingAgentImpl::BackgroundTracingAgentImpl() = default;
+
+BackgroundTracingAgentImpl::~BackgroundTracingAgentImpl() = default;
+
+void BackgroundTracingAgentImpl::Initialize(
+    uint64_t tracing_process_id,
+    mojo::PendingRemote<mojom::BackgroundTracingAgentClient> client) {
+  MemoryDumpManager::GetInstance()->set_tracing_process_id(tracing_process_id);
+
+  client_.Bind(std::move(client));
+  client_->OnInitialized();
+}
+
+void BackgroundTracingAgentImpl::SetUMACallback(
+    const std::string& histogram_name,
+    int32_t histogram_lower_value,
+    int32_t histogram_upper_value,
+    bool repeat) {
+  histogram_last_changed_ = base::Time();
+
+  base::WeakPtr<BackgroundTracingAgentImpl> weak_self =
+      weak_factory_.GetWeakPtr();
+
+  // This callback will run on a random thread, so we need to proxy back to the
+  // current sequence before touching |this|.
+  base::StatisticsRecorder::SetCallback(
+      histogram_name,
+      base::BindRepeating(&BackgroundTracingAgentImpl::OnHistogramChanged,
+                          weak_self, base::SequencedTaskRunnerHandle::Get(),
+                          histogram_name, histogram_lower_value,
+                          histogram_upper_value, repeat));
+
+  base::HistogramBase* existing_histogram =
+      base::StatisticsRecorder::FindHistogram(histogram_name);
+  if (!existing_histogram)
+    return;
+
+  std::unique_ptr<base::HistogramSamples> samples =
+      existing_histogram->SnapshotSamples();
+  if (!samples)
+    return;
+
+  std::unique_ptr<base::SampleCountIterator> sample_iterator =
+      samples->Iterator();
+  if (!sample_iterator)
+    return;
+
+  while (!sample_iterator->Done()) {
+    base::HistogramBase::Sample min;
+    int64_t max;
+    base::HistogramBase::Count count;
+    sample_iterator->Get(&min, &max, &count);
+
+    if (min >= histogram_lower_value && max <= histogram_upper_value) {
+      SendTriggerMessage(histogram_name);
+      break;
+    }
+    if (!repeat) {
+      SendAbortBackgroundTracingMessage();
+      break;
+    }
+
+    sample_iterator->Next();
+  }
+}
+
+void BackgroundTracingAgentImpl::ClearUMACallback(
+    const std::string& histogram_name) {
+  histogram_last_changed_ = base::Time();
+  base::StatisticsRecorder::ClearCallback(histogram_name);
+}
+
+// static
+void BackgroundTracingAgentImpl::OnHistogramChanged(
+    base::WeakPtr<BackgroundTracingAgentImpl> weak_self,
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    const std::string& histogram_name,
+    base::Histogram::Sample histogram_lower_value,
+    base::Histogram::Sample histogram_upper_value,
+    bool repeat,
+    base::Histogram::Sample actual_value) {
+  // NOTE: This method is called from an arbitrary sequence.
+
+  if (actual_value < histogram_lower_value ||
+      actual_value > histogram_upper_value) {
+    if (!repeat) {
+      task_runner->PostTask(
+          FROM_HERE,
+          base::BindOnce(
+              &BackgroundTracingAgentImpl::SendAbortBackgroundTracingMessage,
+              weak_self));
+    }
+    return;
+  }
+
+  task_runner->PostTask(
+      FROM_HERE, base::BindOnce(&BackgroundTracingAgentImpl::SendTriggerMessage,
+                                weak_self, histogram_name));
+}
+
+void BackgroundTracingAgentImpl::SendTriggerMessage(
+    const std::string& histogram_name) {
+  base::Time now = TRACE_TIME_NOW();
+
+  if (!histogram_last_changed_.is_null()) {
+    base::Time computed_next_allowed_time =
+        histogram_last_changed_ + kMinTimeBetweenHistogramChanges;
+    if (computed_next_allowed_time > now)
+      return;
+  }
+  histogram_last_changed_ = now;
+
+  client_->OnTriggerBackgroundTrace(histogram_name);
+}
+
+void BackgroundTracingAgentImpl::SendAbortBackgroundTracingMessage() {
+  client_->OnAbortBackgroundTrace();
+}
+
+}  // namespace tracing
diff --git a/components/tracing/child/background_tracing_agent_impl.h b/components/tracing/child/background_tracing_agent_impl.h
new file mode 100644
index 0000000..500e378
--- /dev/null
+++ b/components/tracing/child/background_tracing_agent_impl.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_TRACING_CHILD_BACKGROUND_TRACING_AGENT_IMPL_H_
+#define COMPONENTS_TRACING_CHILD_BACKGROUND_TRACING_AGENT_IMPL_H_
+
+#include <stdint.h>
+#include <string>
+
+#include "base/macros.h"
+#include "base/metrics/histogram.h"
+#include "base/time/time.h"
+#include "components/tracing/common/background_tracing_agent.mojom.h"
+#include "components/tracing/tracing_export.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace tracing {
+
+// This class sends and receives trace messages on child processes.
+class TRACING_EXPORT BackgroundTracingAgentImpl
+    : public mojom::BackgroundTracingAgent {
+ public:
+  static void Create(
+      mojo::PendingReceiver<mojom::BackgroundTracingAgent> receiver);
+
+  // For backwards compat.
+  static void CreateFromRequest(
+      mojo::InterfaceRequest<mojom::BackgroundTracingAgent> request);
+
+  BackgroundTracingAgentImpl();
+  ~BackgroundTracingAgentImpl() override;
+
+  // mojom::BackgroundTracingAgent methods:
+  void Initialize(uint64_t tracing_process_id,
+                  mojo::PendingRemote<mojom::BackgroundTracingAgentClient>
+                      pending_client) override;
+  void SetUMACallback(const std::string& histogram_name,
+                      int32_t histogram_lower_value,
+                      int32_t histogram_upper_value,
+                      bool repeat) override;
+  void ClearUMACallback(const std::string& histogram_name) override;
+
+ private:
+  static void OnHistogramChanged(
+      base::WeakPtr<BackgroundTracingAgentImpl> weak_self,
+      scoped_refptr<base::SequencedTaskRunner> task_runner,
+      const std::string& histogram_name,
+      base::Histogram::Sample reference_lower_value,
+      base::Histogram::Sample reference_upper_value,
+      bool repeat,
+      base::Histogram::Sample actual_value);
+  void SendTriggerMessage(const std::string& histogram_name);
+  void SendAbortBackgroundTracingMessage();
+
+  mojo::Remote<mojom::BackgroundTracingAgentClient> client_;
+  base::Time histogram_last_changed_;
+
+  base::WeakPtrFactory<BackgroundTracingAgentImpl> weak_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(BackgroundTracingAgentImpl);
+};
+
+}  // namespace tracing
+
+#endif  // COMPONENTS_TRACING_CHILD_BACKGROUND_TRACING_AGENT_IMPL_H_
diff --git a/components/tracing/child/background_tracing_agent_impl_unittest.cc b/components/tracing/child/background_tracing_agent_impl_unittest.cc
new file mode 100644
index 0000000..743e789
--- /dev/null
+++ b/components/tracing/child/background_tracing_agent_impl_unittest.cc
@@ -0,0 +1,118 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/tracing/child/background_tracing_agent_impl.h"
+
+#include "base/run_loop.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "base/test/scoped_task_environment.h"
+#include "mojo/public/cpp/bindings/unique_receiver_set.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace tracing {
+
+class BackgroundTracingAgentClientRecorder
+    : public tracing::mojom::BackgroundTracingAgentClient {
+ public:
+  void OnInitialized() override { ++on_initialized_count_; }
+
+  void OnTriggerBackgroundTrace(const std::string& histogram_name) override {
+    ++on_trigger_background_trace_count_;
+    on_trigger_background_trace_histogram_name_ = histogram_name;
+  }
+
+  void OnAbortBackgroundTrace() override { ++on_abort_background_trace_count_; }
+
+  int on_initialized_count() const { return on_initialized_count_; }
+  int on_trigger_background_trace_count() const {
+    return on_trigger_background_trace_count_;
+  }
+  int on_abort_background_trace_count() const {
+    return on_abort_background_trace_count_;
+  }
+
+  const std::string& on_trigger_background_trace_histogram_name() const {
+    return on_trigger_background_trace_histogram_name_;
+  }
+
+ private:
+  int on_initialized_count_ = 0;
+  int on_trigger_background_trace_count_ = 0;
+  int on_abort_background_trace_count_ = 0;
+  std::string on_trigger_background_trace_histogram_name_;
+};
+
+class BackgroundTracingAgentImplTest : public testing::Test {
+ public:
+  BackgroundTracingAgentImplTest() {
+    agent_set_.Add(std::make_unique<tracing::BackgroundTracingAgentImpl>(),
+                   agent_.BindNewPipeAndPassReceiver());
+
+    auto recorder = std::make_unique<BackgroundTracingAgentClientRecorder>();
+    recorder_ = recorder.get();
+
+    mojo::PendingRemote<tracing::mojom::BackgroundTracingAgentClient> client;
+    client_set_.Add(std::move(recorder),
+                    client.InitWithNewPipeAndPassReceiver());
+
+    agent_->Initialize(0, std::move(client));
+  }
+
+  tracing::mojom::BackgroundTracingAgent* agent() { return agent_.get(); }
+
+  BackgroundTracingAgentClientRecorder* recorder() const { return recorder_; }
+
+ private:
+  base::test::ScopedTaskEnvironment task_environment_;
+  mojo::Remote<tracing::mojom::BackgroundTracingAgent> agent_;
+  mojo::UniqueReceiverSet<tracing::mojom::BackgroundTracingAgent> agent_set_;
+  mojo::UniqueReceiverSet<tracing::mojom::BackgroundTracingAgentClient>
+      client_set_;
+  BackgroundTracingAgentClientRecorder* recorder_ = nullptr;
+};
+
+TEST_F(BackgroundTracingAgentImplTest, TestInitialize) {
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, recorder()->on_initialized_count());
+}
+
+TEST_F(BackgroundTracingAgentImplTest, TestHistogramDoesNotTrigger) {
+  LOCAL_HISTOGRAM_COUNTS("foo1", 10);
+
+  agent()->SetUMACallback("foo1", 20000, 25000, true);
+
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(1, recorder()->on_initialized_count());
+  EXPECT_EQ(0, recorder()->on_trigger_background_trace_count());
+  EXPECT_EQ(0, recorder()->on_abort_background_trace_count());
+}
+
+TEST_F(BackgroundTracingAgentImplTest, TestHistogramTriggers) {
+  LOCAL_HISTOGRAM_COUNTS("foo2", 2);
+
+  agent()->SetUMACallback("foo2", 1, 3, true);
+
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(1, recorder()->on_initialized_count());
+  EXPECT_EQ(1, recorder()->on_trigger_background_trace_count());
+  EXPECT_EQ(0, recorder()->on_abort_background_trace_count());
+  EXPECT_EQ("foo2", recorder()->on_trigger_background_trace_histogram_name());
+}
+
+TEST_F(BackgroundTracingAgentImplTest, TestHistogramAborts) {
+  LOCAL_HISTOGRAM_COUNTS("foo3", 10);
+
+  agent()->SetUMACallback("foo3", 1, 3, false);
+
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(1, recorder()->on_initialized_count());
+  EXPECT_EQ(0, recorder()->on_trigger_background_trace_count());
+  EXPECT_EQ(1, recorder()->on_abort_background_trace_count());
+}
+
+}  // namespace tracing
diff --git a/components/tracing/child/child_trace_message_filter.cc b/components/tracing/child/child_trace_message_filter.cc
deleted file mode 100644
index 4e45bee1..0000000
--- a/components/tracing/child/child_trace_message_filter.cc
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/tracing/child/child_trace_message_filter.h"
-
-#include <memory>
-
-#include "base/metrics/statistics_recorder.h"
-#include "base/trace_event/memory_dump_manager.h"
-#include "base/trace_event/trace_event.h"
-#include "components/tracing/common/tracing_messages.h"
-#include "ipc/ipc_channel.h"
-
-using base::trace_event::MemoryDumpManager;
-using base::trace_event::TraceLog;
-
-namespace tracing {
-
-namespace {
-
-const int kMinTimeBetweenHistogramChangesInSeconds = 10;
-
-}  // namespace
-
-ChildTraceMessageFilter::ChildTraceMessageFilter(
-    base::SingleThreadTaskRunner* ipc_task_runner)
-    : enabled_tracing_modes_(0),
-      sender_(nullptr),
-      ipc_task_runner_(ipc_task_runner) {}
-
-void ChildTraceMessageFilter::OnFilterAdded(IPC::Channel* channel) {
-  sender_ = channel;
-  sender_->Send(new TracingHostMsg_ChildSupportsTracing());
-}
-
-void ChildTraceMessageFilter::SetSenderForTesting(IPC::Sender* sender) {
-  sender_ = sender;
-}
-
-void ChildTraceMessageFilter::OnFilterRemoved() {
-  sender_ = nullptr;
-}
-
-bool ChildTraceMessageFilter::OnMessageReceived(const IPC::Message& message) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(ChildTraceMessageFilter, message)
-    IPC_MESSAGE_HANDLER(TracingMsg_SetTracingProcessId, OnSetTracingProcessId)
-    IPC_MESSAGE_HANDLER(TracingMsg_SetUMACallback, OnSetUMACallback)
-    IPC_MESSAGE_HANDLER(TracingMsg_ClearUMACallback, OnClearUMACallback)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
-
-ChildTraceMessageFilter::~ChildTraceMessageFilter() {}
-
-void ChildTraceMessageFilter::OnSetTracingProcessId(
-    uint64_t tracing_process_id) {
-  MemoryDumpManager::GetInstance()->set_tracing_process_id(tracing_process_id);
-}
-
-void ChildTraceMessageFilter::OnHistogramChanged(
-    const std::string& histogram_name,
-    base::Histogram::Sample reference_lower_value,
-    base::Histogram::Sample reference_upper_value,
-    bool repeat,
-    base::Histogram::Sample actual_value) {
-  if (actual_value < reference_lower_value ||
-      actual_value > reference_upper_value) {
-    if (!repeat) {
-      ipc_task_runner_->PostTask(
-          FROM_HERE,
-          base::BindOnce(
-              &ChildTraceMessageFilter::SendAbortBackgroundTracingMessage,
-              this));
-    }
-    return;
-  }
-
-  ipc_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&ChildTraceMessageFilter::SendTriggerMessage,
-                                this, histogram_name));
-}
-
-void ChildTraceMessageFilter::SendTriggerMessage(
-    const std::string& histogram_name) {
-  if (!histogram_last_changed_.is_null()) {
-    base::Time computed_next_allowed_time =
-        histogram_last_changed_ +
-        base::TimeDelta::FromSeconds(kMinTimeBetweenHistogramChangesInSeconds);
-    if (computed_next_allowed_time > TRACE_TIME_NOW())
-      return;
-  }
-  histogram_last_changed_ = TRACE_TIME_NOW();
-
-  if (sender_)
-    sender_->Send(new TracingHostMsg_TriggerBackgroundTrace(histogram_name));
-}
-
-void ChildTraceMessageFilter::SendAbortBackgroundTracingMessage() {
-  if (sender_)
-    sender_->Send(new TracingHostMsg_AbortBackgroundTrace());
-}
-
-void ChildTraceMessageFilter::OnSetUMACallback(
-    const std::string& histogram_name,
-    int histogram_lower_value,
-    int histogram_upper_value,
-    bool repeat) {
-  histogram_last_changed_ = base::Time();
-  base::StatisticsRecorder::SetCallback(
-      histogram_name, base::Bind(&ChildTraceMessageFilter::OnHistogramChanged,
-                                 this, histogram_name, histogram_lower_value,
-                                 histogram_upper_value, repeat));
-
-  base::HistogramBase* existing_histogram =
-      base::StatisticsRecorder::FindHistogram(histogram_name);
-  if (!existing_histogram)
-    return;
-
-  std::unique_ptr<base::HistogramSamples> samples =
-      existing_histogram->SnapshotSamples();
-  if (!samples)
-    return;
-
-  std::unique_ptr<base::SampleCountIterator> sample_iterator =
-      samples->Iterator();
-  if (!sample_iterator)
-    return;
-
-  while (!sample_iterator->Done()) {
-    base::HistogramBase::Sample min;
-    int64_t max;
-    base::HistogramBase::Count count;
-    sample_iterator->Get(&min, &max, &count);
-
-    if (min >= histogram_lower_value && max <= histogram_upper_value) {
-      ipc_task_runner_->PostTask(
-          FROM_HERE,
-          base::BindOnce(&ChildTraceMessageFilter::SendTriggerMessage, this,
-                         histogram_name));
-      break;
-    } else if (!repeat) {
-      ipc_task_runner_->PostTask(
-          FROM_HERE,
-          base::BindOnce(
-              &ChildTraceMessageFilter::SendAbortBackgroundTracingMessage,
-              this));
-      break;
-    }
-
-    sample_iterator->Next();
-  }
-}
-
-void ChildTraceMessageFilter::OnClearUMACallback(
-    const std::string& histogram_name) {
-  histogram_last_changed_ = base::Time();
-  base::StatisticsRecorder::ClearCallback(histogram_name);
-}
-
-}  // namespace tracing
diff --git a/components/tracing/child/child_trace_message_filter.h b/components/tracing/child/child_trace_message_filter.h
deleted file mode 100644
index a3e9b2a..0000000
--- a/components/tracing/child/child_trace_message_filter.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_TRACING_CHILD_CHILD_TRACE_MESSAGE_FILTER_H_
-#define COMPONENTS_TRACING_CHILD_CHILD_TRACE_MESSAGE_FILTER_H_
-
-#include <stdint.h>
-#include <string>
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "base/metrics/histogram.h"
-#include "base/time/time.h"
-#include "components/tracing/tracing_export.h"
-#include "ipc/message_filter.h"
-
-namespace base {
-class SingleThreadTaskRunner;
-}
-
-namespace tracing {
-
-// This class sends and receives trace messages on child processes.
-class TRACING_EXPORT ChildTraceMessageFilter : public IPC::MessageFilter {
- public:
-  explicit ChildTraceMessageFilter(
-      base::SingleThreadTaskRunner* ipc_task_runner);
-
-  // IPC::MessageFilter implementation.
-  void OnFilterAdded(IPC::Channel* channel) override;
-  void OnFilterRemoved() override;
-  bool OnMessageReceived(const IPC::Message& message) override;
-
-  base::SingleThreadTaskRunner* ipc_task_runner() const {
-    return ipc_task_runner_;
-  }
-
- protected:
-  ~ChildTraceMessageFilter() override;
-
- private:
-  friend class ChildTraceMessageFilterTest;
-
-  // Message handlers.
-  void OnSetTracingProcessId(uint64_t tracing_process_id);
-  void OnSetWatchEvent(const std::string& category_name,
-                       const std::string& event_name);
-  void OnCancelWatchEvent();
-  void OnWatchEventMatched();
-  void OnSetUMACallback(const std::string& histogram_name,
-                        int histogram_lower_value,
-                        int histogram_upper_value,
-                        bool repeat);
-  void OnClearUMACallback(const std::string& histogram_name);
-  void OnHistogramChanged(const std::string& histogram_name,
-                          base::Histogram::Sample reference_lower_value,
-                          base::Histogram::Sample reference_upper_value,
-                          bool repeat,
-                          base::Histogram::Sample actual_value);
-  void SendTriggerMessage(const std::string& histogram_name);
-  void SendAbortBackgroundTracingMessage();
-
-  void SetSenderForTesting(IPC::Sender* sender);
-
-  uint8_t enabled_tracing_modes_;
-
-  IPC::Sender* sender_;
-  base::SingleThreadTaskRunner* ipc_task_runner_;
-
-  base::Time histogram_last_changed_;
-
-  DISALLOW_COPY_AND_ASSIGN(ChildTraceMessageFilter);
-};
-
-}  // namespace tracing
-
-#endif  // COMPONENTS_TRACING_CHILD_CHILD_TRACE_MESSAGE_FILTER_H_
diff --git a/components/tracing/child/child_trace_message_filter_unittest.cc b/components/tracing/child/child_trace_message_filter_unittest.cc
deleted file mode 100644
index f9a64049..0000000
--- a/components/tracing/child/child_trace_message_filter_unittest.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/tracing/child/child_trace_message_filter.h"
-
-#include <memory>
-#include "base/run_loop.h"
-
-#include "base/memory/ref_counted.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/tracing/common/tracing_messages.h"
-#include "ipc/ipc_message.h"
-#include "ipc/ipc_sender.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace tracing {
-
-class FakeSender : public IPC::Sender {
- public:
-  FakeSender() {}
-
-  ~FakeSender() override {}
-
-  bool Send(IPC::Message* msg) override {
-    last_message_.reset(msg);
-    return true;
-  }
-
-  std::unique_ptr<IPC::Message> last_message_;
-};
-
-class ChildTraceMessageFilterTest : public testing::Test {
- public:
-  ChildTraceMessageFilterTest() {
-    message_filter_ = new tracing::ChildTraceMessageFilter(
-        base::ThreadTaskRunnerHandle::Get().get());
-    message_filter_->SetSenderForTesting(&fake_sender_);
-  }
-
-  void OnSetUMACallback(const std::string& histogram,
-                        int low,
-                        int high,
-                        bool repeat) {
-    fake_sender_.last_message_.reset();
-    message_filter_->OnSetUMACallback(histogram, low, high, repeat);
-  }
-
-  base::test::ScopedTaskEnvironment task_environment_;
-  FakeSender fake_sender_;
-  scoped_refptr<tracing::ChildTraceMessageFilter> message_filter_;
-};
-
-TEST_F(ChildTraceMessageFilterTest, TestHistogramDoesNotTrigger) {
-  LOCAL_HISTOGRAM_COUNTS("foo1", 10);
-
-  OnSetUMACallback("foo1", 20000, 25000, true);
-
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_FALSE(fake_sender_.last_message_);
-}
-
-TEST_F(ChildTraceMessageFilterTest, TestHistogramTriggers) {
-  LOCAL_HISTOGRAM_COUNTS("foo2", 2);
-
-  OnSetUMACallback("foo2", 1, 3, true);
-
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_TRUE(fake_sender_.last_message_);
-  EXPECT_EQ(fake_sender_.last_message_->type(),
-            static_cast<uint32_t>(TracingHostMsg_TriggerBackgroundTrace::ID));
-}
-
-TEST_F(ChildTraceMessageFilterTest, TestHistogramAborts) {
-  LOCAL_HISTOGRAM_COUNTS("foo3", 10);
-
-  OnSetUMACallback("foo3", 1, 3, false);
-
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_TRUE(fake_sender_.last_message_);
-  EXPECT_EQ(fake_sender_.last_message_->type(),
-            static_cast<uint32_t>(TracingHostMsg_AbortBackgroundTrace::ID));
-}
-
-}  // namespace tracing
diff --git a/components/tracing/common/BUILD.gn b/components/tracing/common/BUILD.gn
new file mode 100644
index 0000000..61d1403
--- /dev/null
+++ b/components/tracing/common/BUILD.gn
@@ -0,0 +1,13 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/features.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+  sources = [
+    "background_tracing_agent.mojom",
+  ]
+  cpp_only = true
+}
diff --git a/components/tracing/common/OWNERS b/components/tracing/common/OWNERS
index cbf806a..08850f4 100644
--- a/components/tracing/common/OWNERS
+++ b/components/tracing/common/OWNERS
@@ -1,5 +1,2 @@
-per-file *_messages*.h=set noparent
-per-file *_messages*.h=file://ipc/SECURITY_OWNERS
-
-per-file *_messages.cc=set noparent
-per-file *_messages.cc=file://ipc/SECURITY_OWNERS
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/components/tracing/common/background_tracing_agent.mojom b/components/tracing/common/background_tracing_agent.mojom
new file mode 100644
index 0000000..9f502c8
--- /dev/null
+++ b/components/tracing/common/background_tracing_agent.mojom
@@ -0,0 +1,35 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module tracing.mojom;
+
+// This interface is used to receive callbacks from BackgroundTracingAgent.
+interface BackgroundTracingAgentClient {
+  OnInitialized();
+  OnTriggerBackgroundTrace(string histogram_name);
+  OnAbortBackgroundTrace();
+};
+
+// This interface is used to allow clients (the browser process) to monitor for
+// specific metrics being hit and control when to start/stop tracing in
+// response. How metrics are communicated b/w processes is not covered here.
+interface BackgroundTracingAgent {
+  // Call this method first. Results in an OnInitialized callback.
+  Initialize(uint64 tracing_process_id,
+             pending_remote<BackgroundTracingAgentClient> client);
+
+  // Call this method to begin reporting metrics corresponding to the named
+  // histogram. Lower and upper bound values constrain what data is reported.
+  // This results in OnTriggerBackgroundTrace callbacks (multiple if |repeat|
+  // is set to true). If a histogram event does not match and |repeat| is
+  // false, then OnAbortBackgroundTrace is called.
+  SetUMACallback(string histogram_name,
+                 int32 histogram_lower_value,
+                 int32 histogram_upper_value,
+                 bool repeat);
+
+  // Call this method to stop reporting metrics corresponding to the named
+  // histogram.
+  ClearUMACallback(string histogram_name);
+};
diff --git a/components/tracing/common/tracing_messages.cc b/components/tracing/common/tracing_messages.cc
deleted file mode 100644
index 5593d8a..0000000
--- a/components/tracing/common/tracing_messages.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2010 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.
-
-// Get basic type definitions.
-#define IPC_MESSAGE_IMPL
-#undef COMPONENTS_TRACING_COMMON_TRACING_MESSAGES_H_
-#include "components/tracing/common/tracing_messages.h"
-#ifndef COMPONENTS_TRACING_COMMON_TRACING_MESSAGES_H_
-#error "Failed to include components/tracing/common/tracing_messages.h"
-#endif
-
-// Generate constructors.
-#include "ipc/struct_constructor_macros.h"
-#undef COMPONENTS_TRACING_COMMON_TRACING_MESSAGES_H_
-#include "components/tracing/common/tracing_messages.h"
-#ifndef COMPONENTS_TRACING_COMMON_TRACING_MESSAGES_H_
-#error "Failed to include components/tracing/common/tracing_messages.h"
-#endif
-
-// Generate param traits write methods.
-#include "ipc/param_traits_write_macros.h"
-namespace IPC {
-#undef COMPONENTS_TRACING_COMMON_TRACING_MESSAGES_H_
-#include "components/tracing/common/tracing_messages.h"
-#ifndef COMPONENTS_TRACING_COMMON_TRACING_MESSAGES_H_
-#error "Failed to include components/tracing/common/tracing_messages.h"
-#endif
-}  // namespace IPC
-
-// Generate param traits read methods.
-#include "ipc/param_traits_read_macros.h"
-namespace IPC {
-#undef COMPONENTS_TRACING_COMMON_TRACING_MESSAGES_H_
-#include "components/tracing/common/tracing_messages.h"
-#ifndef COMPONENTS_TRACING_COMMON_TRACING_MESSAGES_H_
-#error "Failed to include components/tracing/common/tracing_messages.h"
-#endif
-}  // namespace IPC
-
-// Generate param traits log methods.
-#include "ipc/param_traits_log_macros.h"
-namespace IPC {
-#undef COMPONENTS_TRACING_COMMON_TRACING_MESSAGES_H_
-#include "components/tracing/common/tracing_messages.h"
-#ifndef COMPONENTS_TRACING_COMMON_TRACING_MESSAGES_H_
-#error "Failed to include components/tracing/common/tracing_messages.h"
-#endif
-}  // namespace IPC
diff --git a/components/tracing/common/tracing_messages.h b/components/tracing/common/tracing_messages.h
deleted file mode 100644
index 553418a..0000000
--- a/components/tracing/common/tracing_messages.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_TRACING_COMMON_TRACING_MESSAGES_H_
-#define COMPONENTS_TRACING_COMMON_TRACING_MESSAGES_H_
-
-#include <stdint.h>
-
-#include <string>
-
-#include "base/metrics/histogram.h"
-#include "base/sync_socket.h"
-#include "components/tracing/tracing_export.h"
-#include "ipc/ipc_channel_handle.h"
-#include "ipc/ipc_message_macros.h"
-#include "ipc/ipc_message_utils.h"
-#include "ipc/ipc_platform_file.h"
-
-#undef IPC_MESSAGE_EXPORT
-#define IPC_MESSAGE_EXPORT TRACING_EXPORT
-#define IPC_MESSAGE_START TracingMsgStart
-
-// Sent to all child processes to set tracing process id.
-IPC_MESSAGE_CONTROL1(TracingMsg_SetTracingProcessId,
-                     uint64_t /* Tracing process id (hash of child id) */)
-
-IPC_MESSAGE_CONTROL4(TracingMsg_SetUMACallback,
-                     std::string /* histogram_name */,
-                     base::HistogramBase::Sample /* histogram_lower_value */,
-                     base::HistogramBase::Sample /* histogram_uppwer_value */,
-                     bool /* repeat */)
-
-IPC_MESSAGE_CONTROL1(TracingMsg_ClearUMACallback,
-                     std::string /* histogram_name */)
-
-// Notify the browser that this child process supports tracing.
-IPC_MESSAGE_CONTROL0(TracingHostMsg_ChildSupportsTracing)
-
-IPC_MESSAGE_CONTROL1(TracingHostMsg_TriggerBackgroundTrace,
-                     std::string /* name */)
-
-IPC_MESSAGE_CONTROL0(TracingHostMsg_AbortBackgroundTrace)
-
-#endif  // COMPONENTS_TRACING_COMMON_TRACING_MESSAGES_H_
diff --git a/components/translate/content/browser/BUILD.gn b/components/translate/content/browser/BUILD.gn
index df077d0..8b35e2d 100644
--- a/components/translate/content/browser/BUILD.gn
+++ b/components/translate/content/browser/BUILD.gn
@@ -6,6 +6,8 @@
 
 static_library("browser") {
   sources = [
+    "content_record_page_language.cc",
+    "content_record_page_language.h",
     "content_translate_driver.cc",
     "content_translate_driver.h",
   ]
diff --git a/components/translate/content/browser/content_record_page_language.cc b/components/translate/content/browser/content_record_page_language.cc
new file mode 100644
index 0000000..3af62de
--- /dev/null
+++ b/components/translate/content/browser/content_record_page_language.cc
@@ -0,0 +1,35 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/translate/content/browser/content_record_page_language.h"
+
+#include "content/public/browser/navigation_entry.h"
+
+namespace translate {
+
+namespace {
+// The key used to store page language in the NavigationEntry;
+const char kPageLanguageKey[] = "page_language";
+
+struct LanguageDetectionData : public base::SupportsUserData::Data {
+  // The adopted page language. An ISO 639 language code (two letters, except
+  // for Chinese where a localization is necessary).
+  std::string page_language;
+};
+}  // namespace
+
+std::string GetPageLanguageFromNavigation(content::NavigationEntry* entry) {
+  auto* data =
+      static_cast<LanguageDetectionData*>(entry->GetUserData(kPageLanguageKey));
+  return data ? data->page_language : "";
+}
+
+void SetPageLanguageInNavigation(const std::string& page_language,
+                                 content::NavigationEntry* entry) {
+  auto data = std::make_unique<LanguageDetectionData>();
+  data->page_language = page_language;
+  entry->SetUserData(kPageLanguageKey, std::move(data));
+}
+
+}  // namespace translate
diff --git a/components/translate/content/browser/content_record_page_language.h b/components/translate/content/browser/content_record_page_language.h
new file mode 100644
index 0000000..2983993
--- /dev/null
+++ b/components/translate/content/browser/content_record_page_language.h
@@ -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.
+
+#ifndef COMPONENTS_TRANSLATE_CONTENT_BROWSER_CONTENT_RECORD_PAGE_LANGUAGE_H_
+#define COMPONENTS_TRANSLATE_CONTENT_BROWSER_CONTENT_RECORD_PAGE_LANGUAGE_H_
+
+#include <string>
+
+namespace content {
+class NavigationEntry;
+}
+
+namespace translate {
+
+// Helper functions for storing/getting page language in a NavigationEntry.
+std::string GetPageLanguageFromNavigation(content::NavigationEntry* entry);
+
+void SetPageLanguageInNavigation(const std::string& page_language,
+                                 content::NavigationEntry* entry);
+
+}  // namespace translate
+
+#endif  // COMPONENTS_TRANSLATE_CONTENT_BROWSER_CONTENT_RECORD_PAGE_LANGUAGE_H_
diff --git a/components/translate/content/browser/content_translate_driver.cc b/components/translate/content/browser/content_translate_driver.cc
index 70f6833..60f54353 100644
--- a/components/translate/content/browser/content_translate_driver.cc
+++ b/components/translate/content/browser/content_translate_driver.cc
@@ -17,6 +17,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/google/core/common/google_util.h"
 #include "components/language/core/browser/url_language_histogram.h"
+#include "components/translate/content/browser/content_record_page_language.h"
 #include "components/translate/core/browser/translate_download_manager.h"
 #include "components/translate/core/browser/translate_manager.h"
 #include "components/translate/core/common/translate_util.h"
@@ -44,15 +45,6 @@
 // loading before giving up the translation
 const int kMaxTranslateLoadCheckAttempts = 20;
 
-// The key used to store page language in the NavigationEntry;
-const char kPageLanguageKey[] = "page_language";
-
-struct LanguageDectionData : public base::SupportsUserData::Data {
-  // The adopted language. An ISO 639 language code (two letters, except for
-  // Chinese where a localization is necessary).
-  std::string adopted_language;
-};
-
 }  // namespace
 
 ContentTranslateDriver::ContentTranslateDriver(
@@ -311,11 +303,8 @@
 
     // Save the page language on the navigation entry so it can be synced.
     auto* const entry = web_contents()->GetController().GetLastCommittedEntry();
-    if (entry != nullptr) {
-      auto data = std::make_unique<LanguageDectionData>();
-      data->adopted_language = details.adopted_language;
-      entry->SetUserData(kPageLanguageKey, std::move(data));
-    }
+    if (entry != nullptr)
+      SetPageLanguageInNavigation(details.adopted_language, entry);
   }
 
   for (auto& observer : observer_list_)
diff --git a/components/undo/bookmark_undo_service.cc b/components/undo/bookmark_undo_service.cc
index e4b31ca..e24a856 100644
--- a/components/undo/bookmark_undo_service.cc
+++ b/components/undo/bookmark_undo_service.cc
@@ -49,7 +49,7 @@
  public:
   BookmarkAddOperation(BookmarkModel* bookmark_model,
                        const BookmarkNode* parent,
-                       int index);
+                       size_t index);
   ~BookmarkAddOperation() override {}
 
   // UndoOperation:
@@ -59,19 +59,17 @@
 
  private:
   int64_t parent_id_;
-  const int index_;
+  const size_t index_;
 
   DISALLOW_COPY_AND_ASSIGN(BookmarkAddOperation);
 };
 
-BookmarkAddOperation::BookmarkAddOperation(
-    BookmarkModel* bookmark_model,
-    const BookmarkNode* parent,
-    int index)
+BookmarkAddOperation::BookmarkAddOperation(BookmarkModel* bookmark_model,
+                                           const BookmarkNode* parent,
+                                           size_t index)
     : BookmarkUndoOperation(bookmark_model),
       parent_id_(parent->id()),
-      index_(index) {
-}
+      index_(index) {}
 
 void BookmarkAddOperation::Undo() {
   BookmarkModel* model = bookmark_model();
@@ -79,7 +77,7 @@
       bookmarks::GetBookmarkNodeByID(model, parent_id_);
   DCHECK(parent);
 
-  model->Remove(parent->GetChild(index_));
+  model->Remove(parent->children()[index_].get());
 }
 
 int BookmarkAddOperation::GetUndoLabelId() const {
@@ -101,7 +99,7 @@
   BookmarkRemoveOperation(BookmarkModel* model,
                           BookmarkUndoProvider* undo_provider,
                           const BookmarkNode* parent,
-                          int index,
+                          size_t index,
                           std::unique_ptr<BookmarkNode> node);
   ~BookmarkRemoveOperation() override;
 
@@ -113,7 +111,7 @@
  private:
   BookmarkUndoProvider* undo_provider_;
   const int64_t parent_node_id_;
-  const int index_;
+  const size_t index_;
   std::unique_ptr<BookmarkNode> node_;
 
   DISALLOW_COPY_AND_ASSIGN(BookmarkRemoveOperation);
@@ -123,7 +121,7 @@
     BookmarkModel* model,
     BookmarkUndoProvider* undo_provider,
     const BookmarkNode* parent,
-    int index,
+    size_t index,
     std::unique_ptr<BookmarkNode> node)
     : BookmarkUndoOperation(model),
       undo_provider_(undo_provider),
@@ -207,9 +205,9 @@
  public:
   BookmarkMoveOperation(BookmarkModel* bookmark_model,
                         const BookmarkNode* old_parent,
-                        int old_index,
+                        size_t old_index,
                         const BookmarkNode* new_parent,
-                        int new_index);
+                        size_t new_index);
   ~BookmarkMoveOperation() override {}
   int GetUndoLabelId() const override;
   int GetRedoLabelId() const override;
@@ -220,24 +218,22 @@
  private:
   int64_t old_parent_id_;
   int64_t new_parent_id_;
-  int old_index_;
-  int new_index_;
+  size_t old_index_;
+  size_t new_index_;
 
   DISALLOW_COPY_AND_ASSIGN(BookmarkMoveOperation);
 };
 
-BookmarkMoveOperation::BookmarkMoveOperation(
-    BookmarkModel* bookmark_model,
-    const BookmarkNode* old_parent,
-    int old_index,
-    const BookmarkNode* new_parent,
-    int new_index)
+BookmarkMoveOperation::BookmarkMoveOperation(BookmarkModel* bookmark_model,
+                                             const BookmarkNode* old_parent,
+                                             size_t old_index,
+                                             const BookmarkNode* new_parent,
+                                             size_t new_index)
     : BookmarkUndoOperation(bookmark_model),
       old_parent_id_(old_parent->id()),
       new_parent_id_(new_parent->id()),
       old_index_(old_index),
-      new_index_(new_index) {
-}
+      new_index_(new_index) {}
 
 void BookmarkMoveOperation::Undo() {
   BookmarkModel* model = bookmark_model();
@@ -248,8 +244,8 @@
   DCHECK(old_parent);
   DCHECK(new_parent);
 
-  const BookmarkNode* node = new_parent->GetChild(new_index_);
-  int destination_index = old_index_;
+  const BookmarkNode* node = new_parent->children()[new_index_].get();
+  size_t destination_index = old_index_;
 
   // If the bookmark was moved up within the same parent then the destination
   // index needs to be incremented since the old index did not account for the
@@ -363,9 +359,9 @@
 
 void BookmarkUndoService::BookmarkNodeMoved(BookmarkModel* model,
                                             const BookmarkNode* old_parent,
-                                            int old_index,
+                                            size_t old_index,
                                             const BookmarkNode* new_parent,
-                                            int new_index) {
+                                            size_t new_index) {
   std::unique_ptr<UndoOperation> op(new BookmarkMoveOperation(
       model, old_parent, old_index, new_parent, new_index));
   undo_manager()->AddUndoOperation(std::move(op));
@@ -373,7 +369,7 @@
 
 void BookmarkUndoService::BookmarkNodeAdded(BookmarkModel* model,
                                             const BookmarkNode* parent,
-                                            int index) {
+                                            size_t index) {
   std::unique_ptr<UndoOperation> op(
       new BookmarkAddOperation(model, parent, index));
   undo_manager()->AddUndoOperation(std::move(op));
@@ -407,7 +403,7 @@
 void BookmarkUndoService::OnBookmarkNodeRemoved(
     BookmarkModel* model,
     const BookmarkNode* parent,
-    int index,
+    size_t index,
     std::unique_ptr<BookmarkNode> node) {
   DCHECK(undo_provider_);
   std::unique_ptr<UndoOperation> op(new BookmarkRemoveOperation(
diff --git a/components/undo/bookmark_undo_service.h b/components/undo/bookmark_undo_service.h
index eb7ae5bf..2604efd 100644
--- a/components/undo/bookmark_undo_service.h
+++ b/components/undo/bookmark_undo_service.h
@@ -49,12 +49,12 @@
   void BookmarkModelBeingDeleted(bookmarks::BookmarkModel* model) override;
   void BookmarkNodeMoved(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* old_parent,
-                         int old_index,
+                         size_t old_index,
                          const bookmarks::BookmarkNode* new_parent,
-                         int new_index) override;
+                         size_t new_index) override;
   void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* parent,
-                         int index) override;
+                         size_t index) override;
   void OnWillChangeBookmarkNode(bookmarks::BookmarkModel* model,
                                 const bookmarks::BookmarkNode* node) override;
   void OnWillReorderBookmarkNode(bookmarks::BookmarkModel* model,
@@ -68,7 +68,7 @@
   void OnBookmarkNodeRemoved(
       bookmarks::BookmarkModel* model,
       const bookmarks::BookmarkNode* parent,
-      int index,
+      size_t index,
       std::unique_ptr<bookmarks::BookmarkNode> node) override;
 
   bookmarks::BookmarkModel* model_;
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index f17983a5..39874b7 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -487,13 +487,13 @@
       "//gpu/vulkan:test_support",
       "//gpu/vulkan/init",
     ]
-  }
 
-  # TODO(samans): Support more configurations.
-  # CFI issue: https://crbug.com/967819
-  # x86 issue: https://crbug.com/967831
-  if (use_x11 && target_cpu != "x86" && !is_cfi) {
-    defines = [ "ENABLE_VIZ_VULKAN_TESTS" ]
+    # TODO(samans): Support more configurations.
+    # CFI issue: https://crbug.com/967819
+    # x86 issue: https://crbug.com/967831
+    if (use_x11 && target_cpu != "x86" && !is_cfi) {
+      defines = [ "ENABLE_VIZ_VULKAN_TESTS" ]
+    }
   }
 }
 
diff --git a/components/viz/test/test_gpu_service_holder.cc b/components/viz/test/test_gpu_service_holder.cc
index d1b0de9..eaf0a28 100644
--- a/components/viz/test/test_gpu_service_holder.cc
+++ b/components/viz/test/test_gpu_service_holder.cc
@@ -51,11 +51,7 @@
   ~InstanceResetter() override = default;
 
   void OnTestEnd(const testing::TestInfo& test_info) override {
-    base::AutoLock locked(GetLock());
-    if (g_holder) {
-      delete g_holder;
-      g_holder = nullptr;
-    }
+    TestGpuServiceHolder::ResetInstance();
   }
 
  private:
@@ -88,6 +84,15 @@
 }
 
 // static
+void TestGpuServiceHolder::ResetInstance() {
+  base::AutoLock locked(GetLock());
+  if (g_holder) {
+    delete g_holder;
+    g_holder = nullptr;
+  }
+}
+
+// static
 void TestGpuServiceHolder::DestroyInstanceAfterEachTest() {
   static bool registered_listener = false;
   if (!registered_listener) {
diff --git a/components/viz/test/test_gpu_service_holder.h b/components/viz/test/test_gpu_service_holder.h
index 6ec7b043..b577013 100644
--- a/components/viz/test/test_gpu_service_holder.h
+++ b/components/viz/test/test_gpu_service_holder.h
@@ -44,6 +44,10 @@
   // a separate instance of this class can be created.
   static TestGpuServiceHolder* GetInstance();
 
+  // Resets the singleton instance, joining the GL thread. This is useful for
+  // tests that individually initialize and tear down GL.
+  static void ResetInstance();
+
   // Calling this method ensures that GetInstance() is destroyed after each
   // gtest completes -- it only applies to gtest because it uses gtest hooks. A
   // subsequent call to GetInstance() will create a new instance. Safe to call
diff --git a/components/webdata_services/BUILD.gn b/components/webdata_services/BUILD.gn
index 6efcf74..0544db7 100644
--- a/components/webdata_services/BUILD.gn
+++ b/components/webdata_services/BUILD.gn
@@ -27,18 +27,3 @@
     deps += [ "//components/payments/content:utils" ]
   }
 }
-
-static_library("test_support") {
-  testonly = true
-  sources = [
-    "web_data_service_test_util.cc",
-    "web_data_service_test_util.h",
-  ]
-
-  deps = [
-    ":webdata_services",
-    "//base",
-    "//components/autofill/core/browser",
-    "//components/signin/core/browser",
-  ]
-}
diff --git a/components/webdata_services/web_data_service_test_util.cc b/components/webdata_services/web_data_service_test_util.cc
deleted file mode 100644
index 6cd460f..0000000
--- a/components/webdata_services/web_data_service_test_util.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/webdata_services/web_data_service_test_util.h"
-
-#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
-
-using autofill::AutofillWebDataService;
-
-MockWebDataServiceWrapperBase::MockWebDataServiceWrapperBase() {
-}
-
-MockWebDataServiceWrapperBase::~MockWebDataServiceWrapperBase() {
-}
-
-void MockWebDataServiceWrapperBase::Shutdown() {
-}
-
-// TODO(caitkp): This won't scale well. As we get more WebData subclasses, we
-// will probably need a better way to create these mocks rather than passing
-// all the webdatas in.
-MockWebDataServiceWrapper::MockWebDataServiceWrapper(
-    scoped_refptr<AutofillWebDataService> fake_autofill,
-    scoped_refptr<TokenWebData> fake_token)
-    : fake_autofill_web_data_(fake_autofill), fake_token_web_data_(fake_token) {
-}
-
-MockWebDataServiceWrapper::~MockWebDataServiceWrapper() {
-}
-
-scoped_refptr<AutofillWebDataService>
-MockWebDataServiceWrapper::GetProfileAutofillWebData() {
-  return fake_autofill_web_data_;
-}
-
-scoped_refptr<AutofillWebDataService>
-MockWebDataServiceWrapper::GetAccountAutofillWebData() {
-  // TODO(feuunk): Implement when there are tests covering account data.
-  return nullptr;
-}
-
-scoped_refptr<TokenWebData> MockWebDataServiceWrapper::GetTokenWebData() {
-  return fake_token_web_data_;
-}
diff --git a/components/webdata_services/web_data_service_test_util.h b/components/webdata_services/web_data_service_test_util.h
deleted file mode 100644
index 9f5f6d4..0000000
--- a/components/webdata_services/web_data_service_test_util.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) 2011 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_WEBDATA_SERVICES_WEB_DATA_SERVICE_TEST_UTIL_H__
-#define COMPONENTS_WEBDATA_SERVICES_WEB_DATA_SERVICE_TEST_UTIL_H__
-
-#include "base/macros.h"
-#include "components/signin/core/browser/webdata/token_web_data.h"
-#include "components/webdata_services/web_data_service_wrapper.h"
-
-// Base class for mocks of WebDataService, that does nothing in
-// Shutdown().
-class MockWebDataServiceWrapperBase : public WebDataServiceWrapper {
- public:
-  MockWebDataServiceWrapperBase();
-  ~MockWebDataServiceWrapperBase() override;
-
-  void Shutdown() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockWebDataServiceWrapperBase);
-};
-
-// Pass your fake WebDataService in the constructor and this will
-// serve it up via GetWebData().
-class MockWebDataServiceWrapper : public MockWebDataServiceWrapperBase {
- public:
-  MockWebDataServiceWrapper(
-      scoped_refptr<autofill::AutofillWebDataService> fake_autofill,
-      scoped_refptr<TokenWebData> fake_token);
-
-  ~MockWebDataServiceWrapper() override;
-
-  scoped_refptr<autofill::AutofillWebDataService> GetProfileAutofillWebData()
-      override;
-
-  scoped_refptr<autofill::AutofillWebDataService> GetAccountAutofillWebData()
-      override;
-
-  scoped_refptr<TokenWebData> GetTokenWebData() override;
-
- protected:
-  scoped_refptr<autofill::AutofillWebDataService> fake_autofill_web_data_;
-  scoped_refptr<TokenWebData> fake_token_web_data_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockWebDataServiceWrapper);
-};
-
-#endif  // COMPONENTS_WEBDATA_SERVICES_WEB_DATA_SERVICE_TEST_UTIL_H__
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 3074a2a0..e8b16453 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -68,6 +68,7 @@
     "//components/services/leveldb:lib",
     "//components/tracing",
     "//components/tracing:startup_tracing",
+    "//components/tracing/common:interfaces",
     "//components/ui_devtools",
     "//components/url_formatter",
     "//components/variations",
@@ -1179,6 +1180,8 @@
     "loader/sec_fetch_site_resource_handler.h",
     "loader/shared_cors_origin_access_list_impl.cc",
     "loader/shared_cors_origin_access_list_impl.h",
+    "loader/single_request_url_loader_factory.cc",
+    "loader/single_request_url_loader_factory.h",
     "loader/source_stream_to_data_pipe.cc",
     "loader/source_stream_to_data_pipe.h",
     "loader/throttling_resource_handler.cc",
@@ -1857,6 +1860,8 @@
     "tracing/background_startup_tracing_observer.h",
     "tracing/background_tracing_active_scenario.cc",
     "tracing/background_tracing_active_scenario.h",
+    "tracing/background_tracing_agent_client_impl.cc",
+    "tracing/background_tracing_agent_client_impl.h",
     "tracing/background_tracing_config_impl.cc",
     "tracing/background_tracing_config_impl.h",
     "tracing/background_tracing_manager_impl.cc",
@@ -1867,8 +1872,6 @@
     "tracing/file_tracing_provider_impl.h",
     "tracing/perfetto_file_tracer.cc",
     "tracing/perfetto_file_tracer.h",
-    "tracing/trace_message_filter.cc",
-    "tracing/trace_message_filter.h",
     "tracing/tracing_controller_impl.cc",
     "tracing/tracing_controller_impl.h",
     "tracing/tracing_controller_impl_data_endpoint.cc",
diff --git a/content/browser/accessibility/accessibility_ipc_error_browsertest.cc b/content/browser/accessibility/accessibility_ipc_error_browsertest.cc
index 74a4c644..719abd7e 100644
--- a/content/browser/accessibility/accessibility_ipc_error_browsertest.cc
+++ b/content/browser/accessibility/accessibility_ipc_error_browsertest.cc
@@ -117,16 +117,16 @@
   VLOG(1) << tree->ToString();
 
   EXPECT_EQ(ax::mojom::Role::kRootWebArea, root->data().role);
-  ASSERT_EQ(2u, root->children().size());
+  ASSERT_EQ(2u, root->GetUnignoredChildCount());
 
-  const ui::AXNode* live_region = root->children()[0];
-  ASSERT_EQ(1u, live_region->children().size());
+  const ui::AXNode* live_region = root->GetUnignoredChildAtIndex(0);
+  ASSERT_EQ(1u, live_region->GetUnignoredChildCount());
   EXPECT_EQ(ax::mojom::Role::kGenericContainer, live_region->data().role);
 
-  const ui::AXNode* para = live_region->children().front();
+  const ui::AXNode* para = live_region->GetUnignoredChildAtIndex(0);
   EXPECT_EQ(ax::mojom::Role::kParagraph, para->data().role);
 
-  const ui::AXNode* button = root->children()[1];
+  const ui::AXNode* button = root->GetUnignoredChildAtIndex(1);
   EXPECT_EQ(ax::mojom::Role::kButton, button->data().role);
 }
 
diff --git a/content/browser/accessibility/accessibility_tree_formatter_blink.cc b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
index a237eaf..4ef0ceb 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_blink.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
@@ -183,6 +183,7 @@
   AddPropertyFilter(property_filters, "flowtoIds*");
   AddPropertyFilter(property_filters, "detailsIds*");
   AddPropertyFilter(property_filters, "invalidState=*");
+  AddPropertyFilter(property_filters, "ignored*");
   AddPropertyFilter(property_filters, "invalidState=false",
                     PropertyFilter::DENY);  // Don't show false value
   AddPropertyFilter(property_filters, "roleDescription=*");
@@ -204,8 +205,9 @@
     const BrowserAccessibility& node) const {
   if (node.HasStringAttribute(ax::mojom::StringAttribute::kChildTreeId))
     return node.PlatformChildCount();
-  else
-    return node.InternalChildCount();
+  // We don't want to use InternalGetChild as we want to include
+  // ignored nodes in the tree for tests.
+  return node.node()->children().size();
 }
 
 BrowserAccessibility* AccessibilityTreeFormatterBlink::GetChild(
@@ -213,8 +215,13 @@
     uint32_t i) const {
   if (node.HasStringAttribute(ax::mojom::StringAttribute::kChildTreeId))
     return node.PlatformGetChild(i);
-  else
-    return node.InternalGetChild(i);
+  // We don't want to use InternalGetChild as we want to include
+  // ignored nodes in the tree for tests.
+  if (i < 0 && i >= node.node()->children().size())
+    return nullptr;
+  ui::AXNode* child_node = node.node()->children()[i];
+  DCHECK(child_node);
+  return node.manager()->GetFromAXNode(child_node);
 }
 
 void AccessibilityTreeFormatterBlink::AddProperties(
diff --git a/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc b/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc
index 04c6c27e..01e293a 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc
@@ -191,6 +191,8 @@
       QUOTE(EVENT_OBJECT_DRAGLEAVE),
       QUOTE(EVENT_OBJECT_DRAGDROPPED),
       QUOTE(EVENT_SYSTEM_ALERT),
+      QUOTE(EVENT_SYSTEM_MENUPOPUPSTART),
+      QUOTE(EVENT_SYSTEM_MENUPOPUPEND),
       QUOTE(EVENT_SYSTEM_SCROLLINGSTART),
       QUOTE(EVENT_SYSTEM_SCROLLINGEND),
       QUOTE(IA2_EVENT_ACTION_CHANGED),
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index ac378ae..ed8995b 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -123,7 +123,7 @@
   if (!instance_active())
     return nullptr;
 
-  ui::AXNode* parent = node_->parent();
+  ui::AXNode* parent = node_->GetUnignoredParent();
   if (parent)
     return manager_->GetFromAXNode(parent);
 
@@ -329,25 +329,26 @@
 uint32_t BrowserAccessibility::InternalChildCount() const {
   if (!node_ || !manager_)
     return 0;
-  return uint32_t{node_->children().size()};
+  return node_->GetUnignoredChildCount();
 }
 
 BrowserAccessibility* BrowserAccessibility::InternalGetChild(
     uint32_t child_index) const {
-  if (!node_ || !manager_ || child_index >= InternalChildCount())
+  if (!node_ || !manager_)
     return nullptr;
+  auto* child_node = node_->GetUnignoredChildAtIndex(child_index);
+  if (child_node)
+    return manager_->GetFromAXNode(child_node);
 
-  auto* child_node = node_->children()[child_index];
-  DCHECK(child_node);
-  return manager_->GetFromAXNode(child_node);
+  return nullptr;
 }
 
 BrowserAccessibility* BrowserAccessibility::InternalGetParent() const {
   if (!node_ || !manager_)
     return nullptr;
-  ui::AXNode* parent = node_->parent();
-  if (parent)
-    return manager_->GetFromAXNode(parent);
+  auto* child_node = node_->GetUnignoredParent();
+  if (child_node)
+    return manager_->GetFromAXNode(child_node);
 
   return nullptr;
 }
@@ -1310,7 +1311,7 @@
 const std::vector<gfx::NativeViewAccessible>
 BrowserAccessibility::GetDescendants() const {
   std::vector<gfx::NativeViewAccessible> descendants;
-  if (PlatformChildCount() > 0) {
+  if (!HasState(ax::mojom::State::kIgnored) && PlatformChildCount() > 0) {
     BrowserAccessibility* next_sibling_node = GetNextSibling();
     BrowserAccessibility* next_descendant_node =
         BrowserAccessibilityManager::NextInTreeOrder(this);
@@ -1417,7 +1418,7 @@
 }
 
 int BrowserAccessibility::GetIndexInParent() const {
-  return node_ ? int{node_->index_in_parent()} : -1;
+  return node_ ? node_->GetUnignoredIndexInParent() : -1;
 }
 
 gfx::AcceleratedWidget
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index 16720e3e..4654bf71 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -244,9 +244,9 @@
   bool instance_active() const { return node_ && manager_; }
   ui::AXNode* node() const { return node_; }
 
-  // These access the internal accessibility tree, which doesn't necessarily
-  // reflect the accessibility tree that should be exposed on each platform.
-  // Use PlatformChildCount and PlatformGetChild to implement platform
+  // These access the internal unignored accessibility tree, which doesn't
+  // necessarily reflect the accessibility tree that should be exposed on each
+  // platform. Use PlatformChildCount and PlatformGetChild to implement platform
   // accessibility APIs.
   uint32_t InternalChildCount() const;
   BrowserAccessibility* InternalGetChild(uint32_t child_index) const;
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index be8019d3..40447035 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -1731,16 +1731,17 @@
       break;
 
     case ax::mojom::InvalidState::kTrue:
-    case ax::mojom::InvalidState::kOther:
       message_id = CONTENT_INVALID_TRUE;
       break;
-
-    case ax::mojom::InvalidState::kSpelling:
-      message_id = CONTENT_INVALID_SPELLING;
-      break;
-
-    case ax::mojom::InvalidState::kGrammar:
-      message_id = CONTENT_INVALID_GRAMMAR;
+    case ax::mojom::InvalidState::kOther:
+      std::string ariaInvalid = GetData().GetStringAttribute(
+          ax::mojom::StringAttribute::kAriaInvalidValue);
+      if (ariaInvalid == "spelling")
+        message_id = CONTENT_INVALID_SPELLING;
+      else if (ariaInvalid == "grammar")
+        message_id = CONTENT_INVALID_GRAMMAR;
+      else
+        message_id = CONTENT_INVALID_TRUE;
       break;
   }
 
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index 237b07ca..9f42576 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -1347,10 +1347,6 @@
       return @"false";
     case ax::mojom::InvalidState::kTrue:
       return @"true";
-    case ax::mojom::InvalidState::kSpelling:
-      return @"spelling";
-    case ax::mojom::InvalidState::kGrammar:
-      return @"grammar";
     case ax::mojom::InvalidState::kOther: {
       std::string ariaInvalidValue;
       if (owner_->GetStringAttribute(
diff --git a/content/browser/accessibility/browser_accessibility_com_win.cc b/content/browser/accessibility/browser_accessibility_com_win.cc
index 791f131..58209c7 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.cc
+++ b/content/browser/accessibility/browser_accessibility_com_win.cc
@@ -1838,10 +1838,6 @@
       break;
     case ax::mojom::InvalidState::kTrue:
       return invalid_value = L"true";
-    case ax::mojom::InvalidState::kSpelling:
-      return invalid_value = L"spelling";
-    case ax::mojom::InvalidState::kGrammar:
-      return base::ASCIIToUTF16("grammar");
     case ax::mojom::InvalidState::kOther: {
       base::string16 aria_invalid_value;
       if (target->GetString16Attribute(
@@ -2004,13 +2000,25 @@
     const std::vector<int>& marker_ends =
         owner()->GetIntListAttribute(ax::mojom::IntListAttribute::kMarkerEnds);
     for (size_t i = 0; i < marker_types.size(); ++i) {
-      if (!(marker_types[i] &
-            static_cast<int32_t>(ax::mojom::MarkerType::kSpelling)))
+      bool isSpellingError =
+          (marker_types[i] &
+           static_cast<int32_t>(ax::mojom::MarkerType::kSpelling)) != 0;
+      bool isGrammarError =
+          (marker_types[i] &
+           static_cast<int32_t>(ax::mojom::MarkerType::kGrammar)) != 0;
+      base::string16 invalid_str;
+      if (isSpellingError && isGrammarError)
+        invalid_str = L"invalid:spelling,grammar";
+      else if (isSpellingError)
+        invalid_str = L"invalid:spelling";
+      else if (isGrammarError)
+        invalid_str = L"invalid:grammar";
+      else
         continue;
       int start_offset = marker_starts[i];
       int end_offset = marker_ends[i];
       std::vector<base::string16> start_attributes;
-      start_attributes.push_back(L"invalid:spelling");
+      start_attributes.push_back(invalid_str);
       spelling_attributes[start_offset] = start_attributes;
       spelling_attributes[end_offset] = std::vector<base::string16>();
     }
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 773fc1cb..8903c77 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -1172,7 +1172,6 @@
   BrowserAccessibility* wrapper = factory_->Create();
   id_wrapper_map_[node->id()] = wrapper;
   wrapper->Init(this, node);
-  wrapper->OnDataChanged();
 }
 
 void BrowserAccessibilityManager::OnNodeReparented(ui::AXTree* tree,
@@ -1184,7 +1183,6 @@
     id_wrapper_map_[node->id()] = wrapper;
   }
   wrapper->Init(this, node);
-  wrapper->OnDataChanged();
 }
 
 void BrowserAccessibilityManager::OnNodeChanged(ui::AXTree* tree,
@@ -1218,6 +1216,25 @@
   // we're properly connected.
   if (ax_tree_id_changed || root_changed)
     connected_to_parent_tree_node_ = false;
+
+  // Calls OnDataChanged on newly created or reparented nodes.
+  for (const auto change : changes) {
+    ui::AXNode* node = change.node;
+    BrowserAccessibility* wrapper = GetFromAXNode(node);
+    if (wrapper) {
+      switch (change.type) {
+        case NODE_CREATED:
+        case NODE_REPARENTED:
+          wrapper->OnDataChanged();
+          break;
+          // Unhandled.
+        case NODE_CHANGED:
+        case SUBTREE_CREATED:
+        case SUBTREE_REPARENTED:
+          break;
+      }
+    }
+  }
 }
 
 ui::AXNode* BrowserAccessibilityManager::GetNodeFromTree(
diff --git a/content/browser/accessibility/browser_accessibility_manager_win.cc b/content/browser/accessibility/browser_accessibility_manager_win.cc
index 9c8fd53..0c460ba 100644
--- a/content/browser/accessibility/browser_accessibility_manager_win.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_win.cc
@@ -82,6 +82,10 @@
   BrowserAccessibility* obj = GetFromAXNode(node);
   FireWinAccessibilityEvent(EVENT_OBJECT_HIDE, obj);
   FireUiaStructureChangedEvent(StructureChangeType_ChildRemoved, obj);
+  if (obj && obj->GetRole() == ax::mojom::Role::kMenu) {
+    FireWinAccessibilityEvent(EVENT_SYSTEM_MENUPOPUPEND, obj);
+    FireUiaAccessibilityEvent(UIA_MenuClosedEventId, obj);
+  }
 }
 
 void BrowserAccessibilityManagerWin::UserIsReloading() {
@@ -99,9 +103,6 @@
   BrowserAccessibilityManager::FireFocusEvent(node);
   DCHECK(node);
 
-  if (node->GetRole() == ax::mojom::Role::kMenu)
-    FireUiaAccessibilityEvent(UIA_MenuOpenedEventId, node);
-
   FireWinAccessibilityEvent(EVENT_OBJECT_FOCUS, node);
   FireUiaAccessibilityEvent(UIA_AutomationFocusChangedEventId, node);
 }
@@ -347,6 +348,10 @@
     case ui::AXEventGenerator::Event::SUBTREE_CREATED:
       FireWinAccessibilityEvent(EVENT_OBJECT_SHOW, node);
       FireUiaStructureChangedEvent(StructureChangeType_ChildAdded, node);
+      if (node->GetRole() == ax::mojom::Role::kMenu) {
+        FireWinAccessibilityEvent(EVENT_SYSTEM_MENUPOPUPSTART, node);
+        FireUiaAccessibilityEvent(UIA_MenuOpenedEventId, node);
+      }
       break;
     case ui::AXEventGenerator::Event::VALUE_CHANGED:
       FireWinAccessibilityEvent(EVENT_OBJECT_VALUECHANGE, node);
@@ -393,9 +398,6 @@
 void BrowserAccessibilityManagerWin::OnFocusLost(BrowserAccessibility* node) {
   BrowserAccessibilityManager::OnFocusLost(node);
   DCHECK(node);
-
-  if (node->GetRole() == ax::mojom::Role::kMenu)
-    FireUiaAccessibilityEvent(UIA_MenuClosedEventId, node);
 }
 
 void BrowserAccessibilityManagerWin::FireWinAccessibilityEvent(
diff --git a/content/browser/accessibility/browser_accessibility_unittest.cc b/content/browser/accessibility/browser_accessibility_unittest.cc
index 873b961a..22fff18b 100644
--- a/content/browser/accessibility/browser_accessibility_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_unittest.cc
@@ -100,11 +100,11 @@
 TEST_F(BrowserAccessibilityTest, TestGetDescendants) {
   // Set up ax tree with the following structure:
   //
-  // root____________
-  // |               |
-  // para1___        text3
-  // |       |
-  // text1   text2
+  // root_____________________
+  // |               |       |
+  // para1____       text3   para2____ (hidden)
+  // |       |               |       |
+  // text1   text2           text4   text5 (visible)
   ui::AXNodeData text1;
   text1.id = 111;
   text1.role = ax::mojom::Role::kStaticText;
@@ -120,21 +120,41 @@
   text3.role = ax::mojom::Role::kStaticText;
   text3.SetName("Three four five.");
 
+  ui::AXNodeData text4;
+  text4.id = 114;
+  text4.role = ax::mojom::Role::kStaticText;
+  text4.SetName("four five six.");
+  text4.AddState(ax::mojom::State::kIgnored);
+
+  ui::AXNodeData text5;
+  text5.id = 115;
+  text5.role = ax::mojom::Role::kStaticText;
+  text5.SetName("five six seven.");
+
   ui::AXNodeData para1;
   para1.id = 11;
   para1.role = ax::mojom::Role::kParagraph;
   para1.child_ids.push_back(text1.id);
   para1.child_ids.push_back(text2.id);
 
+  ui::AXNodeData para2;
+  para2.id = 12;
+  para2.role = ax::mojom::Role::kParagraph;
+  para2.child_ids.push_back(text4.id);
+  para2.child_ids.push_back(text5.id);
+  para2.AddState(ax::mojom::State::kIgnored);
+
   ui::AXNodeData root;
   root.id = 1;
   root.role = ax::mojom::Role::kRootWebArea;
   root.child_ids.push_back(para1.id);
   root.child_ids.push_back(text3.id);
+  root.child_ids.push_back(para2.id);
 
   std::unique_ptr<BrowserAccessibilityManager> manager(
       BrowserAccessibilityManager::Create(
-          MakeAXTreeUpdate(root, para1, text1, text2, text3),
+          MakeAXTreeUpdate(root, para1, text1, text2, text3, para2, text4,
+                           text5),
           test_browser_accessibility_delegate_.get(),
           new BrowserAccessibilityFactory()));
 
@@ -143,6 +163,9 @@
   BrowserAccessibility* text1_obj = manager->GetFromID(111);
   BrowserAccessibility* text2_obj = manager->GetFromID(112);
   BrowserAccessibility* text3_obj = manager->GetFromID(113);
+  BrowserAccessibility* para2_obj = manager->GetFromID(12);
+  BrowserAccessibility* text4_obj = manager->GetFromID(114);
+  BrowserAccessibility* text5_obj = root_obj->PlatformGetChild(2);
 
   // Leaf nodes should have no children.
   std::vector<gfx::NativeViewAccessible> descendants =
@@ -156,6 +179,15 @@
   descendants = text3_obj->GetDescendants();
   EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
 
+  descendants = text4_obj->GetDescendants();
+  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
+
+  descendants = text5_obj->GetDescendants();
+  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
+
+  descendants = para2_obj->GetDescendants();
+  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
+
   // Verify that para1 has two children (text1 and tex2).
   descendants = para_obj->GetDescendants();
   expected_descendants = {text1_obj->GetNativeViewAccessible(),
@@ -164,11 +196,13 @@
 
   // Calling GetChildNodeIds on the root should encompass the entire
   // right and left subtrees (para1, text1, text2, and text3).
+  // para2 and its subtree should be ignored, except for text5
   descendants = root_obj->GetDescendants();
   expected_descendants = {para_obj->GetNativeViewAccessible(),
                           text1_obj->GetNativeViewAccessible(),
                           text2_obj->GetNativeViewAccessible(),
-                          text3_obj->GetNativeViewAccessible()};
+                          text3_obj->GetNativeViewAccessible(),
+                          text5_obj->GetNativeViewAccessible()};
   EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
 
   manager.reset();
diff --git a/content/browser/accessibility/cross_platform_accessibility_browsertest.cc b/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
index 34041d0..f594967 100644
--- a/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
+++ b/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
@@ -188,8 +188,8 @@
   EXPECT_EQ(ax::mojom::Role::kRootWebArea, root->data().role);
 
   // Check properties of the BODY element.
-  ASSERT_EQ(1u, root->children().size());
-  const ui::AXNode* body = root->children().front();
+  ASSERT_EQ(1u, root->GetUnignoredChildCount());
+  const ui::AXNode* body = root->GetUnignoredChildAtIndex(0);
   EXPECT_EQ(ax::mojom::Role::kGenericContainer, body->data().role);
   EXPECT_STREQ("body",
                GetAttr(body, ax::mojom::StringAttribute::kHtmlTag).c_str());
@@ -197,9 +197,9 @@
                GetAttr(body, ax::mojom::StringAttribute::kDisplay).c_str());
 
   // Check properties of the two children of the BODY element.
-  ASSERT_EQ(2u, body->children().size());
+  ASSERT_EQ(2u, body->GetUnignoredChildCount());
 
-  const ui::AXNode* button = body->children()[0];
+  const ui::AXNode* button = body->GetUnignoredChildAtIndex(0);
   EXPECT_EQ(ax::mojom::Role::kButton, button->data().role);
   EXPECT_STREQ("input",
                GetAttr(button, ax::mojom::StringAttribute::kHtmlTag).c_str());
@@ -213,7 +213,7 @@
   EXPECT_STREQ("value", button->data().html_attributes[1].first.c_str());
   EXPECT_STREQ("push", button->data().html_attributes[1].second.c_str());
 
-  const ui::AXNode* checkbox = body->children()[1];
+  const ui::AXNode* checkbox = body->GetUnignoredChildAtIndex(1);
   EXPECT_EQ(ax::mojom::Role::kCheckBox, checkbox->data().role);
   EXPECT_STREQ("input",
                GetAttr(checkbox, ax::mojom::StringAttribute::kHtmlTag).c_str());
@@ -238,10 +238,10 @@
 
   const ui::AXTree& tree = GetAXTree();
   const ui::AXNode* root = tree.root();
-  ASSERT_EQ(1u, root->children().size());
-  const ui::AXNode* body = root->children().front();
-  ASSERT_EQ(1u, body->children().size());
-  const ui::AXNode* text = body->children().front();
+  ASSERT_EQ(1u, root->GetUnignoredChildCount());
+  const ui::AXNode* body = root->GetUnignoredChildAtIndex(0);
+  ASSERT_EQ(1u, body->GetUnignoredChildCount());
+  const ui::AXNode* text = body->GetUnignoredChildAtIndex(0);
   EXPECT_EQ(ax::mojom::Role::kTextField, text->data().role);
   EXPECT_STREQ("input",
                GetAttr(text, ax::mojom::StringAttribute::kHtmlTag).c_str());
@@ -269,10 +269,10 @@
 
   const ui::AXTree& tree = GetAXTree();
   const ui::AXNode* root = tree.root();
-  ASSERT_EQ(1u, root->children().size());
-  const ui::AXNode* body = root->children().front();
-  ASSERT_EQ(1u, body->children().size());
-  const ui::AXNode* text = body->children().front();
+  ASSERT_EQ(1u, root->GetUnignoredChildCount());
+  const ui::AXNode* body = root->GetUnignoredChildAtIndex(0);
+  ASSERT_EQ(1u, body->GetUnignoredChildCount());
+  const ui::AXNode* text = body->GetUnignoredChildAtIndex(0);
   EXPECT_EQ(ax::mojom::Role::kTextField, text->data().role);
   EXPECT_STREQ("input",
                GetAttr(text, ax::mojom::StringAttribute::kHtmlTag).c_str());
@@ -323,33 +323,33 @@
 
   const ui::AXTree& tree = GetAXTree();
   const ui::AXNode* root = tree.root();
-  ASSERT_EQ(1u, root->children().size());
-  const ui::AXNode* body = root->children().front();
-  ASSERT_EQ(3u, body->children().size());
+  ASSERT_EQ(1u, root->GetUnignoredChildCount());
+  const ui::AXNode* body = root->GetUnignoredChildAtIndex(0);
+  ASSERT_EQ(3u, body->GetUnignoredChildCount());
 
-  const ui::AXNode* button1 = body->children()[0];
+  const ui::AXNode* button1 = body->GetUnignoredChildAtIndex(0);
   EXPECT_EQ(ax::mojom::Role::kButton, button1->data().role);
   EXPECT_STREQ("Button 1",
                GetAttr(button1, ax::mojom::StringAttribute::kName).c_str());
 
-  const ui::AXNode* iframe = body->children()[1];
+  const ui::AXNode* iframe = body->GetUnignoredChildAtIndex(1);
   EXPECT_STREQ("iframe",
                GetAttr(iframe, ax::mojom::StringAttribute::kHtmlTag).c_str());
-  ASSERT_EQ(1u, iframe->children().size());
+  ASSERT_EQ(1u, iframe->GetUnignoredChildCount());
 
-  const ui::AXNode* sub_document = iframe->children().front();
+  const ui::AXNode* sub_document = iframe->GetUnignoredChildAtIndex(0);
   EXPECT_EQ(ax::mojom::Role::kWebArea, sub_document->data().role);
-  ASSERT_EQ(1u, sub_document->children().size());
+  ASSERT_EQ(1u, sub_document->GetUnignoredChildCount());
 
-  const ui::AXNode* sub_body = sub_document->children().front();
-  ASSERT_EQ(1u, sub_body->children().size());
+  const ui::AXNode* sub_body = sub_document->GetUnignoredChildAtIndex(0);
+  ASSERT_EQ(1u, sub_body->GetUnignoredChildCount());
 
-  const ui::AXNode* button2 = sub_body->children().front();
+  const ui::AXNode* button2 = sub_body->GetUnignoredChildAtIndex(0);
   EXPECT_EQ(ax::mojom::Role::kButton, button2->data().role);
   EXPECT_STREQ("Button 2",
                GetAttr(button2, ax::mojom::StringAttribute::kName).c_str());
 
-  const ui::AXNode* button3 = body->children()[2];
+  const ui::AXNode* button3 = body->GetUnignoredChildAtIndex(2);
   EXPECT_EQ(ax::mojom::Role::kButton, button3->data().role);
   EXPECT_STREQ("Button 3",
                GetAttr(button3, ax::mojom::StringAttribute::kName).c_str());
@@ -396,18 +396,24 @@
 
   const ui::AXTree& tree = GetAXTree();
   const ui::AXNode* root = tree.root();
-  const ui::AXNode* table = root->children().front();
+  const ui::AXNode* table = root->GetUnignoredChildAtIndex(0);
   EXPECT_EQ(ax::mojom::Role::kTable, table->data().role);
-  ASSERT_GE(table->children().size(), 2u);
-  EXPECT_EQ(ax::mojom::Role::kRow, table->children()[0]->data().role);
-  EXPECT_EQ(ax::mojom::Role::kRow, table->children()[1]->data().role);
+  ASSERT_GE(table->GetUnignoredChildCount(), 2u);
+  EXPECT_EQ(ax::mojom::Role::kRow,
+            table->GetUnignoredChildAtIndex(0)->data().role);
+  EXPECT_EQ(ax::mojom::Role::kRow,
+            table->GetUnignoredChildAtIndex(1)->data().role);
   EXPECT_EQ(3, GetIntAttr(table, ax::mojom::IntAttribute::kTableColumnCount));
   EXPECT_EQ(2, GetIntAttr(table, ax::mojom::IntAttribute::kTableRowCount));
 
-  const ui::AXNode* cell1 = table->children()[0]->children()[0];
-  const ui::AXNode* cell2 = table->children()[0]->children()[1];
-  const ui::AXNode* cell3 = table->children()[1]->children()[0];
-  const ui::AXNode* cell4 = table->children()[1]->children()[1];
+  const ui::AXNode* cell1 =
+      table->GetUnignoredChildAtIndex(0)->GetUnignoredChildAtIndex(0);
+  const ui::AXNode* cell2 =
+      table->GetUnignoredChildAtIndex(0)->GetUnignoredChildAtIndex(1);
+  const ui::AXNode* cell3 =
+      table->GetUnignoredChildAtIndex(1)->GetUnignoredChildAtIndex(0);
+  const ui::AXNode* cell4 =
+      table->GetUnignoredChildAtIndex(1)->GetUnignoredChildAtIndex(1);
 
   EXPECT_EQ(0,
             GetIntAttr(cell1, ax::mojom::IntAttribute::kTableCellColumnIndex));
@@ -440,8 +446,8 @@
   NavigateToURL(shell(), url);
   const ui::AXTree& tree = GetAXTree();
   const ui::AXNode* root = tree.root();
-  ASSERT_EQ(1u, root->children().size());
-  const ui::AXNode* textbox = root->children().front();
+  ASSERT_EQ(1u, root->GetUnignoredChildCount());
+  const ui::AXNode* textbox = root->GetUnignoredChildAtIndex(0);
   EXPECT_TRUE(textbox->data().HasAction(ax::mojom::Action::kSetValue));
 }
 
@@ -462,17 +468,17 @@
 
   const ui::AXTree& tree = GetAXTree();
   const ui::AXNode* root = tree.root();
-  const ui::AXNode* table = root->children().front();
+  const ui::AXNode* table = root->GetUnignoredChildAtIndex(0);
   EXPECT_EQ(ax::mojom::Role::kTable, table->data().role);
-  EXPECT_EQ(1u, table->children().size());
-  const ui::AXNode* row = table->children().front();
-  EXPECT_EQ(5u, row->children().size());
+  EXPECT_EQ(1u, table->GetUnignoredChildCount());
+  const ui::AXNode* row = table->GetUnignoredChildAtIndex(0);
+  EXPECT_EQ(5u, row->GetUnignoredChildCount());
 
-  const ui::AXNode* header1 = row->children()[0];
-  const ui::AXNode* header2 = row->children()[1];
-  const ui::AXNode* header3 = row->children()[2];
-  const ui::AXNode* header4 = row->children()[3];
-  const ui::AXNode* header5 = row->children()[4];
+  const ui::AXNode* header1 = row->GetUnignoredChildAtIndex(0);
+  const ui::AXNode* header2 = row->GetUnignoredChildAtIndex(1);
+  const ui::AXNode* header3 = row->GetUnignoredChildAtIndex(2);
+  const ui::AXNode* header4 = row->GetUnignoredChildAtIndex(3);
+  const ui::AXNode* header5 = row->GetUnignoredChildAtIndex(4);
 
   EXPECT_EQ(static_cast<int>(ax::mojom::SortDirection::kAscending),
             GetIntAttr(header1, ax::mojom::IntAttribute::kSortDirection));
@@ -607,7 +613,7 @@
       base::ASCIIToUTF16("highlighted content"));
 
   // Android doesn't always have a child in this case.
-  if (mark_node->PlatformChildCount() > 0) {
+  if (mark_node->PlatformChildCount() > 0u) {
     BrowserAccessibility* mark_text_node = mark_node->PlatformGetChild(0);
     ASSERT_EQ(0u, mark_text_node->PlatformChildCount());
     TestGetStyleNameAttributeAsLocalizedString(
diff --git a/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
index 99070d8..3154970 100644
--- a/content/browser/accessibility/dump_accessibility_events_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
@@ -255,8 +255,15 @@
   RunEventTest(FILE_PATH_LITERAL("aria-controls-changed.html"));
 }
 
+#if defined(OS_WIN)
+#define MAYBE_AccessibilityEventsAriaDisabledChanged \
+  DISABLED_AccessibilityEventsAriaDisabledChanged
+#else
+#define MAYBE_AccessibilityEventsAriaDisabledChanged \
+  AccessibilityEventsAriaDisabledChanged
+#endif
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest,
-                       AccessibilityEventsAriaDisabledChanged) {
+                       MAYBE_AccessibilityEventsAriaDisabledChanged) {
   RunEventTest(FILE_PATH_LITERAL("aria-disabled-changed.html"));
 }
 
diff --git a/content/browser/accessibility/snapshot_ax_tree_browsertest.cc b/content/browser/accessibility/snapshot_ax_tree_browsertest.cc
index e9a2df5..6baee433 100644
--- a/content/browser/accessibility/snapshot_ax_tree_browsertest.cc
+++ b/content/browser/accessibility/snapshot_ax_tree_browsertest.cc
@@ -52,8 +52,8 @@
             node->data().GetStringAttribute(ax::mojom::StringAttribute::kName) +
             "'";
   *dst += "\n";
-  for (const auto* child : node->children())
-    DumpRolesAndNamesAsText(child, indent + 1, dst);
+  for (size_t i = 0; i < node->GetUnignoredChildCount(); ++i)
+    DumpRolesAndNamesAsText(node->GetUnignoredChildAtIndex(i), indent + 1, dst);
 }
 
 }  // namespace
@@ -87,9 +87,9 @@
   ui::AXNode* root = tree.root();
   ASSERT_NE(nullptr, root);
   ASSERT_EQ(ax::mojom::Role::kRootWebArea, root->data().role);
-  ui::AXNode* group = root->children().front();
+  ui::AXNode* group = root->GetUnignoredChildAtIndex(0);
   ASSERT_EQ(ax::mojom::Role::kGenericContainer, group->data().role);
-  ui::AXNode* button = group->children().front();
+  ui::AXNode* button = group->GetUnignoredChildAtIndex(0);
   ASSERT_EQ(ax::mojom::Role::kButton, button->data().role);
 }
 
diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc
index 51c266a..ff5e049 100644
--- a/content/browser/browser_child_process_host_impl.cc
+++ b/content/browser/browser_child_process_host_impl.cc
@@ -34,7 +34,7 @@
 #include "content/browser/histogram_controller.h"
 #include "content/browser/loader/resource_message_filter.h"
 #include "content/browser/service_manager/service_manager_context.h"
-#include "content/browser/tracing/trace_message_filter.h"
+#include "content/browser/tracing/background_tracing_manager_impl.h"
 #include "content/common/child_process_host_impl.h"
 #include "content/common/service_manager/child_connection.h"
 #include "content/public/browser/browser_child_process_host_delegate.h"
@@ -164,7 +164,6 @@
   data_.id = ChildProcessHostImpl::GenerateChildProcessUniqueId();
 
   child_process_host_ = ChildProcessHost::Create(this);
-  AddFilter(new TraceMessageFilter(data_.id));
 
   g_child_process_list.Get().push_back(this);
   GetContentClient()->browser()->BrowserChildProcessHostCreated(this);
@@ -333,6 +332,12 @@
     cmd_line->AppendSwitchASCII(
         service_manager::switches::kServiceRequestChannelToken,
         child_connection_->service_token());
+
+    // Tracing adds too much overhead to the profiling service.
+    if (service_manager::SandboxTypeFromCommandLine(*cmd_line) !=
+        service_manager::SANDBOX_TYPE_PROFILING) {
+      BackgroundTracingManagerImpl::ActivateForProcess(this);
+    }
   }
 
   // All processes should have a non-empty metrics name.
diff --git a/content/browser/browsing_instance.cc b/content/browser/browsing_instance.cc
index 12e1c5c..cfcf10a6 100644
--- a/content/browser/browsing_instance.cc
+++ b/content/browser/browsing_instance.cc
@@ -111,7 +111,7 @@
   // instance when kProcessSharingWithStrictSiteInstances is enabled because in
   // that case we want each site to have their own SiteInstance object and logic
   // elsewhere ensures that those SiteInstances share a process.
-  if (allow_default_instance &&
+  if (allow_default_instance && !url.SchemeIs(kGuestScheme) &&
       !base::FeatureList::IsEnabled(
           features::kProcessSharingWithStrictSiteInstances) &&
       !SiteInstanceImpl::DoesSiteRequireDedicatedProcess(isolation_context_,
diff --git a/content/browser/child_process_security_policy_unittest.cc b/content/browser/child_process_security_policy_unittest.cc
index b29ae26..d62db81 100644
--- a/content/browser/child_process_security_policy_unittest.cc
+++ b/content/browser/child_process_security_policy_unittest.cc
@@ -1209,9 +1209,13 @@
   EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, http_url));
   EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, http2_url));
 
+  // Isolate |http_url| so we can't get a default SiteInstance.
+  p->AddIsolatedOrigins({url::Origin::Create(http_url)}, &browser_context);
+
   // Lock process to |http_url| origin.
   scoped_refptr<SiteInstanceImpl> foo_instance =
       SiteInstanceImpl::CreateForURL(&browser_context, http_url);
+  EXPECT_FALSE(foo_instance->IsDefaultSiteInstance());
   p->LockToOrigin(foo_instance->GetIsolationContext(), kRendererID,
                   foo_instance->GetSiteURL());
 
diff --git a/content/browser/devtools/render_frame_devtools_agent_host_browsertest.cc b/content/browser/devtools/render_frame_devtools_agent_host_browsertest.cc
index 65f563b..7908a8ed 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host_browsertest.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host_browsertest.cc
@@ -95,6 +95,10 @@
   EXPECT_TRUE(observer_b.WaitForResponse());  // Headers are received.
   observer_b.ResumeNavigation();  // ReadyToCommitNavigation is called.
   EXPECT_EQ(speculative_rfh_b, rfh_devtools_agent->GetFrameHostForTesting());
+  auto speculative_rfh_b_site_id =
+      speculative_rfh_b->GetSiteInstance()->GetId();
+  if (AreDefaultSiteInstancesEnabled())
+    EXPECT_TRUE(speculative_rfh_b->GetSiteInstance()->IsDefaultSiteInstance());
 
   // 4) Navigate elsewhere, it will cancel the previous navigation.
 
@@ -106,7 +110,21 @@
   RenderFrameHostImpl* speculative_rfh_c =
       root->render_manager()->speculative_frame_host();
   EXPECT_TRUE(speculative_rfh_c);
-  EXPECT_EQ(current_rfh, rfh_devtools_agent->GetFrameHostForTesting());
+  auto speculative_rfh_c_site_id =
+      speculative_rfh_c->GetSiteInstance()->GetId();
+  if (AreDefaultSiteInstancesEnabled()) {
+    // Verify that this new URL also belongs to the default SiteInstance and
+    // therefore the RenderFrameHost from the previous navigation could be
+    // reused.
+    EXPECT_TRUE(speculative_rfh_c->GetSiteInstance()->IsDefaultSiteInstance());
+    EXPECT_EQ(speculative_rfh_c, rfh_devtools_agent->GetFrameHostForTesting());
+    EXPECT_EQ(speculative_rfh_b_site_id, speculative_rfh_c_site_id);
+  } else {
+    // Verify that the RenderFrameHost is restored because the new URL required
+    // a new SiteInstance.
+    EXPECT_EQ(current_rfh, rfh_devtools_agent->GetFrameHostForTesting());
+    EXPECT_NE(speculative_rfh_b_site_id, speculative_rfh_c_site_id);
+  }
 
   // 4.b) Navigation: ReadyToCommit.
   observer_c.ResumeNavigation();  // Send the request.
diff --git a/content/browser/download/drag_download_file.cc b/content/browser/download/drag_download_file.cc
index c15e78c..fd4918d 100644
--- a/content/browser/download/drag_download_file.cc
+++ b/content/browser/download/drag_download_file.cc
@@ -260,16 +260,20 @@
 
   state_ = is_successful ? SUCCESS : FAILURE;
 
-  if (is_successful)
-    observer_->OnDownloadCompleted(file_path_);
-  else
-    observer_->OnDownloadAborted();
-
+  scoped_refptr<ui::DownloadFileObserver> file_observer = observer_;
   // Release the observer since we do not need it any more.
   observer_ = nullptr;
-
   if (nested_loop_.running())
     nested_loop_.Quit();
+
+  // Calling file_observer->OnDownloadCompleted() could delete this
+  // object.
+  if (is_successful)
+    file_observer->OnDownloadCompleted(file_path_);
+  else
+    file_observer->OnDownloadAborted();
+
+  // Nothing should be called here as the object might get deleted.
 }
 
 void DragDownloadFile::CheckThread() {
diff --git a/content/browser/frame_host/navigation_handle_impl_browsertest.cc b/content/browser/frame_host/navigation_handle_impl_browsertest.cc
index 196b7a1d..34d19c7 100644
--- a/content/browser/frame_host/navigation_handle_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_handle_impl_browsertest.cc
@@ -12,6 +12,7 @@
 #include "content/browser/frame_host/debug_urls.h"
 #include "content/browser/frame_host/navigation_handle_impl.h"
 #include "content/browser/frame_host/navigation_request.h"
+#include "content/browser/site_instance_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -1445,7 +1446,12 @@
             starting_site_instance);
   // Because of the sad tab, this is actually the b.com SiteInstance, which
   // commits immediately after starting the navigation and has a process.
-  EXPECT_EQ(GURL("http://b.com"), starting_site_instance->GetSiteURL());
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(static_cast<SiteInstanceImpl*>(starting_site_instance.get())
+                    ->IsDefaultSiteInstance());
+  } else {
+    EXPECT_EQ(GURL("http://b.com"), starting_site_instance->GetSiteURL());
+  }
   EXPECT_TRUE(starting_site_instance->HasProcess());
 
   // In https://crbug.com/949977, we used the a.com SiteInstance here and didn't
diff --git a/content/browser/frame_host/navigator_impl_unittest.cc b/content/browser/frame_host/navigator_impl_unittest.cc
index 25e97c4..0c79b97 100644
--- a/content/browser/frame_host/navigator_impl_unittest.cc
+++ b/content/browser/frame_host/navigator_impl_unittest.cc
@@ -19,6 +19,7 @@
 #include "content/common/frame.mojom.h"
 #include "content/common/frame_messages.h"
 #include "content/common/navigation_params.h"
+#include "content/public/browser/child_process_security_policy.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/common/url_utils.h"
@@ -65,7 +66,8 @@
       RenderFrameHostManager* rfhm,
       const SiteInstanceDescriptor& descriptor,
       SiteInstance* candidate_instance) {
-    return rfhm->ConvertToSiteInstance(descriptor, candidate_instance);
+    return rfhm->ConvertToSiteInstance(
+        descriptor, static_cast<SiteInstanceImpl*>(candidate_instance));
   }
 };
 
@@ -507,7 +509,11 @@
   TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
   ASSERT_TRUE(speculative_rfh);
   int32_t site_instance_id_1 = speculative_rfh->GetSiteInstance()->GetId();
-  EXPECT_EQ(kUrl1_site, speculative_rfh->GetSiteInstance()->GetSiteURL());
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(speculative_rfh->GetSiteInstance()->IsDefaultSiteInstance());
+  } else {
+    EXPECT_EQ(kUrl1_site, speculative_rfh->GetSiteInstance()->GetSiteURL());
+  }
 
   // Request navigation to the 2nd URL; the NavigationRequest must have been
   // replaced by a new one with a different URL.
@@ -526,7 +532,13 @@
   speculative_rfh = GetSpeculativeRenderFrameHost(node);
   ASSERT_TRUE(speculative_rfh);
   int32_t site_instance_id_2 = speculative_rfh->GetSiteInstance()->GetId();
-  EXPECT_NE(site_instance_id_1, site_instance_id_2);
+
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(speculative_rfh->GetSiteInstance()->IsDefaultSiteInstance());
+    EXPECT_EQ(site_instance_id_1, site_instance_id_2);
+  } else {
+    EXPECT_NE(site_instance_id_1, site_instance_id_2);
+  }
 
   navigation2->ReadyToCommit();
   EXPECT_EQ(speculative_rfh->navigation_requests().size(), 1u);
@@ -537,7 +549,11 @@
 
   // Confirm that the commit corresponds to the new request.
   ASSERT_TRUE(main_test_rfh());
-  EXPECT_EQ(kUrl2_site, main_test_rfh()->GetSiteInstance()->GetSiteURL());
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(main_test_rfh()->GetSiteInstance()->IsDefaultSiteInstance());
+  } else {
+    EXPECT_EQ(kUrl2_site, main_test_rfh()->GetSiteInstance()->GetSiteURL());
+  }
   EXPECT_EQ(kUrl2, contents()->GetLastCommittedURL());
 
   // Confirm that the committed RenderFrameHost is the latest speculative one.
@@ -821,8 +837,12 @@
   int32_t site_instance_id = speculative_rfh->GetSiteInstance()->GetId();
   ASSERT_TRUE(speculative_rfh);
   EXPECT_NE(speculative_rfh, main_test_rfh());
-  EXPECT_EQ(SiteInstance::GetSiteForURL(browser_context(), kUrl),
-            speculative_rfh->GetSiteInstance()->GetSiteURL());
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(speculative_rfh->GetSiteInstance()->IsDefaultSiteInstance());
+  } else {
+    EXPECT_EQ(SiteInstance::GetSiteForURL(browser_context(), kUrl),
+              speculative_rfh->GetSiteInstance()->GetSiteURL());
+  }
 
   navigation->ReadyToCommit();
   EXPECT_EQ(speculative_rfh->navigation_requests().size(), 1u);
@@ -856,8 +876,13 @@
   EXPECT_NE(init_site_instance_id, site_instance_id);
   EXPECT_EQ(init_site_instance_id, main_test_rfh()->GetSiteInstance()->GetId());
   EXPECT_NE(speculative_rfh, main_test_rfh());
-  EXPECT_EQ(SiteInstance::GetSiteForURL(browser_context(), kUrl),
-            speculative_rfh->GetSiteInstance()->GetSiteURL());
+
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(speculative_rfh->GetSiteInstance()->IsDefaultSiteInstance());
+  } else {
+    EXPECT_EQ(SiteInstance::GetSiteForURL(browser_context(), kUrl),
+              speculative_rfh->GetSiteInstance()->GetSiteURL());
+  }
 
   // It then redirects to yet another site.
   NavigationRequest* main_request = node->navigation_request();
@@ -876,20 +901,37 @@
 
   // Send the commit to the renderer.
   navigation->ReadyToCommit();
+
+  // Once commit happens the speculative RenderFrameHost is updated to match the
+  // known final SiteInstance.
   speculative_rfh = GetSpeculativeRenderFrameHost(node);
   ASSERT_TRUE(speculative_rfh);
   EXPECT_EQ(speculative_rfh->navigation_requests().size(), 1u);
   EXPECT_EQ(init_site_instance_id, main_test_rfh()->GetSiteInstance()->GetId());
-  EXPECT_TRUE(rfh_deleted_observer.deleted());
 
-  // Once commit happens the speculative RenderFrameHost is updated to match the
-  // known final SiteInstance.
-  EXPECT_EQ(SiteInstance::GetSiteForURL(browser_context(), kUrlRedirect),
-            speculative_rfh->GetSiteInstance()->GetSiteURL());
   int32_t redirect_site_instance_id =
       speculative_rfh->GetSiteInstance()->GetId();
+
+  // Expect the initial and redirect SiteInstances to be different because
+  // they should be associated with different BrowsingInstances.
   EXPECT_NE(init_site_instance_id, redirect_site_instance_id);
-  EXPECT_NE(site_instance_id, redirect_site_instance_id);
+
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(speculative_rfh->GetSiteInstance()->IsDefaultSiteInstance());
+    EXPECT_EQ(site_instance_id, redirect_site_instance_id);
+
+    // Verify the old speculative RenderFrameHost was not deleted because
+    // the SiteInstance stayed the same.
+    EXPECT_FALSE(rfh_deleted_observer.deleted());
+  } else {
+    EXPECT_EQ(SiteInstance::GetSiteForURL(browser_context(), kUrlRedirect),
+              speculative_rfh->GetSiteInstance()->GetSiteURL());
+    EXPECT_NE(site_instance_id, redirect_site_instance_id);
+
+    // Verify the old speculative RenderFrameHost was deleted because
+    // the SiteInstance changed.
+    EXPECT_TRUE(rfh_deleted_observer.deleted());
+  }
 
   // Invoke DidCommitProvisionalLoad.
   navigation->Commit();
@@ -949,6 +991,13 @@
 TEST_F(NavigatorTest, SiteInstanceDescriptionConversion) {
   // Navigate to set a current SiteInstance on the RenderFrameHost.
   GURL kUrl1("http://a.com");
+  // Isolate one of the sites so the both can't be mapped to the default
+  // site instance.
+  ChildProcessSecurityPolicy::GetInstance()->AddIsolatedOrigins(
+      {
+          url::Origin::Create(kUrl1),
+      },
+      browser_context());
   contents()->NavigateAndCommit(kUrl1);
   SiteInstance* current_instance = main_test_rfh()->GetSiteInstance();
   ASSERT_TRUE(current_instance);
@@ -1065,8 +1114,14 @@
         current_instance->IsRelatedSiteInstance(converted_instance_1.get()));
     EXPECT_NE(related_instance.get(), converted_instance_1.get());
     EXPECT_NE(unrelated_instance.get(), converted_instance_1.get());
-    EXPECT_EQ(SiteInstance::GetSiteForURL(browser_context(), kUrlSameSiteAs2),
-              converted_instance_1->GetSiteURL());
+
+    if (AreDefaultSiteInstancesEnabled()) {
+      EXPECT_TRUE(static_cast<SiteInstanceImpl*>(converted_instance_1.get())
+                      ->IsDefaultSiteInstance());
+    } else {
+      EXPECT_EQ(SiteInstance::GetSiteForURL(browser_context(), kUrlSameSiteAs2),
+                converted_instance_1->GetSiteURL());
+    }
 
     scoped_refptr<SiteInstance> converted_instance_2 =
         ConvertToSiteInstance(rfhm, descriptor, unrelated_instance.get());
diff --git a/content/browser/frame_host/render_frame_host_delegate.cc b/content/browser/frame_host/render_frame_host_delegate.cc
index 2bfbaaa..76a68ea7 100644
--- a/content/browser/frame_host/render_frame_host_delegate.cc
+++ b/content/browser/frame_host/render_frame_host_delegate.cc
@@ -70,14 +70,14 @@
 bool RenderFrameHostDelegate::CheckMediaAccessPermission(
     RenderFrameHost* render_frame_host,
     const url::Origin& security_origin,
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   LOG(ERROR) << "RenderFrameHostDelegate::CheckMediaAccessPermission: "
              << "Not supported.";
   return false;
 }
 
 std::string RenderFrameHostDelegate::GetDefaultMediaDeviceID(
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   return std::string();
 }
 
diff --git a/content/browser/frame_host/render_frame_host_delegate.h b/content/browser/frame_host/render_frame_host_delegate.h
index 11763c7..26ca226 100644
--- a/content/browser/frame_host/render_frame_host_delegate.h
+++ b/content/browser/frame_host/render_frame_host_delegate.h
@@ -224,12 +224,13 @@
   // or MEDIA_DEVICE_VIDEO_CAPTURE.
   virtual bool CheckMediaAccessPermission(RenderFrameHost* render_frame_host,
                                           const url::Origin& security_origin,
-                                          blink::MediaStreamType type);
+                                          blink::mojom::MediaStreamType type);
 
   // Returns the ID of the default device for the given media device |type|.
   // If the returned value is an empty string, it means that there is no
   // default device for the given |type|.
-  virtual std::string GetDefaultMediaDeviceID(blink::MediaStreamType type);
+  virtual std::string GetDefaultMediaDeviceID(
+      blink::mojom::MediaStreamType type);
 
   // Get the accessibility mode for the WebContents that owns this frame.
   virtual ui::AXMode GetAccessibilityMode();
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index 49b3729..237923a 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -1140,9 +1140,9 @@
 scoped_refptr<SiteInstance>
 RenderFrameHostManager::GetSiteInstanceForNavigation(
     const GURL& dest_url,
-    SiteInstance* source_instance,
-    SiteInstance* dest_instance,
-    SiteInstance* candidate_instance,
+    SiteInstanceImpl* source_instance,
+    SiteInstanceImpl* dest_instance,
+    SiteInstanceImpl* candidate_instance,
     ui::PageTransition transition,
     bool is_failure,
     bool dest_is_restore,
@@ -1689,7 +1689,7 @@
 
 scoped_refptr<SiteInstance> RenderFrameHostManager::ConvertToSiteInstance(
     const SiteInstanceDescriptor& descriptor,
-    SiteInstance* candidate_instance) {
+    SiteInstanceImpl* candidate_instance) {
   SiteInstanceImpl* current_instance = render_frame_host_->GetSiteInstance();
 
   // Note: If the |candidate_instance| matches the descriptor, it will already
@@ -1706,11 +1706,7 @@
   // check if the candidate matches.
   if (candidate_instance &&
       !current_instance->IsRelatedSiteInstance(candidate_instance) &&
-      candidate_instance->GetSiteURL() ==
-          SiteInstanceImpl::GetSiteForURL(
-              static_cast<SiteInstanceImpl*>(candidate_instance)
-                  ->GetIsolationContext(),
-              descriptor.dest_url)) {
+      candidate_instance->DoesSiteForURLMatch(descriptor.dest_url)) {
     return candidate_instance;
   }
 
@@ -2180,7 +2176,7 @@
   // should use.
   // TODO(clamy): We should also consider as a candidate SiteInstance the
   // speculative SiteInstance that was computed on redirects.
-  SiteInstance* candidate_site_instance =
+  SiteInstanceImpl* candidate_site_instance =
       speculative_render_frame_host_
           ? speculative_render_frame_host_->GetSiteInstance()
           : nullptr;
diff --git a/content/browser/frame_host/render_frame_host_manager.h b/content/browser/frame_host/render_frame_host_manager.h
index 124e84a..daa381a9 100644
--- a/content/browser/frame_host/render_frame_host_manager.h
+++ b/content/browser/frame_host/render_frame_host_manager.h
@@ -608,9 +608,9 @@
   // Returns the SiteInstance to use for the navigation.
   scoped_refptr<SiteInstance> GetSiteInstanceForNavigation(
       const GURL& dest_url,
-      SiteInstance* source_instance,
-      SiteInstance* dest_instance,
-      SiteInstance* candidate_instance,
+      SiteInstanceImpl* source_instance,
+      SiteInstanceImpl* dest_instance,
+      SiteInstanceImpl* candidate_instance,
       ui::PageTransition transition,
       bool is_failure,
       bool dest_is_restore,
@@ -669,7 +669,7 @@
   // description, it is returned as is.
   scoped_refptr<SiteInstance> ConvertToSiteInstance(
       const SiteInstanceDescriptor& descriptor,
-      SiteInstance* candidate_instance);
+      SiteInstanceImpl* candidate_instance);
 
   // Returns true if |candidate| is currently on the same web site as dest_url.
   bool IsCurrentlySameSite(RenderFrameHostImpl* candidate,
diff --git a/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
index c82c536..185948e 100644
--- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -1941,9 +1941,14 @@
                         ->render_manager()
                         ->speculative_frame_host();
   CHECK(speculative_rfh);
-  EXPECT_EQ(kRedirectSiteURL, speculative_rfh->GetSiteInstance()->GetSiteURL());
-  if (AreAllSitesIsolatedForTesting())
-    EXPECT_EQ(site_instance_id, speculative_rfh->GetSiteInstance()->GetId());
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(speculative_rfh->GetSiteInstance()->IsDefaultSiteInstance());
+  } else {
+    EXPECT_EQ(kRedirectSiteURL,
+              speculative_rfh->GetSiteInstance()->GetSiteURL());
+    if (AreAllSitesIsolatedForTesting())
+      EXPECT_EQ(site_instance_id, speculative_rfh->GetSiteInstance()->GetId());
+  }
 
   // The user requests to go back again while the previous back hasn't committed
   // yet. This should delete the speculative RenderFrameHost trying to commit
@@ -4823,11 +4828,27 @@
   EXPECT_NE(
       GURL(kUnreachableWebDataURL),
       shell()->web_contents()->GetMainFrame()->GetSiteInstance()->GetSiteURL());
-  EXPECT_EQ(
-      success_site_instance->GetSiteURL(),
-      shell()->web_contents()->GetMainFrame()->GetSiteInstance()->GetSiteURL());
-  EXPECT_NE(success_site_instance,
-            shell()->web_contents()->GetMainFrame()->GetSiteInstance());
+  if (AreDefaultSiteInstancesEnabled()) {
+    // Verify that we get the default SiteInstance because the original URL does
+    // not require a dedicated process.
+    EXPECT_TRUE(static_cast<SiteInstanceImpl*>(
+                    shell()->web_contents()->GetMainFrame()->GetSiteInstance())
+                    ->IsDefaultSiteInstance());
+    // TODO(acolwell): This should be changed to EXPECT_EQ() once the initial
+    // navigation is able to use the default SiteInstance . Right now they
+    // are not equal because the initial navigation converts an empty
+    // SiteInstance into one specifically for the initial URL.
+    EXPECT_NE(success_site_instance,
+              shell()->web_contents()->GetMainFrame()->GetSiteInstance());
+  } else {
+    EXPECT_EQ(success_site_instance->GetSiteURL(), shell()
+                                                       ->web_contents()
+                                                       ->GetMainFrame()
+                                                       ->GetSiteInstance()
+                                                       ->GetSiteURL());
+    EXPECT_NE(success_site_instance,
+              shell()->web_contents()->GetMainFrame()->GetSiteInstance());
+  }
   EXPECT_EQ(3, nav_controller.GetEntryCount());
 
   // Repeat again using a renderer-initiated navigation for the successful one.
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc
index 230cfb76..d7501f3 100644
--- a/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -3220,8 +3220,8 @@
   const GURL kFooUrl("https://foo.com");
   const GURL kOriginalUrl("https://original.com");
   const GURL kTranslatedUrl("https://translated.com");
-  EffectiveURLContentBrowserClient modified_client(kOriginalUrl,
-                                                   kTranslatedUrl);
+  EffectiveURLContentBrowserClient modified_client(
+      kOriginalUrl, kTranslatedUrl, /* requires_dedicated_process */ true);
   ContentBrowserClient* regular_client =
       SetBrowserClientForTesting(&modified_client);
 
diff --git a/content/browser/indexed_db/indexed_db_active_blob_registry_unittest.cc b/content/browser/indexed_db/indexed_db_active_blob_registry_unittest.cc
index c2190ff..8a710e7a 100644
--- a/content/browser/indexed_db/indexed_db_active_blob_registry_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_active_blob_registry_unittest.cc
@@ -69,7 +69,7 @@
   ~MockIDBBackingStore() override = default;
 
   void ReportBlobUnused(int64_t database_id, int64_t blob_key) override {
-    unused_blobs_.insert(std::make_pair(database_id, blob_key));
+    unused_blobs_.insert({database_id, blob_key});
   }
 
   bool CheckUnusedBlobsEmpty() const {
@@ -77,7 +77,7 @@
   }
   bool CheckSingleUnusedBlob(int64_t database_id, int64_t blob_key) const {
     return !duplicate_calls_ && unused_blobs_.size() == 1 &&
-           unused_blobs_.count(std::make_pair(database_id, blob_key));
+           unused_blobs_.count({database_id, blob_key});
   }
 
   const KeyPairSet& unused_blobs() const { return unused_blobs_; }
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc
index 3e2ba42..f725eab1 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store.cc
@@ -232,8 +232,7 @@
   Status s = GetBlobJournal(key, transaction, &journal);
   if (!s.ok())
     return s;
-  journal.push_back(
-      std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
+  journal.push_back({database_id, DatabaseMetaDataKey::kAllBlobsKey});
   UpdateBlobJournal(transaction, key, journal);
   return Status::OK();
 }
@@ -656,12 +655,8 @@
       indexed_db::ReportV2Schema(has_blobs, origin_);
       if (has_blobs) {
         INTERNAL_CONSISTENCY_ERROR(UPGRADING_SCHEMA_CORRUPTED_BLOBS);
-        // Put database wiping behind a flag so we can use finch to stop this
-        // behavior if first-party customers have problems.
-        if (base::FeatureList::IsEnabled(
-                features::kWipeCorruptV2IDBDatabases)) {
+        if (origin_.host() != "docs.google.com")
           return InternalInconsistencyStatus();
-        }
       } else {
         PutInt(transaction.get(), schema_version_key, db_schema_version);
       }
@@ -1616,8 +1611,7 @@
     if (current_database_id == database_id &&
         (all_blobs || current_all_blobs || blob_key == current_blob_key)) {
       if (!all_blobs) {
-        primary_journal.push_back(
-            std::make_pair(database_id, current_blob_key));
+        primary_journal.push_back({database_id, current_blob_key});
         if (current_all_blobs)
           new_live_blob_journal.push_back(*journal_iter);
         new_live_blob_journal.insert(new_live_blob_journal.end(),
@@ -1630,8 +1624,7 @@
     }
   }
   if (all_blobs) {
-    primary_journal.push_back(
-        std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
+    primary_journal.push_back({database_id, DatabaseMetaDataKey::kAllBlobsKey});
   }
   UpdatePrimaryBlobJournal(transaction.get(), primary_journal);
   UpdateLiveBlobJournal(transaction.get(), new_live_blob_journal);
@@ -2863,7 +2856,7 @@
           pre_transaction.get(), database_id_, &next_blob_key);
       if (!result || next_blob_key < 0)
         return InternalInconsistencyStatus();
-      blobs_to_write_.push_back(std::make_pair(database_id_, next_blob_key));
+      blobs_to_write_.push_back({database_id_, next_blob_key});
       if (entry.is_file() && !entry.file_path().empty()) {
         new_files_to_write->push_back(
             WriteDescriptor(entry.file_path(), next_blob_key, entry.size(),
@@ -2887,7 +2880,7 @@
       return InternalInconsistencyStatus();
     }
     new_blob_entries->push_back(
-        std::make_pair(blob_entry_key, EncodeBlobData(new_blob_keys)));
+        {blob_entry_key, EncodeBlobData(new_blob_keys)});
   }
 
   AppendBlobsToPrimaryBlobJournal(pre_transaction.get(), blobs_to_write_);
@@ -2930,7 +2923,7 @@
         return false;
       }
       for (const auto& blob : blob_info) {
-        blobs_to_remove_.push_back(std::make_pair(database_id_, blob.key()));
+        blobs_to_remove_.push_back({database_id_, blob.key()});
         transaction_->Remove(blob_entry_key_bytes);
       }
     }
diff --git a/content/browser/indexed_db/indexed_db_context_impl.cc b/content/browser/indexed_db/indexed_db_context_impl.cc
index 635be69..96de4a6e 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.cc
+++ b/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -424,9 +424,8 @@
 
 std::vector<base::FilePath> IndexedDBContextImpl::GetStoragePaths(
     const Origin& origin) const {
-  std::vector<base::FilePath> paths;
-  paths.push_back(GetLevelDBPath(origin));
-  paths.push_back(GetBlobStorePath(origin));
+  std::vector<base::FilePath> paths = {GetLevelDBPath(origin),
+                                       GetBlobStorePath(origin)};
   return paths;
 }
 
diff --git a/content/browser/indexed_db/indexed_db_leveldb_coding.cc b/content/browser/indexed_db/indexed_db_leveldb_coding.cc
index 7951ea2..3deaf0a 100644
--- a/content/browser/indexed_db/indexed_db_leveldb_coding.cc
+++ b/content/browser/indexed_db/indexed_db_leveldb_coding.cc
@@ -507,7 +507,7 @@
         (blob_key != DatabaseMetaDataKey::kAllBlobsKey)) {
       return false;
     }
-    output.push_back(std::make_pair(database_id, blob_key));
+    output.push_back({database_id, blob_key});
   }
   journal->swap(output);
   return true;
diff --git a/content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc b/content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc
index 859411e..6379d02 100644
--- a/content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc
@@ -31,16 +31,13 @@
 }
 
 static IndexedDBKey CreateArrayIDBKey(const IndexedDBKey& key1) {
-  IndexedDBKey::KeyArray array;
-  array.push_back(key1);
+  IndexedDBKey::KeyArray array = {key1};
   return IndexedDBKey(std::move(array));
 }
 
 static IndexedDBKey CreateArrayIDBKey(const IndexedDBKey& key1,
                                       const IndexedDBKey& key2) {
-  IndexedDBKey::KeyArray array;
-  array.push_back(key1);
-  array.push_back(key2);
+  IndexedDBKey::KeyArray array = {key1, key2};
   return IndexedDBKey(std::move(array));
 }
 
@@ -69,10 +66,7 @@
 }
 
 TEST(IndexedDBLevelDBCodingTest, DecodeByte) {
-  std::vector<unsigned char> test_cases;
-  test_cases.push_back(0);
-  test_cases.push_back(1);
-  test_cases.push_back(255);
+  std::vector<unsigned char> test_cases = {0, 1, 255};
 
   for (size_t i = 0; i < test_cases.size(); ++i) {
     unsigned char n = test_cases[i];
@@ -219,18 +213,19 @@
 }
 
 TEST(IndexedDBLevelDBCodingTest, DecodeInt) {
-  std::vector<int64_t> test_cases;
-  test_cases.push_back(0);
-  test_cases.push_back(1);
-  test_cases.push_back(255);
-  test_cases.push_back(256);
-  test_cases.push_back(65535);
-  test_cases.push_back(655536);
-  test_cases.push_back(7711192431755665792ll);
-  test_cases.push_back(0x7fffffffffffffffll);
+  std::vector<int64_t> test_cases = {
+      0,
+      1,
+      255,
+      256,
+      65535,
+      655536,
+      7711192431755665792ll,
+      0x7fffffffffffffffll,
 #ifdef NDEBUG
-  test_cases.push_back(-3);
+      -3,
 #endif
+  };
 
   for (size_t i = 0; i < test_cases.size(); ++i) {
     int64_t n = test_cases[i];
@@ -276,18 +271,19 @@
 }
 
 TEST(IndexedDBLevelDBCodingTest, DecodeVarInt) {
-  std::vector<int64_t> test_cases;
-  test_cases.push_back(0);
-  test_cases.push_back(1);
-  test_cases.push_back(255);
-  test_cases.push_back(256);
-  test_cases.push_back(65535);
-  test_cases.push_back(655536);
-  test_cases.push_back(7711192431755665792ll);
-  test_cases.push_back(0x7fffffffffffffffll);
+  std::vector<int64_t> test_cases = {
+      0,
+      1,
+      255,
+      256,
+      65535,
+      655536,
+      7711192431755665792ll,
+      0x7fffffffffffffffll,
 #ifdef NDEBUG
-  test_cases.push_back(-3);
+      -3,
 #endif
+  };
 
   for (size_t i = 0; i < test_cases.size(); ++i) {
     int64_t n = test_cases[i];
@@ -335,12 +331,9 @@
   const base::char16 test_string_a[] = {'f', 'o', 'o', '\0'};
   const base::char16 test_string_b[] = {0xdead, 0xbeef, '\0'};
 
-  std::vector<base::string16> test_cases;
-  test_cases.push_back(base::string16());
-  test_cases.push_back(ASCIIToUTF16("a"));
-  test_cases.push_back(ASCIIToUTF16("foo"));
-  test_cases.push_back(test_string_a);
-  test_cases.push_back(test_string_b);
+  std::vector<base::string16> test_cases = {base::string16(), ASCIIToUTF16("a"),
+                                            ASCIIToUTF16("foo"), test_string_a,
+                                            test_string_b};
 
   for (size_t i = 0; i < test_cases.size(); ++i) {
     const base::string16& test_case = test_cases[i];
@@ -393,13 +386,12 @@
     long_string[i] = i;
   long_string[kLongStringLen] = 0;
 
-  std::vector<base::string16> test_cases;
-  test_cases.push_back(ASCIIToUTF16(""));
-  test_cases.push_back(ASCIIToUTF16("a"));
-  test_cases.push_back(ASCIIToUTF16("foo"));
-  test_cases.push_back(base::string16(test_string_a));
-  test_cases.push_back(base::string16(test_string_b));
-  test_cases.push_back(base::string16(long_string));
+  std::vector<base::string16> test_cases = {ASCIIToUTF16(""),
+                                            ASCIIToUTF16("a"),
+                                            ASCIIToUTF16("foo"),
+                                            base::string16(test_string_a),
+                                            base::string16(test_string_b),
+                                            base::string16(long_string)};
 
   for (size_t i = 0; i < test_cases.size(); ++i) {
     base::string16 s = test_cases[i];
@@ -447,19 +439,20 @@
   const base::char16 test_string_e[] = {0xd834, 0xdd1e, '\0'};
   const base::char16 test_string_f[] = {0xfffd, '\0'};
 
-  std::vector<base::string16> test_cases;
-  test_cases.push_back(ASCIIToUTF16(""));
-  test_cases.push_back(ASCIIToUTF16("a"));
-  test_cases.push_back(ASCIIToUTF16("b"));
-  test_cases.push_back(ASCIIToUTF16("baaa"));
-  test_cases.push_back(ASCIIToUTF16("baab"));
-  test_cases.push_back(ASCIIToUTF16("c"));
-  test_cases.push_back(base::string16(test_string_a));
-  test_cases.push_back(base::string16(test_string_b));
-  test_cases.push_back(base::string16(test_string_c));
-  test_cases.push_back(base::string16(test_string_d));
-  test_cases.push_back(base::string16(test_string_e));
-  test_cases.push_back(base::string16(test_string_f));
+  std::vector<base::string16> test_cases = {
+      ASCIIToUTF16(""),
+      ASCIIToUTF16("a"),
+      ASCIIToUTF16("b"),
+      ASCIIToUTF16("baaa"),
+      ASCIIToUTF16("baab"),
+      ASCIIToUTF16("c"),
+      base::string16(test_string_a),
+      base::string16(test_string_b),
+      base::string16(test_string_c),
+      base::string16(test_string_d),
+      base::string16(test_string_e),
+      base::string16(test_string_f),
+  };
 
   for (size_t i = 0; i < test_cases.size() - 1; ++i) {
     base::string16 a = test_cases[i];
@@ -504,10 +497,10 @@
 TEST(IndexedDBLevelDBCodingTest, DecodeBinary) {
   const unsigned char binary_data[] = { 0x00, 0x01, 0xfe, 0xff };
 
-  std::vector<std::string> test_cases;
-  test_cases.push_back(std::string(binary_data, binary_data + 0));
-  test_cases.push_back(std::string(binary_data, binary_data + 1));
-  test_cases.push_back(std::string(binary_data, binary_data + 4));
+  std::vector<std::string> test_cases = {
+      std::string(binary_data, binary_data + 0),
+      std::string(binary_data, binary_data + 1),
+      std::string(binary_data, binary_data + 4)};
 
   for (size_t i = 0; i < test_cases.size(); ++i) {
     std::string value = test_cases[i];
@@ -546,9 +539,7 @@
 }
 
 TEST(IndexedDBLevelDBCodingTest, DecodeDouble) {
-  std::vector<double> test_cases;
-  test_cases.push_back(3.14);
-  test_cases.push_back(-3.14);
+  std::vector<double> test_cases = {3.14, -3.14};
 
   for (size_t i = 0; i < test_cases.size(); ++i) {
     double value = test_cases[i];
@@ -581,19 +572,19 @@
   std::string v;
   StringPiece slice;
 
-  std::vector<IndexedDBKey> test_cases;
-  test_cases.push_back(IndexedDBKey(1234, blink::mojom::IDBKeyType::Number));
-  test_cases.push_back(IndexedDBKey(7890, blink::mojom::IDBKeyType::Date));
-  test_cases.push_back(IndexedDBKey(ASCIIToUTF16("Hello World!")));
-  test_cases.push_back(IndexedDBKey(std::string("\x01\x02")));
-  test_cases.push_back(IndexedDBKey(IndexedDBKey::KeyArray()));
+  std::vector<IndexedDBKey> test_cases = {
+      IndexedDBKey(1234, blink::mojom::IDBKeyType::Number),
+      IndexedDBKey(7890, blink::mojom::IDBKeyType::Date),
+      IndexedDBKey(ASCIIToUTF16("Hello World!")),
+      IndexedDBKey(std::string("\x01\x02")),
+      IndexedDBKey(IndexedDBKey::KeyArray())};
 
-  IndexedDBKey::KeyArray array;
-  array.push_back(IndexedDBKey(1234, blink::mojom::IDBKeyType::Number));
-  array.push_back(IndexedDBKey(7890, blink::mojom::IDBKeyType::Date));
-  array.push_back(IndexedDBKey(ASCIIToUTF16("Hello World!")));
-  array.push_back(IndexedDBKey(std::string("\x01\x02")));
-  array.push_back(IndexedDBKey(IndexedDBKey::KeyArray()));
+  IndexedDBKey::KeyArray array = {
+      IndexedDBKey(1234, blink::mojom::IDBKeyType::Number),
+      IndexedDBKey(7890, blink::mojom::IDBKeyType::Date),
+      IndexedDBKey(ASCIIToUTF16("Hello World!")),
+      IndexedDBKey(std::string("\x01\x02")),
+      IndexedDBKey(IndexedDBKey::KeyArray())};
   test_cases.push_back(IndexedDBKey(std::move(array)));
 
   for (size_t i = 0; i < test_cases.size(); ++i) {
@@ -664,10 +655,8 @@
   }
 
   {
-    std::vector<base::string16> array;
-    array.push_back(base::string16());
-    array.push_back(ASCIIToUTF16("foo"));
-    array.push_back(ASCIIToUTF16("foo.bar"));
+    std::vector<base::string16> array = {base::string16(), ASCIIToUTF16("foo"),
+                                         ASCIIToUTF16("foo.bar")};
 
     key_paths.push_back(IndexedDBKeyPath(array));
     char expected[] = {0, 0,                       // Header
@@ -704,32 +693,21 @@
   std::vector<BlobJournalType> journals;
 
   {  // Empty journal
-    BlobJournalType journal;
-    journals.push_back(journal);
+    journals.push_back({});
   }
 
   {  // One item
-    BlobJournalType journal;
-    journal.push_back(std::make_pair(4, 7));
-    journals.push_back(journal);
+    journals.push_back({{4, 7}});
   }
 
   {  // kAllBlobsKey
-    BlobJournalType journal;
-    journal.push_back(std::make_pair(5, DatabaseMetaDataKey::kAllBlobsKey));
-    journals.push_back(journal);
+    journals.push_back({{5, DatabaseMetaDataKey::kAllBlobsKey}});
   }
 
   {  // A bunch of items
-    BlobJournalType journal;
-    journal.push_back(std::make_pair(4, 7));
-    journal.push_back(std::make_pair(5, 6));
-    journal.push_back(std::make_pair(4, 5));
-    journal.push_back(std::make_pair(4, 4));
-    journal.push_back(std::make_pair(1, 12));
-    journal.push_back(std::make_pair(4, 3));
-    journal.push_back(std::make_pair(15, 14));
-    journals.push_back(journal);
+
+    journals.push_back(
+        {{4, 7}, {5, 6}, {4, 5}, {4, 4}, {1, 12}, {4, 3}, {15, 14}});
   }
 
   for (const auto& journal_iter : journals) {
@@ -744,15 +722,11 @@
   journals.clear();
 
   {  // Illegal database id
-    BlobJournalType journal;
-    journal.push_back(std::make_pair(0, 3));
-    journals.push_back(journal);
+    journals.push_back({{0, 3}});
   }
 
   {  // Illegal blob id
-    BlobJournalType journal;
-    journal.push_back(std::make_pair(4, 0));
-    journals.push_back(journal);
+    journals.push_back({{4, 0}});
   }
 
   for (const auto& journal_iter : journals) {
@@ -798,51 +772,52 @@
 }
 
 TEST(IndexedDBLevelDBCodingTest, ExtractAndCompareIDBKeys) {
-  std::vector<IndexedDBKey> keys;
+  std::vector<IndexedDBKey> keys = {
 
-  keys.push_back(IndexedDBKey(-10, blink::mojom::IDBKeyType::Number));
-  keys.push_back(IndexedDBKey(0, blink::mojom::IDBKeyType::Number));
-  keys.push_back(IndexedDBKey(3.14, blink::mojom::IDBKeyType::Number));
+      IndexedDBKey(-10, blink::mojom::IDBKeyType::Number),
+      IndexedDBKey(0, blink::mojom::IDBKeyType::Number),
+      IndexedDBKey(3.14, blink::mojom::IDBKeyType::Number),
 
-  keys.push_back(IndexedDBKey(0, blink::mojom::IDBKeyType::Date));
-  keys.push_back(IndexedDBKey(100, blink::mojom::IDBKeyType::Date));
-  keys.push_back(IndexedDBKey(100000, blink::mojom::IDBKeyType::Date));
+      IndexedDBKey(0, blink::mojom::IDBKeyType::Date),
+      IndexedDBKey(100, blink::mojom::IDBKeyType::Date),
+      IndexedDBKey(100000, blink::mojom::IDBKeyType::Date),
 
-  keys.push_back(IndexedDBKey(ASCIIToUTF16("")));
-  keys.push_back(IndexedDBKey(ASCIIToUTF16("a")));
-  keys.push_back(IndexedDBKey(ASCIIToUTF16("b")));
-  keys.push_back(IndexedDBKey(ASCIIToUTF16("baaa")));
-  keys.push_back(IndexedDBKey(ASCIIToUTF16("baab")));
-  keys.push_back(IndexedDBKey(ASCIIToUTF16("c")));
+      IndexedDBKey(ASCIIToUTF16("")),
+      IndexedDBKey(ASCIIToUTF16("a")),
+      IndexedDBKey(ASCIIToUTF16("b")),
+      IndexedDBKey(ASCIIToUTF16("baaa")),
+      IndexedDBKey(ASCIIToUTF16("baab")),
+      IndexedDBKey(ASCIIToUTF16("c")),
 
-  keys.push_back(IndexedDBKey(std::string()));
-  keys.push_back(IndexedDBKey(std::string("\x01")));
-  keys.push_back(IndexedDBKey(std::string("\x01\x01")));
-  keys.push_back(IndexedDBKey(std::string("\x01\x02")));
-  keys.push_back(IndexedDBKey(std::string("\x02")));
-  keys.push_back(IndexedDBKey(std::string("\x02\x01")));
-  keys.push_back(IndexedDBKey(std::string("\x02\x02")));
-  keys.push_back(IndexedDBKey(std::string("\xff")));
+      IndexedDBKey(std::string()),
+      IndexedDBKey(std::string("\x01")),
+      IndexedDBKey(std::string("\x01\x01")),
+      IndexedDBKey(std::string("\x01\x02")),
+      IndexedDBKey(std::string("\x02")),
+      IndexedDBKey(std::string("\x02\x01")),
+      IndexedDBKey(std::string("\x02\x02")),
+      IndexedDBKey(std::string("\xff")),
 
-  keys.push_back(CreateArrayIDBKey());
-  keys.push_back(
-      CreateArrayIDBKey(IndexedDBKey(0, blink::mojom::IDBKeyType::Number)));
-  keys.push_back(
+      CreateArrayIDBKey(),
+
+      CreateArrayIDBKey(IndexedDBKey(0, blink::mojom::IDBKeyType::Number)),
+
       CreateArrayIDBKey(IndexedDBKey(0, blink::mojom::IDBKeyType::Number),
-                        IndexedDBKey(3.14, blink::mojom::IDBKeyType::Number)));
-  keys.push_back(
-      CreateArrayIDBKey(IndexedDBKey(0, blink::mojom::IDBKeyType::Date)));
-  keys.push_back(
+                        IndexedDBKey(3.14, blink::mojom::IDBKeyType::Number)),
+
+      CreateArrayIDBKey(IndexedDBKey(0, blink::mojom::IDBKeyType::Date)),
+
       CreateArrayIDBKey(IndexedDBKey(0, blink::mojom::IDBKeyType::Date),
-                        IndexedDBKey(0, blink::mojom::IDBKeyType::Date)));
-  keys.push_back(CreateArrayIDBKey(IndexedDBKey(ASCIIToUTF16(""))));
-  keys.push_back(CreateArrayIDBKey(IndexedDBKey(ASCIIToUTF16("")),
-                                   IndexedDBKey(ASCIIToUTF16("a"))));
-  keys.push_back(CreateArrayIDBKey(CreateArrayIDBKey()));
-  keys.push_back(CreateArrayIDBKey(CreateArrayIDBKey(), CreateArrayIDBKey()));
-  keys.push_back(CreateArrayIDBKey(CreateArrayIDBKey(CreateArrayIDBKey())));
-  keys.push_back(CreateArrayIDBKey(
-      CreateArrayIDBKey(CreateArrayIDBKey(CreateArrayIDBKey()))));
+                        IndexedDBKey(0, blink::mojom::IDBKeyType::Date)),
+      CreateArrayIDBKey(IndexedDBKey(ASCIIToUTF16(""))),
+      CreateArrayIDBKey(IndexedDBKey(ASCIIToUTF16("")),
+                        IndexedDBKey(ASCIIToUTF16("a"))),
+      CreateArrayIDBKey(CreateArrayIDBKey()),
+      CreateArrayIDBKey(CreateArrayIDBKey(), CreateArrayIDBKey()),
+      CreateArrayIDBKey(CreateArrayIDBKey(CreateArrayIDBKey())),
+      CreateArrayIDBKey(
+          CreateArrayIDBKey(CreateArrayIDBKey(CreateArrayIDBKey()))),
+  };
 
   for (size_t i = 0; i < keys.size() - 1; ++i) {
     const IndexedDBKey& key_a = keys[i];
@@ -882,85 +857,85 @@
 }
 
 TEST(IndexedDBLevelDBCodingTest, ComparisonTest) {
-  std::vector<std::string> keys;
-  keys.push_back(SchemaVersionKey::Encode());
-  keys.push_back(MaxDatabaseIdKey::Encode());
-  keys.push_back(DatabaseFreeListKey::Encode(0));
-  keys.push_back(DatabaseFreeListKey::EncodeMaxKey());
-  keys.push_back(DatabaseNameKey::Encode("", ASCIIToUTF16("")));
-  keys.push_back(DatabaseNameKey::Encode("", ASCIIToUTF16("a")));
-  keys.push_back(DatabaseNameKey::Encode("a", ASCIIToUTF16("a")));
-  keys.push_back(
-      DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::ORIGIN_NAME));
-  keys.push_back(
-      DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::DATABASE_NAME));
-  keys.push_back(
-      DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::USER_STRING_VERSION));
-  keys.push_back(
-      DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID));
-  keys.push_back(
-      DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::USER_VERSION));
-  keys.push_back(
-      ObjectStoreMetaDataKey::Encode(1, 1, ObjectStoreMetaDataKey::NAME));
-  keys.push_back(
-      ObjectStoreMetaDataKey::Encode(1, 1, ObjectStoreMetaDataKey::KEY_PATH));
-  keys.push_back(ObjectStoreMetaDataKey::Encode(
-      1, 1, ObjectStoreMetaDataKey::AUTO_INCREMENT));
-  keys.push_back(
-      ObjectStoreMetaDataKey::Encode(1, 1, ObjectStoreMetaDataKey::EVICTABLE));
-  keys.push_back(ObjectStoreMetaDataKey::Encode(
-      1, 1, ObjectStoreMetaDataKey::LAST_VERSION));
-  keys.push_back(ObjectStoreMetaDataKey::Encode(
-      1, 1, ObjectStoreMetaDataKey::MAX_INDEX_ID));
-  keys.push_back(ObjectStoreMetaDataKey::Encode(
-      1, 1, ObjectStoreMetaDataKey::HAS_KEY_PATH));
-  keys.push_back(ObjectStoreMetaDataKey::Encode(
-      1, 1, ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER));
-  keys.push_back(ObjectStoreMetaDataKey::EncodeMaxKey(1, 1));
-  keys.push_back(ObjectStoreMetaDataKey::EncodeMaxKey(1, 2));
-  keys.push_back(ObjectStoreMetaDataKey::EncodeMaxKey(1));
-  keys.push_back(IndexMetaDataKey::Encode(1, 1, 30, IndexMetaDataKey::NAME));
-  keys.push_back(IndexMetaDataKey::Encode(1, 1, 30, IndexMetaDataKey::UNIQUE));
-  keys.push_back(
-      IndexMetaDataKey::Encode(1, 1, 30, IndexMetaDataKey::KEY_PATH));
-  keys.push_back(
-      IndexMetaDataKey::Encode(1, 1, 30, IndexMetaDataKey::MULTI_ENTRY));
-  keys.push_back(IndexMetaDataKey::Encode(1, 1, 31, 0));
-  keys.push_back(IndexMetaDataKey::Encode(1, 1, 31, 1));
-  keys.push_back(IndexMetaDataKey::EncodeMaxKey(1, 1, 31));
-  keys.push_back(IndexMetaDataKey::EncodeMaxKey(1, 1, 32));
-  keys.push_back(IndexMetaDataKey::EncodeMaxKey(1, 1));
-  keys.push_back(IndexMetaDataKey::EncodeMaxKey(1, 2));
-  keys.push_back(ObjectStoreFreeListKey::Encode(1, 1));
-  keys.push_back(ObjectStoreFreeListKey::EncodeMaxKey(1));
-  keys.push_back(IndexFreeListKey::Encode(1, 1, kMinimumIndexId));
-  keys.push_back(IndexFreeListKey::EncodeMaxKey(1, 1));
-  keys.push_back(IndexFreeListKey::Encode(1, 2, kMinimumIndexId));
-  keys.push_back(IndexFreeListKey::EncodeMaxKey(1, 2));
-  keys.push_back(ObjectStoreNamesKey::Encode(1, ASCIIToUTF16("")));
-  keys.push_back(ObjectStoreNamesKey::Encode(1, ASCIIToUTF16("a")));
-  keys.push_back(IndexNamesKey::Encode(1, 1, ASCIIToUTF16("")));
-  keys.push_back(IndexNamesKey::Encode(1, 1, ASCIIToUTF16("a")));
-  keys.push_back(IndexNamesKey::Encode(1, 2, ASCIIToUTF16("a")));
-  keys.push_back(ObjectStoreDataKey::Encode(1, 1, std::string()));
-  keys.push_back(ObjectStoreDataKey::Encode(1, 1, MinIDBKey()));
-  keys.push_back(ObjectStoreDataKey::Encode(1, 1, MaxIDBKey()));
-  keys.push_back(ExistsEntryKey::Encode(1, 1, std::string()));
-  keys.push_back(ExistsEntryKey::Encode(1, 1, MinIDBKey()));
-  keys.push_back(ExistsEntryKey::Encode(1, 1, MaxIDBKey()));
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), std::string(), 0));
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MinIDBKey(), 0));
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MinIDBKey(), 1));
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MaxIDBKey(), 0));
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MaxIDBKey(), 1));
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MinIDBKey(), 0));
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MinIDBKey(), 1));
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MaxIDBKey(), 0));
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MaxIDBKey(), 1));
-  keys.push_back(IndexDataKey::Encode(1, 1, 31, MinIDBKey(), MinIDBKey(), 0));
-  keys.push_back(IndexDataKey::Encode(1, 2, 30, MinIDBKey(), MinIDBKey(), 0));
-  keys.push_back(IndexDataKey::EncodeMaxKey(
-      1, 2, std::numeric_limits<int32_t>::max() - 1));
+  std::vector<std::string> keys = {
+      SchemaVersionKey::Encode(),
+      MaxDatabaseIdKey::Encode(),
+      DatabaseFreeListKey::Encode(0),
+      DatabaseFreeListKey::EncodeMaxKey(),
+      DatabaseNameKey::Encode("", ASCIIToUTF16("")),
+      DatabaseNameKey::Encode("", ASCIIToUTF16("a")),
+      DatabaseNameKey::Encode("a", ASCIIToUTF16("a")),
+
+      DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::ORIGIN_NAME),
+
+      DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::DATABASE_NAME),
+
+      DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::USER_STRING_VERSION),
+
+      DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID),
+
+      DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::USER_VERSION),
+
+      ObjectStoreMetaDataKey::Encode(1, 1, ObjectStoreMetaDataKey::NAME),
+
+      ObjectStoreMetaDataKey::Encode(1, 1, ObjectStoreMetaDataKey::KEY_PATH),
+      ObjectStoreMetaDataKey::Encode(1, 1,
+                                     ObjectStoreMetaDataKey::AUTO_INCREMENT),
+
+      ObjectStoreMetaDataKey::Encode(1, 1, ObjectStoreMetaDataKey::EVICTABLE),
+      ObjectStoreMetaDataKey::Encode(1, 1,
+                                     ObjectStoreMetaDataKey::LAST_VERSION),
+      ObjectStoreMetaDataKey::Encode(1, 1,
+                                     ObjectStoreMetaDataKey::MAX_INDEX_ID),
+      ObjectStoreMetaDataKey::Encode(1, 1,
+                                     ObjectStoreMetaDataKey::HAS_KEY_PATH),
+      ObjectStoreMetaDataKey::Encode(
+          1, 1, ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER),
+      ObjectStoreMetaDataKey::EncodeMaxKey(1, 1),
+      ObjectStoreMetaDataKey::EncodeMaxKey(1, 2),
+      ObjectStoreMetaDataKey::EncodeMaxKey(1),
+      IndexMetaDataKey::Encode(1, 1, 30, IndexMetaDataKey::NAME),
+      IndexMetaDataKey::Encode(1, 1, 30, IndexMetaDataKey::UNIQUE),
+
+      IndexMetaDataKey::Encode(1, 1, 30, IndexMetaDataKey::KEY_PATH),
+
+      IndexMetaDataKey::Encode(1, 1, 30, IndexMetaDataKey::MULTI_ENTRY),
+      IndexMetaDataKey::Encode(1, 1, 31, 0),
+      IndexMetaDataKey::Encode(1, 1, 31, 1),
+      IndexMetaDataKey::EncodeMaxKey(1, 1, 31),
+      IndexMetaDataKey::EncodeMaxKey(1, 1, 32),
+      IndexMetaDataKey::EncodeMaxKey(1, 1),
+      IndexMetaDataKey::EncodeMaxKey(1, 2),
+      ObjectStoreFreeListKey::Encode(1, 1),
+      ObjectStoreFreeListKey::EncodeMaxKey(1),
+      IndexFreeListKey::Encode(1, 1, kMinimumIndexId),
+      IndexFreeListKey::EncodeMaxKey(1, 1),
+      IndexFreeListKey::Encode(1, 2, kMinimumIndexId),
+      IndexFreeListKey::EncodeMaxKey(1, 2),
+      ObjectStoreNamesKey::Encode(1, ASCIIToUTF16("")),
+      ObjectStoreNamesKey::Encode(1, ASCIIToUTF16("a")),
+      IndexNamesKey::Encode(1, 1, ASCIIToUTF16("")),
+      IndexNamesKey::Encode(1, 1, ASCIIToUTF16("a")),
+      IndexNamesKey::Encode(1, 2, ASCIIToUTF16("a")),
+      ObjectStoreDataKey::Encode(1, 1, std::string()),
+      ObjectStoreDataKey::Encode(1, 1, MinIDBKey()),
+      ObjectStoreDataKey::Encode(1, 1, MaxIDBKey()),
+      ExistsEntryKey::Encode(1, 1, std::string()),
+      ExistsEntryKey::Encode(1, 1, MinIDBKey()),
+      ExistsEntryKey::Encode(1, 1, MaxIDBKey()),
+      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), std::string(), 0),
+      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MinIDBKey(), 0),
+      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MinIDBKey(), 1),
+      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MaxIDBKey(), 0),
+      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MaxIDBKey(), 1),
+      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MinIDBKey(), 0),
+      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MinIDBKey(), 1),
+      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MaxIDBKey(), 0),
+      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MaxIDBKey(), 1),
+      IndexDataKey::Encode(1, 1, 31, MinIDBKey(), MinIDBKey(), 0),
+      IndexDataKey::Encode(1, 2, 30, MinIDBKey(), MinIDBKey(), 0),
+      IndexDataKey::EncodeMaxKey(1, 2, std::numeric_limits<int32_t>::max() - 1),
+  };
 
   for (size_t i = 0; i < keys.size(); ++i) {
     EXPECT_EQ(Compare(keys[i], keys[i], false), 0);
@@ -973,22 +948,22 @@
 }
 
 TEST(IndexedDBLevelDBCodingTest, IndexDataKeyEncodeDecode) {
-  std::vector<std::string> keys;
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MinIDBKey(), 0));
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MinIDBKey(), 1));
-  keys.push_back(
+  std::vector<std::string> keys = {
+      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MinIDBKey(), 0),
+      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MinIDBKey(), 1),
+
       IndexDataKey::Encode(1, 1, 30, IndexedDBKey(ASCIIToUTF16("user key")),
-                           IndexedDBKey(ASCIIToUTF16("primary key"))));
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MaxIDBKey(), 0));
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MaxIDBKey(), 1));
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MinIDBKey(), 0));
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MinIDBKey(), 1));
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MaxIDBKey(), 0));
-  keys.push_back(IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MaxIDBKey(), 1));
-  keys.push_back(IndexDataKey::Encode(1, 1, 31, MinIDBKey(), MinIDBKey(), 0));
-  keys.push_back(IndexDataKey::Encode(1, 2, 30, MinIDBKey(), MinIDBKey(), 0));
-  keys.push_back(IndexDataKey::EncodeMaxKey(
-      1, 2, std::numeric_limits<int32_t>::max() - 1));
+                           IndexedDBKey(ASCIIToUTF16("primary key"))),
+      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MaxIDBKey(), 0),
+      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MaxIDBKey(), 1),
+      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MinIDBKey(), 0),
+      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MinIDBKey(), 1),
+      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MaxIDBKey(), 0),
+      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MaxIDBKey(), 1),
+      IndexDataKey::Encode(1, 1, 31, MinIDBKey(), MinIDBKey(), 0),
+      IndexDataKey::Encode(1, 2, 30, MinIDBKey(), MinIDBKey(), 0),
+      IndexDataKey::EncodeMaxKey(1, 2, std::numeric_limits<int32_t>::max() - 1),
+  };
 
   std::vector<IndexDataKey> obj_keys;
   for (const std::string& key : keys) {
@@ -1004,10 +979,7 @@
 }
 
 TEST(IndexedDBLevelDBCodingTest, EncodeVarIntVSEncodeByteTest) {
-  std::vector<unsigned char> test_cases;
-  test_cases.push_back(0);
-  test_cases.push_back(1);
-  test_cases.push_back(127);
+  std::vector<unsigned char> test_cases = {0, 1, 127};
 
   for (size_t i = 0; i < test_cases.size(); ++i) {
     unsigned char n = test_cases[i];
diff --git a/content/browser/indexed_db/indexed_db_origin_state.cc b/content/browser/indexed_db/indexed_db_origin_state.cc
index 02cb72b..27ad7cf 100644
--- a/content/browser/indexed_db/indexed_db_origin_state.cc
+++ b/content/browser/indexed_db/indexed_db_origin_state.cc
@@ -113,10 +113,21 @@
     auto it = databases_.find(origin);
     if (it == databases_.end())
       continue;
-    for (IndexedDBConnection* connection : it->second->connections()) {
-      connection->FinishAllTransactions(
-          IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionUnknownError,
-                                 "Aborting all transactions for the origin."));
+
+    // Calling FinishAllTransactions can destruct the IndexedDBConnection &
+    // modify the IndexedDBDatabase::connection() list. To prevent UAFs, start
+    // by taking a WeakPtr of all connections, and then iterate that list.
+    std::vector<base::WeakPtr<IndexedDBConnection>> weak_connections;
+    weak_connections.reserve(it->second->connections().size());
+    for (IndexedDBConnection* connection : it->second->connections())
+      weak_connections.push_back(connection->GetWeakPtr());
+
+    for (base::WeakPtr<IndexedDBConnection> connection : weak_connections) {
+      if (connection) {
+        connection->FinishAllTransactions(IndexedDBDatabaseError(
+            blink::kWebIDBDatabaseExceptionUnknownError,
+            "Aborting all transactions for the origin."));
+      }
     }
   }
   if (compact)
diff --git a/content/browser/indexed_db/indexed_db_transaction.cc b/content/browser/indexed_db/indexed_db_transaction.cc
index 3d4d112..8abc24a 100644
--- a/content/browser/indexed_db/indexed_db_transaction.cc
+++ b/content/browser/indexed_db/indexed_db_transaction.cc
@@ -611,8 +611,7 @@
   auto it = connection_changes_map_.find(connection_id);
   if (it == connection_changes_map_.end()) {
     it = connection_changes_map_
-             .insert(std::make_pair(connection_id,
-                                    blink::mojom::IDBObserverChanges::New()))
+             .insert({connection_id, blink::mojom::IDBObserverChanges::New()})
              .first;
   }
   it->second->observations.push_back(std::move(observation));
diff --git a/content/browser/indexed_db/leveldb/fake_leveldb_factory.cc b/content/browser/indexed_db/leveldb/fake_leveldb_factory.cc
index eb1a29c..c6880ce 100644
--- a/content/browser/indexed_db/leveldb/fake_leveldb_factory.cc
+++ b/content/browser/indexed_db/leveldb/fake_leveldb_factory.cc
@@ -377,7 +377,7 @@
       std::make_unique<BreakOnCallbackDB>(std::move(db));
   base::OnceCallback<void(leveldb::Status)> callback = base::BindOnce(
       &BreakOnCallbackDB::Break, base::Unretained(breakable_db.get()));
-  return std::make_pair(std::move(breakable_db), std::move(callback));
+  return {std::move(breakable_db), std::move(callback)};
 }
 
 // static
diff --git a/content/browser/indexed_db/scopes/leveldb_scope.cc b/content/browser/indexed_db/scopes/leveldb_scope.cc
index 4827e7e7..e45a59b 100644
--- a/content/browser/indexed_db/scopes/leveldb_scope.cc
+++ b/content/browser/indexed_db/scopes/leveldb_scope.cc
@@ -302,12 +302,11 @@
       break;
     default:
       NOTREACHED();
-      return std::make_pair(
-          leveldb::Status::NotSupported("Unknown scopes mode."), mode_);
+      return {leveldb::Status::NotSupported("Unknown scopes mode."), mode_};
   }
   locks_.clear();
   committed_ = true;
-  return std::make_pair(s, mode_);
+  return {s, mode_};
 }
 
 void LevelDBScope::AddUndoPutTask(std::string key, std::string value) {
diff --git a/content/browser/indexed_db/scopes/leveldb_scope_unittest.cc b/content/browser/indexed_db/scopes/leveldb_scope_unittest.cc
index b6712331..a1a8d4fa 100644
--- a/content/browser/indexed_db/scopes/leveldb_scope_unittest.cc
+++ b/content/browser/indexed_db/scopes/leveldb_scope_unittest.cc
@@ -492,8 +492,7 @@
           [&failure_status](leveldb::Status s) { failure_status = s; }));
 
   std::vector<std::pair<std::string, std::string>> empty_ranges = {
-      std::make_pair(CreateKey(0), CreateKey(10)),
-      std::make_pair(CreateKey(30), CreateKey(50))};
+      {CreateKey(0), CreateKey(10)}, {CreateKey(30), CreateKey(50)}};
   leveldb::Status s = scopes.Initialize();
   EXPECT_TRUE(s.ok());
   scopes.StartRecoveryAndCleanupTasks();
diff --git a/content/browser/loader/navigation_loader_interceptor.h b/content/browser/loader/navigation_loader_interceptor.h
index 54aac11..87d3302 100644
--- a/content/browser/loader/navigation_loader_interceptor.h
+++ b/content/browser/loader/navigation_loader_interceptor.h
@@ -10,8 +10,8 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "content/browser/loader/single_request_url_loader_factory.h"
 #include "content/common/content_export.h"
-#include "content/common/single_request_url_loader_factory.h"
 #include "net/url_request/redirect_info.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
diff --git a/content/common/single_request_url_loader_factory.cc b/content/browser/loader/single_request_url_loader_factory.cc
similarity index 97%
rename from content/common/single_request_url_loader_factory.cc
rename to content/browser/loader/single_request_url_loader_factory.cc
index c1160d85..22d6ab4e 100644
--- a/content/common/single_request_url_loader_factory.cc
+++ b/content/browser/loader/single_request_url_loader_factory.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/common/single_request_url_loader_factory.h"
+#include "content/browser/loader/single_request_url_loader_factory.h"
 
 #include <memory>
 #include <utility>
diff --git a/content/common/single_request_url_loader_factory.h b/content/browser/loader/single_request_url_loader_factory.h
similarity index 89%
rename from content/common/single_request_url_loader_factory.h
rename to content/browser/loader/single_request_url_loader_factory.h
index 41e8689..dcf8336b 100644
--- a/content/common/single_request_url_loader_factory.h
+++ b/content/browser/loader/single_request_url_loader_factory.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_COMMON_SINGLE_REQUEST_URL_LOADER_FACTORY_H_
-#define CONTENT_COMMON_SINGLE_REQUEST_URL_LOADER_FACTORY_H_
+#ifndef CONTENT_BROWSER_LOADER_SINGLE_REQUEST_URL_LOADER_FACTORY_H_
+#define CONTENT_BROWSER_LOADER_SINGLE_REQUEST_URL_LOADER_FACTORY_H_
 
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
@@ -50,4 +50,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_COMMON_SINGLE_REQUEST_URL_LOADER_FACTORY_H_
+#endif  // CONTENT_BROWSER_LOADER_SINGLE_REQUEST_URL_LOADER_FACTORY_H_
diff --git a/content/browser/media/media_devices_permission_checker.cc b/content/browser/media/media_devices_permission_checker.cc
index eb4af5d..24d7727 100644
--- a/content/browser/media/media_devices_permission_checker.cc
+++ b/content/browser/media/media_devices_permission_checker.cc
@@ -40,7 +40,7 @@
   RenderFrameHostDelegate* delegate = frame_host->delegate();
   url::Origin origin = frame_host->GetLastCommittedOrigin();
   bool audio_permission = delegate->CheckMediaAccessPermission(
-      frame_host, origin, blink::MEDIA_DEVICE_AUDIO_CAPTURE);
+      frame_host, origin, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE);
   bool mic_feature_policy = true;
   bool camera_feature_policy = true;
   mic_feature_policy = frame_host->IsFeatureEnabled(
@@ -64,8 +64,9 @@
   // Camera.
   result[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] =
       requested_device_types[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] &&
-      delegate->CheckMediaAccessPermission(frame_host, origin,
-                                           blink::MEDIA_DEVICE_VIDEO_CAPTURE) &&
+      delegate->CheckMediaAccessPermission(
+          frame_host, origin,
+          blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) &&
       camera_feature_policy;
 
   return result;
diff --git a/content/browser/media/media_devices_permission_checker_unittest.cc b/content/browser/media/media_devices_permission_checker_unittest.cc
index 9bf63f2..40d9d80 100644
--- a/content/browser/media/media_devices_permission_checker_unittest.cc
+++ b/content/browser/media/media_devices_permission_checker_unittest.cc
@@ -27,7 +27,7 @@
 
   bool CheckMediaAccessPermission(RenderFrameHost* render_Frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type) override {
+                                  blink::mojom::MediaStreamType type) override {
     return true;
   }
 };
diff --git a/content/browser/media/media_devices_util.cc b/content/browser/media/media_devices_util.cc
index 7b5b355..1369712 100644
--- a/content/browser/media/media_devices_util.cc
+++ b/content/browser/media/media_devices_util.cc
@@ -42,13 +42,13 @@
   if (!delegate)
     return std::string();
 
-  blink::MediaStreamType media_stream_type;
+  blink::mojom::MediaStreamType media_stream_type;
   switch (device_type) {
     case blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT:
-      media_stream_type = blink::MEDIA_DEVICE_AUDIO_CAPTURE;
+      media_stream_type = blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE;
       break;
     case blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT:
-      media_stream_type = blink::MEDIA_DEVICE_VIDEO_CAPTURE;
+      media_stream_type = blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE;
       break;
     default:
       return std::string();
diff --git a/content/browser/media/media_internals.cc b/content/browser/media/media_internals.cc
index d1a17e07..3d25fc29 100644
--- a/content/browser/media/media_internals.cc
+++ b/content/browser/media/media_internals.cc
@@ -669,7 +669,7 @@
 void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   for (size_t i = 0; i < update_callbacks_.size(); ++i) {
-    if (update_callbacks_[i].Equals(callback)) {
+    if (update_callbacks_[i] == callback) {
       update_callbacks_.erase(update_callbacks_.begin() + i);
       break;
     }
diff --git a/content/browser/media/media_keys_listener_manager_impl.cc b/content/browser/media/media_keys_listener_manager_impl.cc
index 6802e42e..2c6ae1b 100644
--- a/content/browser/media/media_keys_listener_manager_impl.cc
+++ b/content/browser/media/media_keys_listener_manager_impl.cc
@@ -130,7 +130,7 @@
   // We should never receive an accelerator that was never registered.
   DCHECK(delegate_map_.contains(accelerator.key_code()));
 
-#if defined(OS_WIN) || defined(OS_MACOSX)
+#if defined(OS_MACOSX)
   // For privacy, we don't want to handle media keys when the system is locked.
   // On Windows and Mac OS X, this will happen unless we explicitly prevent it.
   // TODO(steimel): Consider adding an idle monitor instead and disabling the
diff --git a/content/browser/media/system_media_controls_notifier.cc b/content/browser/media/system_media_controls_notifier.cc
index c86ab6d5..c5fdb38 100644
--- a/content/browser/media/system_media_controls_notifier.cc
+++ b/content/browser/media/system_media_controls_notifier.cc
@@ -7,10 +7,13 @@
 #include <memory>
 #include <utility>
 
+#include "base/bind.h"
+#include "base/time/time.h"
 #include "content/public/browser/content_browser_client.h"
 #include "services/media_session/public/mojom/constants.mojom.h"
 #include "services/media_session/public/mojom/media_session.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
+#include "ui/base/idle/idle.h"
 #include "ui/base/win/system_media_controls/system_media_controls_service.h"
 
 namespace content {
@@ -20,6 +23,12 @@
 const int kMinImageSize = 71;
 const int kDesiredImageSize = 150;
 
+constexpr base::TimeDelta kScreenLockPollInterval =
+    base::TimeDelta::FromSeconds(1);
+constexpr int kHideSmtcDelaySeconds = 5;
+constexpr base::TimeDelta kHideSmtcDelay =
+    base::TimeDelta::FromSeconds(kHideSmtcDelaySeconds);
+
 SystemMediaControlsNotifier::SystemMediaControlsNotifier(
     service_manager::Connector* connector)
     : connector_(connector) {}
@@ -27,6 +36,8 @@
 SystemMediaControlsNotifier::~SystemMediaControlsNotifier() = default;
 
 void SystemMediaControlsNotifier::Initialize() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   // |service_| can be set in tests.
   if (!service_)
     service_ = system_media_controls::SystemMediaControlsService::GetInstance();
@@ -36,6 +47,11 @@
   if (!service_)
     return;
 
+  lock_polling_timer_.Start(
+      FROM_HERE, kScreenLockPollInterval,
+      base::BindRepeating(&SystemMediaControlsNotifier::CheckLockState,
+                          base::Unretained(this)));
+
   // |connector_| can be null in tests.
   if (!connector_)
     return;
@@ -65,14 +81,77 @@
       kDesiredImageSize, std::move(image_observer_ptr));
 }
 
+void SystemMediaControlsNotifier::CheckLockState() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  bool new_state = ui::CheckIdleStateIsLocked();
+  if (screen_locked_ == new_state)
+    return;
+
+  screen_locked_ = new_state;
+  if (screen_locked_)
+    OnScreenLocked();
+  else
+    OnScreenUnlocked();
+}
+
+void SystemMediaControlsNotifier::OnScreenLocked() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(service_);
+
+  // If media is currently playing, don't hide the SMTC.
+  if (session_info_ptr_ &&
+      session_info_ptr_->playback_state ==
+          media_session::mojom::MediaPlaybackState::kPlaying) {
+    return;
+  }
+
+  // Otherwise, hide them.
+  service_->SetEnabled(false);
+}
+
+void SystemMediaControlsNotifier::OnScreenUnlocked() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(service_);
+
+  StopHideSmtcTimer();
+  service_->SetEnabled(true);
+}
+
+void SystemMediaControlsNotifier::StartHideSmtcTimer() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  hide_smtc_timer_.Start(
+      FROM_HERE, kHideSmtcDelay,
+      base::BindOnce(&SystemMediaControlsNotifier::HideSmtcTimerFired,
+                     base::Unretained(this)));
+}
+
+void SystemMediaControlsNotifier::StopHideSmtcTimer() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  hide_smtc_timer_.Stop();
+}
+
+void SystemMediaControlsNotifier::HideSmtcTimerFired() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(service_);
+
+  service_->SetEnabled(false);
+}
+
 void SystemMediaControlsNotifier::MediaSessionInfoChanged(
     media_session::mojom::MediaSessionInfoPtr session_info_ptr) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(service_);
 
+  bool is_playing = false;
+
   session_info_ptr_ = std::move(session_info_ptr);
   if (session_info_ptr_) {
     if (session_info_ptr_->playback_state ==
         media_session::mojom::MediaPlaybackState::kPlaying) {
+      is_playing = true;
       service_->SetPlaybackStatus(
           MediaPlaybackStatus::MediaPlaybackStatus_Playing);
     } else {
@@ -89,10 +168,18 @@
     // presented to the platform, and terminate these steps.
     service_->ClearMetadata();
   }
+
+  if (screen_locked_) {
+    if (is_playing)
+      StopHideSmtcTimer();
+    else if (!hide_smtc_timer_.IsRunning())
+      StartHideSmtcTimer();
+  }
 }
 
 void SystemMediaControlsNotifier::MediaSessionMetadataChanged(
     const base::Optional<media_session::MediaMetadata>& metadata) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(service_);
 
   if (metadata.has_value()) {
@@ -116,6 +203,7 @@
 void SystemMediaControlsNotifier::MediaControllerImageChanged(
     media_session::mojom::MediaSessionImageType type,
     const SkBitmap& bitmap) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(service_);
 
   if (!bitmap.empty()) {
diff --git a/content/browser/media/system_media_controls_notifier.h b/content/browser/media/system_media_controls_notifier.h
index 7f41acb..23aaf87 100644
--- a/content/browser/media/system_media_controls_notifier.h
+++ b/content/browser/media/system_media_controls_notifier.h
@@ -8,6 +8,8 @@
 #include <memory>
 #include <vector>
 
+#include "base/sequence_checker.h"
+#include "base/timer/timer.h"
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/media_session/public/mojom/media_controller.mojom.h"
@@ -57,6 +59,27 @@
   }
 
  private:
+  friend class SystemMediaControlsNotifierTest;
+
+  // Polls the current idle state of the system.
+  void CheckLockState();
+
+  // Called when the idle state changes from unlocked to locked.
+  void OnScreenLocked();
+
+  // Called when the idle state changes from locked to unlocked.
+  void OnScreenUnlocked();
+
+  // Helper functions for dealing with the timer that hides the System Media
+  // Transport Controls on the lock screen 5 seconds after the user pauses.
+  void StartHideSmtcTimer();
+  void StopHideSmtcTimer();
+  void HideSmtcTimerFired();
+
+  bool screen_locked_ = false;
+  base::RepeatingTimer lock_polling_timer_;
+  base::OneShotTimer hide_smtc_timer_;
+
   // Our connection to Window's System Media Transport Controls.
   system_media_controls::SystemMediaControlsService* service_ = nullptr;
 
@@ -73,6 +96,8 @@
   mojo::Binding<media_session::mojom::MediaControllerImageObserver>
       media_controller_image_observer_binding_{this};
 
+  SEQUENCE_CHECKER(sequence_checker_);
+
   DISALLOW_COPY_AND_ASSIGN(SystemMediaControlsNotifier);
 };
 
diff --git a/content/browser/media/system_media_controls_notifier_unittest.cc b/content/browser/media/system_media_controls_notifier_unittest.cc
index e6c39a6..b3e39184 100644
--- a/content/browser/media/system_media_controls_notifier_unittest.cc
+++ b/content/browser/media/system_media_controls_notifier_unittest.cc
@@ -7,9 +7,11 @@
 #include <memory>
 #include <utility>
 
+#include "base/test/scoped_task_environment.h"
 #include "services/media_session/public/mojom/media_session.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/idle/scoped_set_idle_state.h"
 #include "ui/base/win/system_media_controls/mock_system_media_controls_service.h"
 
 namespace content {
@@ -23,7 +25,9 @@
 
 class SystemMediaControlsNotifierTest : public testing::Test {
  public:
-  SystemMediaControlsNotifierTest() = default;
+  SystemMediaControlsNotifierTest()
+      : scoped_task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
   ~SystemMediaControlsNotifierTest() override = default;
 
   void SetUp() override {
@@ -78,7 +82,14 @@
     return mock_system_media_controls_service_;
   }
 
+  base::RepeatingTimer& lock_polling_timer() {
+    return notifier_->lock_polling_timer_;
+  }
+
+  base::OneShotTimer& hide_smtc_timer() { return notifier_->hide_smtc_timer_; }
+
  private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   std::unique_ptr<SystemMediaControlsNotifier> notifier_;
   system_media_controls::testing::MockSystemMediaControlsService
       mock_system_media_controls_service_;
@@ -134,4 +145,83 @@
   SimulateImageChanged();
 }
 
+TEST_F(SystemMediaControlsNotifierTest, DisablesOnLockAndEnablesOnUnlock) {
+  EXPECT_CALL(mock_system_media_controls_service(), SetEnabled(false));
+
+  {
+    // Lock the screen.
+    ui::ScopedSetIdleState locked(ui::IDLE_STATE_LOCKED);
+
+    // Make sure that the lock polling timer is running and then force it to
+    // fire so that we don't need to wait. This should disable the service.
+    EXPECT_TRUE(lock_polling_timer().IsRunning());
+    lock_polling_timer().user_task().Run();
+  }
+
+  // Ensure that the service was disabled.
+  testing::Mock::VerifyAndClearExpectations(
+      &mock_system_media_controls_service());
+
+  // The service should be reenabled on unlock.
+  EXPECT_CALL(mock_system_media_controls_service(), SetEnabled(true));
+
+  {
+    // Unlock the screen.
+    ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_ACTIVE);
+
+    // Make sure that the lock polling timer is running and then force it to
+    // fire so that we don't need to wait. This should enable the service.
+    EXPECT_TRUE(lock_polling_timer().IsRunning());
+    lock_polling_timer().user_task().Run();
+  }
+}
+
+TEST_F(SystemMediaControlsNotifierTest, DoesNotDisableOnLockWhenPlaying) {
+  EXPECT_CALL(mock_system_media_controls_service(), SetEnabled(_)).Times(0);
+
+  SimulatePlaying();
+
+  // Lock the screen.
+  ui::ScopedSetIdleState locked(ui::IDLE_STATE_LOCKED);
+
+  // Make sure that the lock polling timer is running and then force it to
+  // fire so that we don't need to wait. This should not disable the service.
+  EXPECT_TRUE(lock_polling_timer().IsRunning());
+  lock_polling_timer().user_task().Run();
+}
+
+TEST_F(SystemMediaControlsNotifierTest, DisablesAfterPausingOnLockScreen) {
+  Expectation playing = EXPECT_CALL(
+      mock_system_media_controls_service(),
+      SetPlaybackStatus(MediaPlaybackStatus::MediaPlaybackStatus_Playing));
+  Expectation paused =
+      EXPECT_CALL(
+          mock_system_media_controls_service(),
+          SetPlaybackStatus(MediaPlaybackStatus::MediaPlaybackStatus_Paused))
+          .After(playing);
+  EXPECT_CALL(mock_system_media_controls_service(), SetEnabled(false))
+      .After(paused);
+
+  SimulatePlaying();
+
+  // Lock the screen.
+  ui::ScopedSetIdleState locked(ui::IDLE_STATE_LOCKED);
+
+  // Make sure that the lock polling timer is running and then force it to
+  // fire so that we don't need to wait. This should not disable the service.
+  EXPECT_TRUE(lock_polling_timer().IsRunning());
+  lock_polling_timer().user_task().Run();
+
+  // Since we're playing, the timer to hide the SMTC should not be running.
+  EXPECT_FALSE(hide_smtc_timer().IsRunning());
+
+  SimulatePaused();
+
+  // Now that we're paused, the timer to hide the SMTC should be running.
+  EXPECT_TRUE(hide_smtc_timer().IsRunning());
+
+  // Force the timer to fire now. This should disable the service.
+  hide_smtc_timer().FireNow();
+}
+
 }  // namespace content
diff --git a/content/browser/native_file_system/file_system_chooser_browsertest.cc b/content/browser/native_file_system/file_system_chooser_browsertest.cc
index 08b3797c..253ffb2 100644
--- a/content/browser/native_file_system/file_system_chooser_browsertest.cc
+++ b/content/browser/native_file_system/file_system_chooser_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
 #include "content/browser/native_file_system/file_system_chooser_test_helpers.h"
+#include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
@@ -34,6 +35,12 @@
     ContentBrowserTest::SetUp();
   }
 
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // Enable experimental web platform features to enable write access.
+    command_line->AppendSwitch(
+        switches::kEnableExperimentalWebPlatformFeatures);
+  }
+
   void TearDown() override {
     ContentBrowserTest::TearDown();
     ASSERT_TRUE(temp_dir_.Delete());
diff --git a/content/browser/native_file_system/native_file_system_directory_handle_impl.cc b/content/browser/native_file_system/native_file_system_directory_handle_impl.cc
index f0e1185..6da991297 100644
--- a/content/browser/native_file_system/native_file_system_directory_handle_impl.cc
+++ b/content/browser/native_file_system/native_file_system_directory_handle_impl.cc
@@ -65,6 +65,18 @@
 NativeFileSystemDirectoryHandleImpl::~NativeFileSystemDirectoryHandleImpl() =
     default;
 
+void NativeFileSystemDirectoryHandleImpl::GetPermissionStatus(
+    bool writable,
+    GetPermissionStatusCallback callback) {
+  DoGetPermissionStatus(writable, std::move(callback));
+}
+
+void NativeFileSystemDirectoryHandleImpl::RequestPermission(
+    bool writable,
+    RequestPermissionCallback callback) {
+  DoRequestPermission(writable, std::move(callback));
+}
+
 void NativeFileSystemDirectoryHandleImpl::GetFile(const std::string& name,
                                                   bool create,
                                                   GetFileCallback callback) {
@@ -77,12 +89,27 @@
     return;
   }
 
+  if (GetReadPermissionStatus() != PermissionStatus::GRANTED) {
+    std::move(callback).Run(
+        NativeFileSystemError::New(base::File::FILE_ERROR_ACCESS_DENIED),
+        nullptr);
+    return;
+  }
+
   if (create) {
-    operation_runner()->CreateFile(
-        child_url, /*exclusive=*/false,
-        base::BindOnce(&NativeFileSystemDirectoryHandleImpl::DidGetFile,
-                       weak_factory_.GetWeakPtr(), child_url,
-                       std::move(callback)));
+    // If |create| is true, write permission is required unconditionally, i.e.
+    // even if the file already exists. This is intentional, and matches the
+    // behavior that is specified in the spec.
+    RunWithWritePermission(
+        base::BindOnce(
+            &NativeFileSystemDirectoryHandleImpl::GetFileWithWritePermission,
+            weak_factory_.GetWeakPtr(), child_url),
+        base::BindOnce([](GetFileCallback callback) {
+          std::move(callback).Run(
+              NativeFileSystemError::New(base::File::FILE_ERROR_ACCESS_DENIED),
+              nullptr);
+        }),
+        std::move(callback));
   } else {
     operation_runner()->FileExists(
         child_url,
@@ -105,12 +132,27 @@
     return;
   }
 
+  if (GetReadPermissionStatus() != PermissionStatus::GRANTED) {
+    std::move(callback).Run(
+        NativeFileSystemError::New(base::File::FILE_ERROR_ACCESS_DENIED),
+        nullptr);
+    return;
+  }
+
   if (create) {
-    operation_runner()->CreateDirectory(
-        child_url, /*exclusive=*/false, /*recursive=*/false,
-        base::BindOnce(&NativeFileSystemDirectoryHandleImpl::DidGetDirectory,
-                       weak_factory_.GetWeakPtr(), child_url,
-                       std::move(callback)));
+    // If |create| is true, write permission is required unconditionally, i.e.
+    // even if the file already exists. This is intentional, and matches the
+    // behavior that is specified in the spec.
+    RunWithWritePermission(
+        base::BindOnce(&NativeFileSystemDirectoryHandleImpl::
+                           GetDirectoryWithWritePermission,
+                       weak_factory_.GetWeakPtr(), child_url),
+        base::BindOnce([](GetDirectoryCallback callback) {
+          std::move(callback).Run(
+              NativeFileSystemError::New(base::File::FILE_ERROR_ACCESS_DENIED),
+              nullptr);
+        }),
+        std::move(callback));
   } else {
     operation_runner()->DirectoryExists(
         child_url,
@@ -131,43 +173,18 @@
                  base::Owned(new ReadDirectoryState{std::move(callback)})));
 }
 
-void NativeFileSystemDirectoryHandleImpl::MoveFrom(
-    NativeFileSystemTransferTokenPtr source,
-    const std::string& name,
-    MoveFromCallback callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  manager()->ResolveTransferToken(
-      std::move(source),
-      base::BindOnce(&NativeFileSystemDirectoryHandleImpl::DoCopyOrMoveFrom,
-                     weak_factory_.GetWeakPtr(), name, /*is_copy=*/false,
-                     std::move(callback)));
-}
-
-void NativeFileSystemDirectoryHandleImpl::CopyFrom(
-    NativeFileSystemTransferTokenPtr source,
-    const std::string& name,
-    CopyFromCallback callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  manager()->ResolveTransferToken(
-      std::move(source),
-      base::BindOnce(&NativeFileSystemDirectoryHandleImpl::DoCopyOrMoveFrom,
-                     weak_factory_.GetWeakPtr(), name, /*is_copy=*/true,
-                     std::move(callback)));
-}
-
 void NativeFileSystemDirectoryHandleImpl::Remove(bool recurse,
                                                  RemoveCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  operation_runner()->Remove(
-      url(), recurse,
-      base::BindOnce(
-          [](RemoveCallback callback, base::File::Error result) {
-            std::move(callback).Run(NativeFileSystemError::New(result));
-          },
-          std::move(callback)));
+  RunWithWritePermission(
+      base::BindOnce(&NativeFileSystemDirectoryHandleImpl::RemoveImpl,
+                     weak_factory_.GetWeakPtr(), recurse),
+      base::BindOnce([](RemoveCallback callback) {
+        std::move(callback).Run(
+            NativeFileSystemError::New(base::File::FILE_ERROR_ACCESS_DENIED));
+      }),
+      std::move(callback));
 }
 
 void NativeFileSystemDirectoryHandleImpl::Transfer(
@@ -177,9 +194,24 @@
   manager()->CreateTransferToken(*this, std::move(token));
 }
 
-void NativeFileSystemDirectoryHandleImpl::DidGetFile(storage::FileSystemURL url,
-                                                     GetFileCallback callback,
-                                                     base::File::Error result) {
+void NativeFileSystemDirectoryHandleImpl::GetFileWithWritePermission(
+    const storage::FileSystemURL& child_url,
+    GetFileCallback callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_EQ(GetWritePermissionStatus(),
+            blink::mojom::PermissionStatus::GRANTED);
+
+  operation_runner()->CreateFile(
+      child_url, /*exclusive=*/false,
+      base::BindOnce(&NativeFileSystemDirectoryHandleImpl::DidGetFile,
+                     weak_factory_.GetWeakPtr(), child_url,
+                     std::move(callback)));
+}
+
+void NativeFileSystemDirectoryHandleImpl::DidGetFile(
+    const storage::FileSystemURL& url,
+    GetFileCallback callback,
+    base::File::Error result) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (result != base::File::FILE_OK) {
@@ -192,8 +224,22 @@
       manager()->CreateFileHandle(context(), url, file_system()));
 }
 
+void NativeFileSystemDirectoryHandleImpl::GetDirectoryWithWritePermission(
+    const storage::FileSystemURL& child_url,
+    GetDirectoryCallback callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_EQ(GetWritePermissionStatus(),
+            blink::mojom::PermissionStatus::GRANTED);
+
+  operation_runner()->CreateDirectory(
+      child_url, /*exclusive=*/false, /*recursive=*/false,
+      base::BindOnce(&NativeFileSystemDirectoryHandleImpl::DidGetDirectory,
+                     weak_factory_.GetWeakPtr(), child_url,
+                     std::move(callback)));
+}
+
 void NativeFileSystemDirectoryHandleImpl::DidGetDirectory(
-    storage::FileSystemURL url,
+    const storage::FileSystemURL& url,
     GetDirectoryCallback callback,
     base::File::Error result) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -245,65 +291,19 @@
   }
 }
 
-void NativeFileSystemDirectoryHandleImpl::DoCopyOrMoveFrom(
-    const std::string& new_name,
-    bool is_copy,
-    CopyOrMoveCallback callback,
-    NativeFileSystemTransferTokenImpl* source) {
+void NativeFileSystemDirectoryHandleImpl::RemoveImpl(bool recurse,
+                                                     RemoveCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_EQ(GetWritePermissionStatus(),
+            blink::mojom::PermissionStatus::GRANTED);
 
-  if (!source) {
-    std::move(callback).Run(
-        NativeFileSystemError::New(base::File::FILE_ERROR_NOT_FOUND), nullptr);
-    return;
-  }
-
-  storage::FileSystemURL url;
-  const base::File::Error file_error = GetChildURL(new_name, &url);
-  if (file_error != base::File::FILE_OK) {
-    std::move(callback).Run(NativeFileSystemError::New(file_error), nullptr);
-    return;
-  }
-
-  if (url == source->url()) {
-    std::move(callback).Run(
-        NativeFileSystemError::New(base::File::FILE_ERROR_INVALID_OPERATION),
-        nullptr);
-    return;
-  }
-
-  auto result_callback = base::BindOnce(
-      &NativeFileSystemDirectoryHandleImpl::DidCopyOrMove,
-      weak_factory_.GetWeakPtr(), std::move(callback), new_name, url,
-      source->type() ==
-          NativeFileSystemTransferTokenImpl::HandleType::kDirectory);
-  if (is_copy) {
-    operation_runner()->Copy(
-        source->url(), url, storage::FileSystemOperation::OPTION_NONE,
-        storage::FileSystemOperation::ERROR_BEHAVIOR_ABORT,
-        /*progress_callback=*/base::NullCallback(), std::move(result_callback));
-  } else {
-    operation_runner()->Move(source->url(), url,
-                             storage::FileSystemOperation::OPTION_NONE,
-                             std::move(result_callback));
-  }
-}
-
-void NativeFileSystemDirectoryHandleImpl::DidCopyOrMove(
-    CopyOrMoveCallback callback,
-    const std::string& new_name,
-    const storage::FileSystemURL& new_url,
-    bool is_directory,
-    base::File::Error result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  if (result != base::File::FILE_OK) {
-    std::move(callback).Run(NativeFileSystemError::New(result), nullptr);
-    return;
-  }
-
-  std::move(callback).Run(NativeFileSystemError::New(base::File::FILE_OK),
-                          CreateEntry(new_name, new_url, is_directory));
+  operation_runner()->Remove(
+      url(), recurse,
+      base::BindOnce(
+          [](RemoveCallback callback, base::File::Error result) {
+            std::move(callback).Run(NativeFileSystemError::New(result));
+          },
+          std::move(callback)));
 }
 
 base::File::Error NativeFileSystemDirectoryHandleImpl::GetChildURL(
@@ -355,4 +355,4 @@
       name);
 }
 
-}  // namespace content
\ No newline at end of file
+}  // namespace content
diff --git a/content/browser/native_file_system/native_file_system_directory_handle_impl.h b/content/browser/native_file_system/native_file_system_directory_handle_impl.h
index 4b7c612e..8066a95 100644
--- a/content/browser/native_file_system/native_file_system_directory_handle_impl.h
+++ b/content/browser/native_file_system/native_file_system_directory_handle_impl.h
@@ -14,8 +14,6 @@
 #include "third_party/blink/public/mojom/native_file_system/native_file_system_directory_handle.mojom.h"
 
 namespace content {
-class NativeFileSystemTransferTokenImpl;
-
 // This is the browser side implementation of the
 // NativeFileSystemDirectoryHandle mojom interface. Instances of this class are
 // owned by the NativeFileSystemManagerImpl instance passed in to the
@@ -35,6 +33,10 @@
   ~NativeFileSystemDirectoryHandleImpl() override;
 
   // blink::mojom::NativeFileSystemDirectoryHandle:
+  void GetPermissionStatus(bool writable,
+                           GetPermissionStatusCallback callback) override;
+  void RequestPermission(bool writable,
+                         RequestPermissionCallback callback) override;
   void GetFile(const std::string& name,
                bool create,
                GetFileCallback callback) override;
@@ -42,12 +44,6 @@
                     bool create,
                     GetDirectoryCallback callback) override;
   void GetEntries(GetEntriesCallback callback) override;
-  void MoveFrom(blink::mojom::NativeFileSystemTransferTokenPtr source,
-                const std::string& name,
-                MoveFromCallback callback) override;
-  void CopyFrom(blink::mojom::NativeFileSystemTransferTokenPtr source,
-                const std::string& name,
-                CopyFromCallback callback) override;
   void Remove(bool recurse, RemoveCallback callback) override;
   void Transfer(
       blink::mojom::NativeFileSystemTransferTokenRequest token) override;
@@ -56,10 +52,18 @@
   // State that is kept for the duration of a GetEntries/ReadDirectory call.
   struct ReadDirectoryState;
 
-  void DidGetFile(storage::FileSystemURL url,
+  // This method creates the file if it does not currently exists. I.e. it is
+  // the implementation for passing create=true to GetFile.
+  void GetFileWithWritePermission(const storage::FileSystemURL& child_url,
+                                  GetFileCallback callback);
+  void DidGetFile(const storage::FileSystemURL& url,
                   GetFileCallback callback,
                   base::File::Error result);
-  void DidGetDirectory(storage::FileSystemURL url,
+  // This method creates the directory if it does not currently exists. I.e. it
+  // is the implementation for passing create=true to GetDirectory.
+  void GetDirectoryWithWritePermission(const storage::FileSystemURL& child_url,
+                                       GetDirectoryCallback callback);
+  void DidGetDirectory(const storage::FileSystemURL& url,
                        GetDirectoryCallback callback,
                        base::File::Error result);
   void DidReadDirectory(
@@ -68,16 +72,7 @@
       std::vector<filesystem::mojom::DirectoryEntry> file_list,
       bool has_more);
 
-  using CopyOrMoveCallback = MoveFromCallback;
-  void DoCopyOrMoveFrom(const std::string& new_name,
-                        bool is_copy,
-                        CopyOrMoveCallback callback,
-                        NativeFileSystemTransferTokenImpl* source);
-  void DidCopyOrMove(CopyOrMoveCallback callback,
-                     const std::string& new_name,
-                     const storage::FileSystemURL& new_url,
-                     bool is_directory,
-                     base::File::Error result);
+  void RemoveImpl(bool recurse, RemoveCallback callback);
 
   // Calculates a FileSystemURL for a (direct) child of this directory with the
   // given name.  Returns an error when |name| includes invalid input like "/".
diff --git a/content/browser/native_file_system/native_file_system_file_handle_impl.cc b/content/browser/native_file_system/native_file_system_file_handle_impl.cc
index 92ed65f7..9d95d5b 100644
--- a/content/browser/native_file_system/native_file_system_file_handle_impl.cc
+++ b/content/browser/native_file_system/native_file_system_file_handle_impl.cc
@@ -38,8 +38,27 @@
 
 NativeFileSystemFileHandleImpl::~NativeFileSystemFileHandleImpl() = default;
 
+void NativeFileSystemFileHandleImpl::GetPermissionStatus(
+    bool writable,
+    GetPermissionStatusCallback callback) {
+  DoGetPermissionStatus(writable, std::move(callback));
+}
+
+void NativeFileSystemFileHandleImpl::RequestPermission(
+    bool writable,
+    RequestPermissionCallback callback) {
+  DoRequestPermission(writable, std::move(callback));
+}
+
 void NativeFileSystemFileHandleImpl::AsBlob(AsBlobCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (GetReadPermissionStatus() != PermissionStatus::GRANTED) {
+    std::move(callback).Run(
+        NativeFileSystemError::New(base::File::FILE_ERROR_ACCESS_DENIED),
+        nullptr);
+    return;
+  }
+
   // TODO(mek): Check backend::SupportsStreaming and create snapshot file if
   // streaming is not supported.
   operation_runner()->GetMetadata(
@@ -54,12 +73,14 @@
 void NativeFileSystemFileHandleImpl::Remove(RemoveCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  operation_runner()->RemoveFile(
-      url(), base::BindOnce(
-                 [](RemoveCallback callback, base::File::Error result) {
-                   std::move(callback).Run(NativeFileSystemError::New(result));
-                 },
-                 std::move(callback)));
+  RunWithWritePermission(
+      base::BindOnce(&NativeFileSystemFileHandleImpl::RemoveImpl,
+                     weak_factory_.GetWeakPtr()),
+      base::BindOnce([](RemoveCallback callback) {
+        std::move(callback).Run(
+            NativeFileSystemError::New(base::File::FILE_ERROR_ACCESS_DENIED));
+      }),
+      std::move(callback));
 }
 
 void NativeFileSystemFileHandleImpl::Write(uint64_t offset,
@@ -67,10 +88,15 @@
                                            WriteCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  blob_context()->GetBlobDataFromBlobPtr(
-      std::move(data),
-      base::BindOnce(&NativeFileSystemFileHandleImpl::DoWriteBlob,
-                     weak_factory_.GetWeakPtr(), std::move(callback), offset));
+  RunWithWritePermission(
+      base::BindOnce(&NativeFileSystemFileHandleImpl::WriteImpl,
+                     weak_factory_.GetWeakPtr(), offset, std::move(data)),
+      base::BindOnce([](WriteCallback callback) {
+        std::move(callback).Run(
+            NativeFileSystemError::New(base::File::FILE_ERROR_ACCESS_DENIED),
+            0);
+      }),
+      std::move(callback));
 }
 
 void NativeFileSystemFileHandleImpl::WriteStream(
@@ -79,33 +105,29 @@
     WriteStreamCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  // FileSystemOperationRunner assumes that positions passed to Write are always
-  // valid, and will NOTREACHED() if that is not the case, so first check the
-  // size of the file to make sure the position passed in from the renderer is
-  // in fact valid.
-  // Of course the file could still change between checking its size and the
-  // write operation being started, but this is at least a lot better than the
-  // old implementation where the renderer only checks against how big it thinks
-  // the file currently is.
-  // TODO(https://crbug.com/957214): Fix this situation.
-  operation_runner()->GetMetadata(
-      url(), FileSystemOperation::GET_METADATA_FIELD_SIZE,
-      base::BindOnce(&NativeFileSystemFileHandleImpl::DoWriteStreamWithFileInfo,
-                     weak_factory_.GetWeakPtr(), std::move(callback), offset,
-                     std::move(stream)));
+  RunWithWritePermission(
+      base::BindOnce(&NativeFileSystemFileHandleImpl::WriteStreamImpl,
+                     weak_factory_.GetWeakPtr(), offset, std::move(stream)),
+      base::BindOnce([](WriteStreamCallback callback) {
+        std::move(callback).Run(
+            NativeFileSystemError::New(base::File::FILE_ERROR_ACCESS_DENIED),
+            0);
+      }),
+      std::move(callback));
 }
 
 void NativeFileSystemFileHandleImpl::Truncate(uint64_t length,
                                               TruncateCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  operation_runner()->Truncate(
-      url(), length,
-      base::BindOnce(
-          [](TruncateCallback callback, base::File::Error result) {
-            std::move(callback).Run(NativeFileSystemError::New(result));
-          },
-          std::move(callback)));
+  RunWithWritePermission(
+      base::BindOnce(&NativeFileSystemFileHandleImpl::TruncateImpl,
+                     weak_factory_.GetWeakPtr(), length),
+      base::BindOnce([](TruncateCallback callback) {
+        std::move(callback).Run(
+            NativeFileSystemError::New(base::File::FILE_ERROR_ACCESS_DENIED));
+      }),
+      std::move(callback));
 }
 
 void NativeFileSystemFileHandleImpl::Transfer(
@@ -152,6 +174,31 @@
       blink::mojom::SerializedBlob::New(uuid, "application/octet-stream",
                                         info.size, blob_ptr.PassInterface()));
 }
+void NativeFileSystemFileHandleImpl::RemoveImpl(RemoveCallback callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_EQ(GetWritePermissionStatus(),
+            blink::mojom::PermissionStatus::GRANTED);
+
+  operation_runner()->RemoveFile(
+      url(), base::BindOnce(
+                 [](RemoveCallback callback, base::File::Error result) {
+                   std::move(callback).Run(NativeFileSystemError::New(result));
+                 },
+                 std::move(callback)));
+}
+
+void NativeFileSystemFileHandleImpl::WriteImpl(uint64_t offset,
+                                               blink::mojom::BlobPtr data,
+                                               WriteCallback callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_EQ(GetWritePermissionStatus(),
+            blink::mojom::PermissionStatus::GRANTED);
+
+  blob_context()->GetBlobDataFromBlobPtr(
+      std::move(data),
+      base::BindOnce(&NativeFileSystemFileHandleImpl::DoWriteBlob,
+                     weak_factory_.GetWeakPtr(), std::move(callback), offset));
+}
 
 void NativeFileSystemFileHandleImpl::DoWriteBlob(
     WriteCallback callback,
@@ -165,36 +212,6 @@
     return;
   }
 
-  // FileSystemOperationRunner assumes that positions passed to Write are always
-  // valid, and will NOTREACHED() if that is not the case, so first check the
-  // size of the file to make sure the position passed in from the renderer is
-  // in fact valid.
-  // Of course the file could still change between checking its size and the
-  // write operation being started, but this is at least a lot better than the
-  // old implementation where the renderer only checks against how big it thinks
-  // the file currently is.
-  // TODO(https://crbug.com/957214): Fix this situation.
-  operation_runner()->GetMetadata(
-      url(), FileSystemOperation::GET_METADATA_FIELD_SIZE,
-      base::BindOnce(&NativeFileSystemFileHandleImpl::DoWriteBlobWithFileInfo,
-                     weak_factory_.GetWeakPtr(), std::move(callback), position,
-                     std::move(blob)));
-}
-
-void NativeFileSystemFileHandleImpl::DoWriteBlobWithFileInfo(
-    WriteCallback callback,
-    uint64_t position,
-    std::unique_ptr<BlobDataHandle> blob,
-    base::File::Error result,
-    const base::File::Info& file_info) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  if (file_info.size < 0 || position > static_cast<uint64_t>(file_info.size)) {
-    std::move(callback).Run(
-        NativeFileSystemError::New(base::File::FILE_ERROR_FAILED), 0);
-    return;
-  }
-
   operation_runner()->Write(
       url(), std::move(blob), position,
       base::BindRepeating(&NativeFileSystemFileHandleImpl::DidWrite,
@@ -202,22 +219,16 @@
                           base::Owned(new WriteState{std::move(callback)})));
 }
 
-void NativeFileSystemFileHandleImpl::DoWriteStreamWithFileInfo(
-    WriteStreamCallback callback,
-    uint64_t position,
-    mojo::ScopedDataPipeConsumerHandle data_pipe,
-    base::File::Error result,
-    const base::File::Info& file_info) {
+void NativeFileSystemFileHandleImpl::WriteStreamImpl(
+    uint64_t offset,
+    mojo::ScopedDataPipeConsumerHandle stream,
+    WriteStreamCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  if (file_info.size < 0 || position > static_cast<uint64_t>(file_info.size)) {
-    std::move(callback).Run(
-        NativeFileSystemError::New(base::File::FILE_ERROR_FAILED), 0);
-    return;
-  }
+  DCHECK_EQ(GetWritePermissionStatus(),
+            blink::mojom::PermissionStatus::GRANTED);
 
   operation_runner()->Write(
-      url(), std::move(data_pipe), position,
+      url(), std::move(stream), offset,
       base::BindRepeating(&NativeFileSystemFileHandleImpl::DidWrite,
                           weak_factory_.GetWeakPtr(),
                           base::Owned(new WriteState{std::move(callback)})));
@@ -237,4 +248,19 @@
   }
 }
 
-}  // namespace content
\ No newline at end of file
+void NativeFileSystemFileHandleImpl::TruncateImpl(uint64_t length,
+                                                  TruncateCallback callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_EQ(GetWritePermissionStatus(),
+            blink::mojom::PermissionStatus::GRANTED);
+
+  operation_runner()->Truncate(
+      url(), length,
+      base::BindOnce(
+          [](TruncateCallback callback, base::File::Error result) {
+            std::move(callback).Run(NativeFileSystemError::New(result));
+          },
+          std::move(callback)));
+}
+
+}  // namespace content
diff --git a/content/browser/native_file_system/native_file_system_file_handle_impl.h b/content/browser/native_file_system/native_file_system_file_handle_impl.h
index b303c80..75ab721 100644
--- a/content/browser/native_file_system/native_file_system_file_handle_impl.h
+++ b/content/browser/native_file_system/native_file_system_file_handle_impl.h
@@ -43,6 +43,10 @@
   ~NativeFileSystemFileHandleImpl() override;
 
   // blink::mojom::NativeFileSystemFileHandle:
+  void GetPermissionStatus(bool writable,
+                           GetPermissionStatusCallback callback) override;
+  void RequestPermission(bool writable,
+                         RequestPermissionCallback callback) override;
   void AsBlob(AsBlobCallback callback) override;
   void Remove(RemoveCallback callback) override;
   void Write(uint64_t offset,
@@ -64,24 +68,24 @@
                              base::File::Error result,
                              const base::File::Info& info);
 
+  void RemoveImpl(RemoveCallback callback);
+
+  void WriteImpl(uint64_t offset,
+                 blink::mojom::BlobPtr data,
+                 WriteCallback callback);
   void DoWriteBlob(WriteCallback callback,
                    uint64_t position,
                    std::unique_ptr<storage::BlobDataHandle> blob);
-  void DoWriteBlobWithFileInfo(WriteCallback callback,
-                               uint64_t position,
-                               std::unique_ptr<storage::BlobDataHandle> blob,
-                               base::File::Error result,
-                               const base::File::Info& file_info);
-  void DoWriteStreamWithFileInfo(WriteStreamCallback callback,
-                                 uint64_t position,
-                                 mojo::ScopedDataPipeConsumerHandle data_pipe,
-                                 base::File::Error result,
-                                 const base::File::Info& file_info);
+  void WriteStreamImpl(uint64_t offset,
+                       mojo::ScopedDataPipeConsumerHandle stream,
+                       WriteStreamCallback callback);
   void DidWrite(WriteState* state,
                 base::File::Error result,
                 int64_t bytes,
                 bool complete);
 
+  void TruncateImpl(uint64_t length, TruncateCallback callback);
+
   base::WeakPtrFactory<NativeFileSystemFileHandleImpl> weak_factory_{this};
   DISALLOW_COPY_AND_ASSIGN(NativeFileSystemFileHandleImpl);
 };
diff --git a/content/browser/native_file_system/native_file_system_handle_base.cc b/content/browser/native_file_system/native_file_system_handle_base.cc
index c3ebad3..f8be556 100644
--- a/content/browser/native_file_system/native_file_system_handle_base.cc
+++ b/content/browser/native_file_system/native_file_system_handle_base.cc
@@ -4,6 +4,8 @@
 
 #include "content/browser/native_file_system/native_file_system_handle_base.h"
 
+#include "content/public/common/content_switches.h"
+
 namespace content {
 
 NativeFileSystemHandleBase::NativeFileSystemHandleBase(
@@ -19,8 +21,67 @@
   DCHECK_EQ(url_.mount_type() == storage::kFileSystemTypeIsolated,
             file_system_.is_valid())
       << url_.mount_type();
+  // For now only support sandboxed file system and native file system.
+  DCHECK(url_.type() == storage::kFileSystemTypeNativeLocal ||
+         url_.type() == storage::kFileSystemTypePersistent ||
+         url_.type() == storage::kFileSystemTypeTemporary ||
+         url_.type() == storage::kFileSystemTypeTest)
+      << url_.type();
+  // Sandboxed and test file systems should always be writable.
+  if (url_.type() == storage::kFileSystemTypePersistent ||
+      url_.type() == storage::kFileSystemTypeTemporary ||
+      url_.type() == storage::kFileSystemTypeTest) {
+    write_permission_status_ = PermissionStatus::GRANTED;
+  }
 }
 
 NativeFileSystemHandleBase::~NativeFileSystemHandleBase() = default;
 
-}  // namespace content
\ No newline at end of file
+NativeFileSystemHandleBase::PermissionStatus
+NativeFileSystemHandleBase::GetWritePermissionStatus() const {
+  // It is not currently possible to have write only handles, so first check the
+  // read permission status. See also:
+  // http://wicg.github.io/native-file-system/#api-filesystemhandle-querypermission
+  if (read_permission_status_ != PermissionStatus::GRANTED)
+    return read_permission_status_;
+  return write_permission_status_;
+}
+
+void NativeFileSystemHandleBase::DoGetPermissionStatus(
+    bool writable,
+    base::OnceCallback<void(PermissionStatus)> callback) {
+  std::move(callback).Run(writable ? GetWritePermissionStatus()
+                                   : GetReadPermissionStatus());
+}
+
+void NativeFileSystemHandleBase::DoRequestPermission(
+    bool writable,
+    base::OnceCallback<void(PermissionStatus)> callback) {
+  PermissionStatus current_status =
+      writable ? GetWritePermissionStatus() : GetReadPermissionStatus();
+  if (current_status != PermissionStatus::ASK) {
+    std::move(callback).Run(current_status);
+    return;
+  }
+  if (!writable) {
+    // TODO(https://crbug.com/971401): Implement prompt. Currently unreachable,
+    // since read permission isn't revokable yet.
+    std::move(callback).Run(read_permission_status_);
+    return;
+  }
+  // TODO(https://crbug.com/971401): Today read permission isn't revokable, so
+  // current status should always be GRANTED.
+  DCHECK_EQ(GetReadPermissionStatus(), PermissionStatus::GRANTED);
+  // TODO(https://crbug.com/878585): Actually prompt for write permission. For
+  // now we're just always denying requests, unless Experimental Web Platform
+  // features are enabled.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableExperimentalWebPlatformFeatures)) {
+    write_permission_status_ = PermissionStatus::GRANTED;
+  } else {
+    write_permission_status_ = PermissionStatus::DENIED;
+  }
+  std::move(callback).Run(write_permission_status_);
+}
+
+}  // namespace content
diff --git a/content/browser/native_file_system/native_file_system_handle_base.h b/content/browser/native_file_system/native_file_system_handle_base.h
index 6f2c1a37..336c8acf 100644
--- a/content/browser/native_file_system/native_file_system_handle_base.h
+++ b/content/browser/native_file_system/native_file_system_handle_base.h
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "content/browser/native_file_system/native_file_system_manager_impl.h"
+#include "content/common/content_export.h"
 #include "storage/browser/fileapi/file_system_url.h"
 #include "storage/browser/fileapi/isolated_context.h"
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
@@ -29,9 +30,10 @@
 // thread. This is because code interacts directly with the file system backends
 // (via storage::FileSystemContext and store::FileSystemOperationRunner, which
 // both expect some of their methods to only be called on the IO thread).
-class NativeFileSystemHandleBase {
+class CONTENT_EXPORT NativeFileSystemHandleBase {
  public:
   using BindingContext = NativeFileSystemManagerImpl::BindingContext;
+  using PermissionStatus = blink::mojom::PermissionStatus;
 
   NativeFileSystemHandleBase(
       NativeFileSystemManagerImpl* manager,
@@ -45,6 +47,30 @@
     return file_system_;
   }
 
+  PermissionStatus GetReadPermissionStatus() const {
+    return read_permission_status_;
+  }
+  PermissionStatus GetWritePermissionStatus() const;
+
+  // Implementation for the GetPermissionStatus method in the
+  // blink::mojom::NativeFileSystemFileHandle and DirectoryHandle interfaces.
+  void DoGetPermissionStatus(
+      bool writable,
+      base::OnceCallback<void(PermissionStatus)> callback);
+  // Implementation for the RequestPermission method in the
+  // blink::mojom::NativeFileSystemFileHandle and DirectoryHandle interfaces.
+  void DoRequestPermission(bool writable,
+                           base::OnceCallback<void(PermissionStatus)> callback);
+
+  // Invokes |callback|, possibly after first requesting write permission. If
+  // permission isn't granted, |permission_denied| is invoked instead. The
+  // callbacks can be invoked synchronously.
+  template <typename CallbackArgType>
+  void RunWithWritePermission(
+      base::OnceCallback<void(CallbackArgType)> callback,
+      base::OnceCallback<void(CallbackArgType)> no_permission_callback,
+      CallbackArgType callback_arg);
+
  protected:
   NativeFileSystemManagerImpl* manager() { return manager_; }
   const BindingContext& context() { return context_; }
@@ -65,9 +91,36 @@
   const storage::FileSystemURL url_;
   const storage::IsolatedContext::ScopedFSHandle file_system_;
 
+  // TODO(mek): We'll likely end up with something more complicated than simple
+  // fields like this, but this will do for now.
+  PermissionStatus read_permission_status_ = PermissionStatus::GRANTED;
+  PermissionStatus write_permission_status_ = PermissionStatus::ASK;
+
   DISALLOW_COPY_AND_ASSIGN(NativeFileSystemHandleBase);
 };
 
+template <typename CallbackArgType>
+void NativeFileSystemHandleBase::RunWithWritePermission(
+    base::OnceCallback<void(CallbackArgType)> callback,
+    base::OnceCallback<void(CallbackArgType)> no_permission_callback,
+    CallbackArgType callback_arg) {
+  DoRequestPermission(
+      /*writable=*/true,
+      base::BindOnce(
+          [](base::OnceCallback<void(CallbackArgType)> callback,
+             base::OnceCallback<void(CallbackArgType)> no_permission_callback,
+             CallbackArgType callback_arg,
+             blink::mojom::PermissionStatus status) {
+            if (status == blink::mojom::PermissionStatus::GRANTED) {
+              std::move(callback).Run(std::move(callback_arg));
+              return;
+            }
+            std::move(no_permission_callback).Run(std::move(callback_arg));
+          },
+          std::move(callback), std::move(no_permission_callback),
+          std::move(callback_arg)));
+}
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_NATIVE_FILE_SYSTEM_NATIVE_FILE_SYSTEM_HANDLE_BASE_H_
diff --git a/content/browser/native_file_system/native_file_system_handle_base_unittest.cc b/content/browser/native_file_system/native_file_system_handle_base_unittest.cc
new file mode 100644
index 0000000..dc12427
--- /dev/null
+++ b/content/browser/native_file_system/native_file_system_handle_base_unittest.cc
@@ -0,0 +1,122 @@
+// 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 "content/browser/native_file_system/native_file_system_handle_base.h"
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_task_environment.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "storage/browser/blob/blob_storage_context.h"
+#include "storage/browser/test/test_file_system_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
+
+namespace content {
+
+using blink::mojom::PermissionStatus;
+using storage::FileSystemURL;
+
+class NativeFileSystemHandleBaseTest : public testing::Test {
+ public:
+  NativeFileSystemHandleBaseTest()
+      : scoped_task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::IO) {
+    scoped_feature_list_.InitAndEnableFeature(
+        blink::features::kNativeFileSystemAPI);
+  }
+
+  void SetUp() override {
+    ASSERT_TRUE(dir_.CreateUniqueTempDir());
+    file_system_context_ = CreateFileSystemContextForTesting(
+        /*quota_manager_proxy=*/nullptr, dir_.GetPath());
+
+    chrome_blob_context_ = base::MakeRefCounted<ChromeBlobStorageContext>();
+    chrome_blob_context_->InitializeOnIOThread(base::FilePath(), nullptr);
+
+    manager_ = base::MakeRefCounted<NativeFileSystemManagerImpl>(
+        file_system_context_, chrome_blob_context_);
+  }
+
+ protected:
+  const url::Origin kTestOrigin =
+      url::Origin::Create(GURL("https://example.com"));
+  base::test::ScopedFeatureList scoped_feature_list_;
+  TestBrowserThreadBundle scoped_task_environment_;
+
+  base::ScopedTempDir dir_;
+  scoped_refptr<storage::FileSystemContext> file_system_context_;
+  scoped_refptr<ChromeBlobStorageContext> chrome_blob_context_;
+  scoped_refptr<NativeFileSystemManagerImpl> manager_;
+};
+
+TEST_F(NativeFileSystemHandleBaseTest, InitialPermissionStatus_TestURL) {
+  auto url =
+      FileSystemURL::CreateForTest(kTestOrigin, storage::kFileSystemTypeTest,
+                                   base::FilePath::FromUTF8Unsafe("test"));
+  NativeFileSystemHandleBase handle(manager_.get(),
+                                    NativeFileSystemManagerImpl::BindingContext(
+                                        kTestOrigin, /*process_id=*/1,
+                                        /*frame_id=*/MSG_ROUTING_NONE),
+                                    url,
+                                    storage::IsolatedContext::ScopedFSHandle());
+  EXPECT_EQ(PermissionStatus::GRANTED, handle.GetReadPermissionStatus());
+  EXPECT_EQ(PermissionStatus::GRANTED, handle.GetWritePermissionStatus());
+}
+
+TEST_F(NativeFileSystemHandleBaseTest, InitialPermissionStatus_SandboxedURL) {
+  auto url = FileSystemURL::CreateForTest(
+      kTestOrigin, storage::kFileSystemTypeTemporary,
+      base::FilePath::FromUTF8Unsafe("test"));
+
+  NativeFileSystemHandleBase handle(manager_.get(),
+                                    NativeFileSystemManagerImpl::BindingContext(
+                                        kTestOrigin, /*process_id=*/1,
+                                        /*frame_id=*/MSG_ROUTING_NONE),
+                                    url,
+                                    storage::IsolatedContext::ScopedFSHandle());
+  EXPECT_EQ(PermissionStatus::GRANTED, handle.GetReadPermissionStatus());
+  EXPECT_EQ(PermissionStatus::GRANTED, handle.GetWritePermissionStatus());
+}
+
+TEST_F(NativeFileSystemHandleBaseTest, InitialPermissionStatus_NativeFSURL) {
+  auto url = FileSystemURL::CreateForTest(
+      kTestOrigin, storage::kFileSystemTypeNativeLocal,
+      base::FilePath::FromUTF8Unsafe("test"));
+
+  NativeFileSystemHandleBase handle(manager_.get(),
+                                    NativeFileSystemManagerImpl::BindingContext(
+                                        kTestOrigin, /*process_id=*/1,
+                                        /*frame_id=*/MSG_ROUTING_NONE),
+                                    url,
+                                    storage::IsolatedContext::ScopedFSHandle());
+  EXPECT_EQ(PermissionStatus::GRANTED, handle.GetReadPermissionStatus());
+  EXPECT_EQ(PermissionStatus::ASK, handle.GetWritePermissionStatus());
+}
+
+TEST_F(NativeFileSystemHandleBaseTest, RequestWritePermission_NativeFSURL) {
+  auto url = FileSystemURL::CreateForTest(
+      kTestOrigin, storage::kFileSystemTypeNativeLocal,
+      base::FilePath::FromUTF8Unsafe("test"));
+
+  NativeFileSystemHandleBase handle(manager_.get(),
+                                    NativeFileSystemManagerImpl::BindingContext(
+                                        kTestOrigin, /*process_id=*/1,
+                                        /*frame_id=*/MSG_ROUTING_NONE),
+                                    url,
+                                    storage::IsolatedContext::ScopedFSHandle());
+
+  base::RunLoop loop;
+  handle.DoRequestPermission(
+      /*writable=*/true,
+      base::BindLambdaForTesting([&](PermissionStatus result) {
+        EXPECT_EQ(PermissionStatus::DENIED, result);
+        loop.Quit();
+      }));
+  loop.Run();
+  EXPECT_EQ(PermissionStatus::DENIED, handle.GetWritePermissionStatus());
+}
+
+}  // namespace content
diff --git a/content/browser/push_messaging/push_messaging_manager.cc b/content/browser/push_messaging/push_messaging_manager.cc
index 95633a6..ba83728d 100644
--- a/content/browser/push_messaging/push_messaging_manager.cc
+++ b/content/browser/push_messaging/push_messaging_manager.cc
@@ -706,19 +706,19 @@
     case blink::mojom::PushUnregistrationStatus::SUCCESS_UNREGISTERED:
     case blink::mojom::PushUnregistrationStatus::PENDING_NETWORK_ERROR:
     case blink::mojom::PushUnregistrationStatus::PENDING_SERVICE_ERROR:
-      std::move(callback).Run(blink::WebPushError::kErrorTypeNone,
+      std::move(callback).Run(blink::mojom::PushErrorType::NONE,
                               true /* did_unsubscribe */,
                               base::nullopt /* error_message */);
       break;
     case blink::mojom::PushUnregistrationStatus::SUCCESS_WAS_NOT_REGISTERED:
-      std::move(callback).Run(blink::WebPushError::kErrorTypeNone,
+      std::move(callback).Run(blink::mojom::PushErrorType::NONE,
                               false /* did_unsubscribe */,
                               base::nullopt /* error_message */);
       break;
     case blink::mojom::PushUnregistrationStatus::NO_SERVICE_WORKER:
     case blink::mojom::PushUnregistrationStatus::SERVICE_NOT_AVAILABLE:
     case blink::mojom::PushUnregistrationStatus::STORAGE_ERROR:
-      std::move(callback).Run(blink::WebPushError::kErrorTypeAbort, false,
+      std::move(callback).Run(blink::mojom::PushErrorType::ABORT, false,
                               std::string(PushUnregistrationStatusToString(
                                   unregistration_status)) /* error_message */);
       break;
diff --git a/content/browser/renderer_host/media/audio_input_delegate_impl.cc b/content/browser/renderer_host/media/audio_input_delegate_impl.cc
index a39bef26..48a2088 100644
--- a/content/browser/renderer_host/media/audio_input_delegate_impl.cc
+++ b/content/browser/renderer_host/media/audio_input_delegate_impl.cc
@@ -208,7 +208,8 @@
         writer_.get(), user_input_monitor);
     DCHECK(controller_);
     // Only count for captures from desktop media picker dialog.
-    if (device->type == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE)
+    if (device->type ==
+        blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE)
       IncrementDesktopCaptureCounter(TAB_AUDIO_CAPTURER_CREATED);
   } else {
     controller_ = media::AudioInputController::Create(
@@ -218,7 +219,8 @@
 
     // Only count for captures from desktop media picker dialog and system loop
     // back audio.
-    if (device->type == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE &&
+    if (device->type ==
+            blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE &&
         (media::AudioDeviceDescription::IsLoopbackDevice(device_id))) {
       IncrementDesktopCaptureCounter(SYSTEM_LOOPBACK_AUDIO_CAPTURER_CREATED);
     }
diff --git a/content/browser/renderer_host/media/audio_input_delegate_impl_unittest.cc b/content/browser/renderer_host/media/audio_input_delegate_impl_unittest.cc
index bf55656a..e50cdc52 100644
--- a/content/browser/renderer_host/media/audio_input_delegate_impl_unittest.cc
+++ b/content/browser/renderer_host/media/audio_input_delegate_impl_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/sync_socket.h"
+#include "base/test/gmock_callback_support.h"
 #include "build/build_config.h"
 #include "content/browser/media/capture/audio_mirroring_manager.h"
 #include "content/browser/media/media_internals.h"
@@ -29,7 +30,6 @@
 #include "media/audio/mock_audio_manager.h"
 #include "media/audio/test_audio_thread.h"
 #include "media/base/bind_to_current_loop.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/media_switches.h"
 #include "media/base/user_input_monitor.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -138,11 +138,11 @@
   ~MockMediaStreamProviderListener() override {}
 
   MOCK_METHOD2(Opened,
-               void(blink::MediaStreamType stream_type,
+               void(blink::mojom::MediaStreamType stream_type,
                     int capture_session_id));
-  void Closed(blink::MediaStreamType stream_type,
+  void Closed(blink::mojom::MediaStreamType stream_type,
               int capture_session_id) override {}
-  void Aborted(blink::MediaStreamType stream_type,
+  void Aborted(blink::mojom::MediaStreamType stream_type,
                int capture_session_id) override {}
 };
 
@@ -177,12 +177,15 @@
         &listener);
 
     int session_id = media_stream_manager_.audio_input_device_manager()->Open(
-        blink::MediaStreamDevice(blink::MEDIA_DEVICE_AUDIO_CAPTURE, device_id,
-                                 name));
+        blink::MediaStreamDevice(
+            blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, device_id,
+            name));
 
     // Block for completion.
     base::RunLoop loop;
-    EXPECT_CALL(listener, Opened(blink::MEDIA_DEVICE_AUDIO_CAPTURE, session_id))
+    EXPECT_CALL(
+        listener,
+        Opened(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, session_id))
         .WillOnce(InvokeWithoutArgs(&loop, &base::RunLoop::Quit));
     loop.Run();
     media_stream_manager_.audio_input_device_manager()->UnregisterListener(
diff --git a/content/browser/renderer_host/media/audio_input_device_manager.cc b/content/browser/renderer_host/media/audio_input_device_manager.cc
index ae5246a..8c540ec 100644
--- a/content/browser/renderer_host/media/audio_input_device_manager.cc
+++ b/content/browser/renderer_host/media/audio_input_device_manager.cc
@@ -120,7 +120,7 @@
   auto device = GetDevice(session_id);
   if (device == devices_.end())
     return;
-  const blink::MediaStreamType stream_type = device->type;
+  const blink::mojom::MediaStreamType stream_type = device->type;
   if (session_id != kFakeOpenSessionId)
     devices_.erase(device);
 
@@ -216,7 +216,7 @@
 }
 
 void AudioInputDeviceManager::ClosedOnIOThread(
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     int session_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   for (auto& listener : listeners_)
diff --git a/content/browser/renderer_host/media/audio_input_device_manager.h b/content/browser/renderer_host/media/audio_input_device_manager.h
index c9df401a..12346a7 100644
--- a/content/browser/renderer_host/media/audio_input_device_manager.h
+++ b/content/browser/renderer_host/media/audio_input_device_manager.h
@@ -104,7 +104,7 @@
 
   // Callback called on IO thread with the session_id referencing the closed
   // device.
-  void ClosedOnIOThread(blink::MediaStreamType type, int session_id);
+  void ClosedOnIOThread(blink::mojom::MediaStreamType type, int session_id);
 
   // Helper to return iterator to the device referenced by |session_id|. If no
   // device is found, it will return devices_.end().
diff --git a/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc b/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
index a4379f5..e308b5a 100644
--- a/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
+++ b/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
@@ -39,9 +39,9 @@
   MockAudioInputDeviceManagerListener() {}
   ~MockAudioInputDeviceManagerListener() override {}
 
-  MOCK_METHOD2(Opened, void(blink::MediaStreamType, const int));
-  MOCK_METHOD2(Closed, void(blink::MediaStreamType, const int));
-  MOCK_METHOD2(Aborted, void(blink::MediaStreamType, int));
+  MOCK_METHOD2(Opened, void(blink::mojom::MediaStreamType, const int));
+  MOCK_METHOD2(Closed, void(blink::mojom::MediaStreamType, const int));
+  MOCK_METHOD2(Aborted, void(blink::mojom::MediaStreamType, int));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockAudioInputDeviceManagerListener);
@@ -73,10 +73,10 @@
     base::RunLoop().RunUntilIdle();
 
     // Use fake devices.
-    devices_.emplace_back(blink::MEDIA_DEVICE_AUDIO_CAPTURE, "fake_device_1",
-                          "Fake Device 1");
-    devices_.emplace_back(blink::MEDIA_DEVICE_AUDIO_CAPTURE, "fake_device_2",
-                          "Fake Device 2");
+    devices_.emplace_back(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                          "fake_device_1", "Fake Device 1");
+    devices_.emplace_back(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                          "fake_device_2", "Fake Device 2");
   }
 
   void SetUp() override {
@@ -131,15 +131,17 @@
     int session_id = manager_->Open(*iter);
 
     // Expected mock call with expected return value.
-    EXPECT_CALL(*audio_input_listener_,
-                Opened(blink::MEDIA_DEVICE_AUDIO_CAPTURE, session_id))
+    EXPECT_CALL(
+        *audio_input_listener_,
+        Opened(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, session_id))
         .Times(1);
     // Waits for the callback.
     WaitForOpenCompletion();
 
     manager_->Close(session_id);
-    EXPECT_CALL(*audio_input_listener_,
-                Closed(blink::MEDIA_DEVICE_AUDIO_CAPTURE, session_id))
+    EXPECT_CALL(
+        *audio_input_listener_,
+        Closed(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, session_id))
         .Times(1);
 
     // Waits for the callback.
@@ -164,7 +166,8 @@
 
     // Expected mock call with expected returned value.
     EXPECT_CALL(*audio_input_listener_,
-                Opened(blink::MEDIA_DEVICE_AUDIO_CAPTURE, session_id[index]))
+                Opened(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                       session_id[index]))
         .Times(1);
 
     // Waits for the callback.
@@ -182,7 +185,8 @@
     // Closes the devices.
     manager_->Close(session_id[i]);
     EXPECT_CALL(*audio_input_listener_,
-                Closed(blink::MEDIA_DEVICE_AUDIO_CAPTURE, session_id[i]))
+                Closed(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                       session_id[i]))
         .Times(1);
 
     // Waits for the callback.
@@ -194,14 +198,16 @@
 TEST_F(MAYBE_AudioInputDeviceManagerTest, OpenNotExistingDevice) {
   InSequence s;
 
-  blink::MediaStreamType stream_type = blink::MEDIA_DEVICE_AUDIO_CAPTURE;
+  blink::mojom::MediaStreamType stream_type =
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE;
   std::string device_name("device_doesnt_exist");
   std::string device_id("id_doesnt_exist");
   blink::MediaStreamDevice dummy_device(stream_type, device_id, device_name);
 
   int session_id = manager_->Open(dummy_device);
-  EXPECT_CALL(*audio_input_listener_,
-              Opened(blink::MEDIA_DEVICE_AUDIO_CAPTURE, session_id))
+  EXPECT_CALL(
+      *audio_input_listener_,
+      Opened(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, session_id))
       .Times(1);
 
   // Waits for the callback.
@@ -221,10 +227,12 @@
   // Expected mock calls with expected returned values.
   EXPECT_NE(first_session_id, second_session_id);
   EXPECT_CALL(*audio_input_listener_,
-              Opened(blink::MEDIA_DEVICE_AUDIO_CAPTURE, first_session_id))
+              Opened(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                     first_session_id))
       .Times(1);
   EXPECT_CALL(*audio_input_listener_,
-              Opened(blink::MEDIA_DEVICE_AUDIO_CAPTURE, second_session_id))
+              Opened(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                     second_session_id))
       .Times(1);
   // Waits for the callback.
   WaitForOpenCompletion();
@@ -232,10 +240,12 @@
   manager_->Close(first_session_id);
   manager_->Close(second_session_id);
   EXPECT_CALL(*audio_input_listener_,
-              Closed(blink::MEDIA_DEVICE_AUDIO_CAPTURE, first_session_id))
+              Closed(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                     first_session_id))
       .Times(1);
   EXPECT_CALL(*audio_input_listener_,
-              Closed(blink::MEDIA_DEVICE_AUDIO_CAPTURE, second_session_id))
+              Closed(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                     second_session_id))
       .Times(1);
   // Waits for the callback.
   base::RunLoop().RunUntilIdle();
@@ -258,7 +268,8 @@
     // stopped the device before calling close.
     session_id[index] = manager_->Open(*iter);
     EXPECT_CALL(*audio_input_listener_,
-                Opened(blink::MEDIA_DEVICE_AUDIO_CAPTURE, session_id[index]))
+                Opened(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                       session_id[index]))
         .Times(1);
     WaitForOpenCompletion();
 
@@ -268,7 +279,8 @@
     EXPECT_EQ(iter->id, device->id);
     manager_->Close(session_id[index]);
     EXPECT_CALL(*audio_input_listener_,
-                Closed(blink::MEDIA_DEVICE_AUDIO_CAPTURE, session_id[index]))
+                Closed(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                       session_id[index]))
         .Times(1);
     base::RunLoop().RunUntilIdle();
   }
@@ -281,8 +293,9 @@
   // Opens the first device.
   blink::MediaStreamDevices::const_iterator iter = devices_.begin();
   int session_id = manager_->Open(*iter);
-  EXPECT_CALL(*audio_input_listener_,
-              Opened(blink::MEDIA_DEVICE_AUDIO_CAPTURE, session_id))
+  EXPECT_CALL(
+      *audio_input_listener_,
+      Opened(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, session_id))
       .Times(1);
   WaitForOpenCompletion();
 
@@ -294,8 +307,9 @@
   DCHECK(!device);
 
   manager_->Close(session_id);
-  EXPECT_CALL(*audio_input_listener_,
-              Closed(blink::MEDIA_DEVICE_AUDIO_CAPTURE, session_id))
+  EXPECT_CALL(
+      *audio_input_listener_,
+      Closed(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, session_id))
       .Times(1);
   base::RunLoop().RunUntilIdle();
 }
@@ -312,12 +326,13 @@
         std::make_unique<media::AudioThreadImpl>());
 
     // Devices to request from AudioInputDeviceManager.
-    devices_.emplace_back(blink::MEDIA_GUM_TAB_AUDIO_CAPTURE, "tab_capture",
-                          "Tab capture");
-    devices_.emplace_back(blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE,
-                          "desktop_capture", "Desktop capture");
-    devices_.emplace_back(blink::MEDIA_DEVICE_AUDIO_CAPTURE, "fake_device",
-                          "Fake Device");
+    devices_.emplace_back(blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE,
+                          "tab_capture", "Tab capture");
+    devices_.emplace_back(
+        blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE,
+        "desktop_capture", "Desktop capture");
+    devices_.emplace_back(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                          "fake_device", "Fake Device");
   }
 
  private:
diff --git a/content/browser/renderer_host/media/audio_output_delegate_impl_unittest.cc b/content/browser/renderer_host/media/audio_output_delegate_impl_unittest.cc
index 86070c3..a254d534 100644
--- a/content/browser/renderer_host/media/audio_output_delegate_impl_unittest.cc
+++ b/content/browser/renderer_host/media/audio_output_delegate_impl_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/run_loop.h"
 #include "base/sync_socket.h"
 #include "base/task/post_task.h"
+#include "base/test/gmock_callback_support.h"
 #include "content/browser/media/capture/audio_mirroring_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -25,7 +26,6 @@
 #include "media/audio/fake_audio_log_factory.h"
 #include "media/audio/fake_audio_manager.h"
 #include "media/base/bind_to_current_loop.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/media_switches.h"
 #include "media/mojo/interfaces/audio_output_stream.mojom.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
@@ -69,12 +69,12 @@
                                   int render_frame_id,
                                   int page_request_id,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType stream_type,
+                                  blink::mojom::MediaStreamType stream_type,
                                   MediaRequestState state) override {}
   void OnSetCapturingLinkSecured(int render_process_id,
                                  int render_frame_id,
                                  int page_request_id,
-                                 blink::MediaStreamType stream_type,
+                                 blink::mojom::MediaStreamType stream_type,
                                  bool is_secure) override {}
 
   MOCK_METHOD2(OnCreatingAudioStream,
@@ -493,7 +493,7 @@
     // the delegate along since destructing it will close the stream and void
     // the purpose of this test.
     EXPECT_CALL(event_handler_, OnStreamError(kStreamId))
-        .WillOnce(media::RunClosure(media::BindToCurrentLoop(base::Bind(
+        .WillOnce(base::test::RunClosure(media::BindToCurrentLoop(base::Bind(
             &AudioOutputDelegateTest::TrampolineToUI, base::Unretained(this),
             std::move(done), base::Passed(&delegate)))));
   }
diff --git a/content/browser/renderer_host/media/fake_video_capture_device_launcher.cc b/content/browser/renderer_host/media/fake_video_capture_device_launcher.cc
index 1408405b..58937b9f 100644
--- a/content/browser/renderer_host/media/fake_video_capture_device_launcher.cc
+++ b/content/browser/renderer_host/media/fake_video_capture_device_launcher.cc
@@ -68,7 +68,7 @@
 
 void FakeVideoCaptureDeviceLauncher::LaunchDeviceAsync(
     const std::string& device_id,
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     const media::VideoCaptureParams& params,
     base::WeakPtr<media::VideoFrameReceiver> receiver,
     base::OnceClosure connection_lost_cb,
diff --git a/content/browser/renderer_host/media/fake_video_capture_device_launcher.h b/content/browser/renderer_host/media/fake_video_capture_device_launcher.h
index 5dd407e..d7d63c3 100644
--- a/content/browser/renderer_host/media/fake_video_capture_device_launcher.h
+++ b/content/browser/renderer_host/media/fake_video_capture_device_launcher.h
@@ -17,7 +17,7 @@
   ~FakeVideoCaptureDeviceLauncher() override;
 
   void LaunchDeviceAsync(const std::string& device_id,
-                         blink::MediaStreamType stream_type,
+                         blink::mojom::MediaStreamType stream_type,
                          const media::VideoCaptureParams& params,
                          base::WeakPtr<media::VideoFrameReceiver> receiver,
                          base::OnceClosure connection_lost_cb,
diff --git a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
index d311c63..4627cef 100644
--- a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
+++ b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
@@ -89,7 +89,7 @@
 
 void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
     const std::string& device_id,
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     const media::VideoCaptureParams& params,
     base::WeakPtr<media::VideoFrameReceiver> receiver_on_io_thread,
     base::OnceClosure /* connection_lost_cb */,
@@ -121,7 +121,7 @@
                      base::Unretained(this), callbacks, std::move(done_cb)));
 
   switch (stream_type) {
-    case blink::MEDIA_DEVICE_VIDEO_CAPTURE: {
+    case blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE: {
       if (!video_capture_system_) {
         // Clients who create an instance of |this| without providing a
         // VideoCaptureSystem instance are expected to know that
@@ -142,7 +142,7 @@
 
 #if defined(ENABLE_SCREEN_CAPTURE)
 #if !defined(OS_ANDROID)
-    case blink::MEDIA_GUM_TAB_VIDEO_CAPTURE:
+    case blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE:
       start_capture_closure = base::BindOnce(
           &InProcessVideoCaptureDeviceLauncher::DoStartTabCaptureOnDeviceThread,
           base::Unretained(this), device_id, params, std::move(receiver),
@@ -150,9 +150,9 @@
       break;
 #endif  // !defined(OS_ANDROID)
 
-    case blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE:
+    case blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE:
       FALLTHROUGH;
-    case blink::MEDIA_DISPLAY_VIDEO_CAPTURE: {
+    case blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE: {
       const DesktopMediaID desktop_id = DesktopMediaID::Parse(device_id);
       if (desktop_id.is_null()) {
         DLOG(ERROR) << "Desktop media ID is null";
diff --git a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.h b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.h
index 863b7bb..f4c6af7 100644
--- a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.h
+++ b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.h
@@ -34,7 +34,7 @@
   ~InProcessVideoCaptureDeviceLauncher() override;
 
   void LaunchDeviceAsync(const std::string& device_id,
-                         blink::MediaStreamType stream_type,
+                         blink::mojom::MediaStreamType stream_type,
                          const media::VideoCaptureParams& params,
                          base::WeakPtr<media::VideoFrameReceiver> receiver,
                          base::OnceClosure connection_lost_cb,
diff --git a/content/browser/renderer_host/media/media_devices_manager.cc b/content/browser/renderer_host/media/media_devices_manager.cc
index 77c0dab..e6ebdee 100644
--- a/content/browser/renderer_host/media/media_devices_manager.cc
+++ b/content/browser/renderer_host/media/media_devices_manager.cc
@@ -591,7 +591,7 @@
   if (try_in_use_first) {
     base::Optional<media::VideoCaptureFormat> format =
         video_capture_manager_->GetDeviceFormatInUse(
-            blink::MEDIA_DEVICE_VIDEO_CAPTURE, device_id);
+            blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, device_id);
     if (format.has_value()) {
       formats.push_back(format.value());
       ReplaceInvalidFrameRatesWithFallback(&formats);
diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
index a430751..094e166 100644
--- a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
+++ b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
@@ -179,7 +179,7 @@
 
 void MediaStreamDispatcherHost::OpenDevice(int32_t page_request_id,
                                            const std::string& device_id,
-                                           blink::MediaStreamType type,
+                                           blink::mojom::MediaStreamType type,
                                            OpenDeviceCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
@@ -196,7 +196,7 @@
 void MediaStreamDispatcherHost::DoOpenDevice(
     int32_t page_request_id,
     const std::string& device_id,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     OpenDeviceCallback callback,
     MediaDeviceSaltAndOrigin salt_and_origin) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -222,7 +222,7 @@
 
 void MediaStreamDispatcherHost::SetCapturingLinkSecured(
     int32_t session_id,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     bool is_secure) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host.h b/content/browser/renderer_host/media/media_stream_dispatcher_host.h
index 8c4c2c27..53b517b 100644
--- a/content/browser/renderer_host/media/media_stream_dispatcher_host.h
+++ b/content/browser/renderer_host/media/media_stream_dispatcher_host.h
@@ -62,11 +62,11 @@
                         int32_t session_id) override;
   void OpenDevice(int32_t request_id,
                   const std::string& device_id,
-                  blink::MediaStreamType type,
+                  blink::mojom::MediaStreamType type,
                   OpenDeviceCallback callback) override;
   void CloseDevice(const std::string& label) override;
   void SetCapturingLinkSecured(int32_t session_id,
-                               blink::MediaStreamType type,
+                               blink::mojom::MediaStreamType type,
                                bool is_secure) override;
   void OnStreamStarted(const std::string& label) override;
 
@@ -77,7 +77,7 @@
                         MediaDeviceSaltAndOrigin salt_and_origin);
   void DoOpenDevice(int32_t request_id,
                     const std::string& device_id,
-                    blink::MediaStreamType type,
+                    blink::mojom::MediaStreamType type,
                     OpenDeviceCallback callback,
                     MediaDeviceSaltAndOrigin salt_and_origin);
 
diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
index 50fd44a..7e4ad8a 100644
--- a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
+++ b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
@@ -117,7 +117,7 @@
 
   void OnOpenDevice(int page_request_id,
                     const std::string& device_id,
-                    blink::MediaStreamType type,
+                    blink::mojom::MediaStreamType type,
                     const base::Closure& quit_closure) {
     quit_closures_.push(quit_closure);
     MediaStreamDispatcherHost::OpenDevice(
@@ -193,9 +193,9 @@
 
   void OnDeviceStoppedInternal(const std::string& label,
                                const blink::MediaStreamDevice& device) {
-    if (IsVideoInputMediaType(device.type))
+    if (blink::IsVideoInputMediaType(device.type))
       EXPECT_TRUE(device.IsSameDevice(video_devices_[0]));
-    if (IsAudioInputMediaType(device.type))
+    if (blink::IsAudioInputMediaType(device.type))
       EXPECT_TRUE(device.IsSameDevice(audio_devices_[0]));
 
     OnDeviceStopSuccess();
@@ -365,7 +365,7 @@
     EXPECT_CALL(*host_, OnDeviceOpenSuccess());
     base::RunLoop run_loop;
     host_->OnOpenDevice(page_request_id, device_id,
-                        blink::MEDIA_DEVICE_VIDEO_CAPTURE,
+                        blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
                         run_loop.QuitClosure());
     run_loop.Run();
     EXPECT_FALSE(DoesContainRawIds(host_->video_devices_));
@@ -377,7 +377,7 @@
     EXPECT_CALL(*host_, OnDeviceOpenSuccess()).Times(0);
     base::RunLoop run_loop;
     host_->OnOpenDevice(page_request_id, device_id,
-                        blink::MEDIA_DEVICE_VIDEO_CAPTURE,
+                        blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
                         run_loop.QuitClosure());
     run_loop.Run();
     EXPECT_FALSE(DoesContainRawIds(host_->video_devices_));
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index 2a1be8b..8b735c4 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -92,10 +92,10 @@
 using blink::MediaStreamDevice;
 using blink::MediaStreamDevices;
 using blink::MediaStreamRequestType;
-using blink::MediaStreamType;
 using blink::StreamControls;
 using blink::TrackControls;
 using blink::mojom::MediaStreamRequestResult;
+using blink::mojom::MediaStreamType;
 
 namespace {
 // Creates a random label used to identify requests.
@@ -168,21 +168,21 @@
 MediaStreamType ConvertToMediaStreamType(blink::MediaDeviceType type) {
   switch (type) {
     case blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT:
-      return blink::MEDIA_DEVICE_AUDIO_CAPTURE;
+      return MediaStreamType::DEVICE_AUDIO_CAPTURE;
     case blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT:
-      return blink::MEDIA_DEVICE_VIDEO_CAPTURE;
+      return MediaStreamType::DEVICE_VIDEO_CAPTURE;
     default:
       NOTREACHED();
   }
 
-  return blink::MEDIA_NO_SERVICE;
+  return MediaStreamType::NO_SERVICE;
 }
 
 blink::MediaDeviceType ConvertToMediaDeviceType(MediaStreamType stream_type) {
   switch (stream_type) {
-    case blink::MEDIA_DEVICE_AUDIO_CAPTURE:
+    case MediaStreamType::DEVICE_AUDIO_CAPTURE:
       return blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT;
-    case blink::MEDIA_DEVICE_VIDEO_CAPTURE:
+    case MediaStreamType::DEVICE_VIDEO_CAPTURE:
       return blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT;
     default:
       NOTREACHED();
@@ -237,7 +237,7 @@
     }
   }
   DesktopMediaID media_id(desktop_media_type, desktop_media_id_id);
-  MediaStreamDevice device(blink::MEDIA_DISPLAY_VIDEO_CAPTURE,
+  MediaStreamDevice device(MediaStreamType::DISPLAY_VIDEO_CAPTURE,
                            media_id.ToString(), media_id.ToString());
   device.display_media_info = media::mojom::DisplayMediaInformation::New(
       display_surface, true, media::mojom::CursorCaptureType::NEVER);
@@ -245,7 +245,7 @@
   if (!request_audio)
     return devices;
 
-  devices.emplace_back(blink::MEDIA_DISPLAY_AUDIO_CAPTURE,
+  devices.emplace_back(MediaStreamType::DISPLAY_AUDIO_CAPTURE,
                        media::AudioDeviceDescription::kDefaultDeviceId,
                        "Fake audio");
   return devices;
@@ -301,10 +301,11 @@
         controls(controls),
         salt_and_origin(std::move(salt_and_origin)),
         device_stopped_cb(std::move(device_stopped_cb)),
-        state_(blink::NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_NOT_REQUESTED),
+        state_(static_cast<size_t>(MediaStreamType::NUM_MEDIA_TYPES),
+               MEDIA_REQUEST_STATE_NOT_REQUESTED),
         request_type_(request_type),
-        audio_type_(blink::MEDIA_NO_SERVICE),
-        video_type_(blink::MEDIA_NO_SERVICE),
+        audio_type_(MediaStreamType::NO_SERVICE),
+        video_type_(MediaStreamType::NO_SERVICE),
         target_process_id_(-1),
         target_frame_id_(-1) {}
 
@@ -314,16 +315,16 @@
   MediaStreamRequestType request_type() const { return request_type_; }
 
   void SetAudioType(MediaStreamType audio_type) {
-    DCHECK(IsAudioInputMediaType(audio_type) ||
-           audio_type == blink::MEDIA_NO_SERVICE);
+    DCHECK(blink::IsAudioInputMediaType(audio_type) ||
+           audio_type == MediaStreamType::NO_SERVICE);
     audio_type_ = audio_type;
   }
 
   MediaStreamType audio_type() const { return audio_type_; }
 
   void SetVideoType(MediaStreamType video_type) {
-    DCHECK(IsVideoInputMediaType(video_type) ||
-           video_type == blink::MEDIA_NO_SERVICE);
+    DCHECK(blink::IsVideoInputMediaType(video_type) ||
+           video_type == MediaStreamType::NO_SERVICE);
     video_type_ = video_type;
   }
 
@@ -363,13 +364,13 @@
 
   // Update the request state and notify observers.
   void SetState(MediaStreamType stream_type, MediaRequestState new_state) {
-    if (stream_type == blink::NUM_MEDIA_TYPES) {
-      for (int i = blink::MEDIA_NO_SERVICE + 1; i < blink::NUM_MEDIA_TYPES;
-           ++i) {
-        state_[static_cast<MediaStreamType>(i)] = new_state;
+    if (stream_type == MediaStreamType::NUM_MEDIA_TYPES) {
+      for (int i = static_cast<int>(MediaStreamType::NO_SERVICE) + 1;
+           i < static_cast<int>(MediaStreamType::NUM_MEDIA_TYPES); ++i) {
+        state_[i] = new_state;
       }
     } else {
-      state_[stream_type] = new_state;
+      state_[static_cast<int>(stream_type)] = new_state;
     }
 
     MediaObserver* media_observer =
@@ -377,9 +378,9 @@
     if (!media_observer)
       return;
 
-    if (stream_type == blink::NUM_MEDIA_TYPES) {
-      for (int i = blink::MEDIA_NO_SERVICE + 1; i < blink::NUM_MEDIA_TYPES;
-           ++i) {
+    if (stream_type == MediaStreamType::NUM_MEDIA_TYPES) {
+      for (int i = static_cast<int>(MediaStreamType::NO_SERVICE) + 1;
+           i < static_cast<int>(MediaStreamType::NUM_MEDIA_TYPES); ++i) {
         media_observer->OnMediaRequestStateChanged(
             target_process_id_, target_frame_id_, page_request_id,
             salt_and_origin.origin.GetURL(), static_cast<MediaStreamType>(i),
@@ -393,7 +394,7 @@
   }
 
   MediaRequestState state(MediaStreamType stream_type) const {
-    return state_[stream_type];
+    return state_[static_cast<int>(stream_type)];
   }
 
   void SetCapturingLinkSecured(bool is_secure) {
@@ -764,7 +765,8 @@
   }
 
   // Cancel the request if still pending at UI side.
-  request->SetState(blink::NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_CLOSING);
+  request->SetState(MediaStreamType::NUM_MEDIA_TYPES,
+                    MEDIA_REQUEST_STATE_CLOSING);
   DeleteRequest(label);
 }
 
@@ -824,7 +826,7 @@
   for (const LabeledDeviceRequest& device_request : requests_) {
     for (const MediaStreamDevice& device : device_request.second->devices) {
       if (device.id == device_id &&
-          device.type == blink::MEDIA_DEVICE_VIDEO_CAPTURE) {
+          device.type == MediaStreamType::DEVICE_VIDEO_CAPTURE) {
         return device.session_id;
       }
     }
@@ -900,15 +902,15 @@
                                     OpenDeviceCallback open_device_cb,
                                     DeviceStoppedCallback device_stopped_cb) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(type == blink::MEDIA_DEVICE_AUDIO_CAPTURE ||
-         type == blink::MEDIA_DEVICE_VIDEO_CAPTURE);
+  DCHECK(type == MediaStreamType::DEVICE_AUDIO_CAPTURE ||
+         type == MediaStreamType::DEVICE_VIDEO_CAPTURE);
   DVLOG(1) << "OpenDevice ({page_request_id = " << page_request_id << "})";
   StreamControls controls;
-  if (IsAudioInputMediaType(type)) {
+  if (blink::IsAudioInputMediaType(type)) {
     controls.audio.requested = true;
     controls.audio.stream_type = type;
     controls.audio.device_id = device_id;
-  } else if (IsVideoInputMediaType(type)) {
+  } else if (blink::IsVideoInputMediaType(type)) {
     controls.video.requested = true;
     controls.video.stream_type = type;
     controls.video.device_id = device_id;
@@ -939,8 +941,8 @@
     const url::Origin& security_origin,
     const std::string& source_id,
     std::string* device_id) const {
-  DCHECK(stream_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE ||
-         stream_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE);
+  DCHECK(stream_type == MediaStreamType::DEVICE_AUDIO_CAPTURE ||
+         stream_type == MediaStreamType::DEVICE_VIDEO_CAPTURE);
   // The source_id can be empty if the constraint is set but empty.
   if (source_id.empty())
     return false;
@@ -988,8 +990,8 @@
   AddLogMessageOnIOThread(
       base::StringPrintf(
           "Media input device removed: type=%s, id=%s, name=%s ",
-          (stream_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE ? "audio"
-                                                            : "video"),
+          (stream_type == MediaStreamType::DEVICE_AUDIO_CAPTURE ? "audio"
+                                                                : "video"),
           media_device_info.device_id.c_str(), media_device_info.label.c_str())
           .c_str());
 }
@@ -1016,10 +1018,10 @@
     MediaStreamType type,
     const blink::WebMediaDeviceInfoArray& devices,
     std::string* device_id) const {
-  if (type == blink::MEDIA_DEVICE_AUDIO_CAPTURE) {
+  if (type == MediaStreamType::DEVICE_AUDIO_CAPTURE) {
     return PickDeviceId(request->salt_and_origin, request->controls.audio,
                         devices, device_id);
-  } else if (type == blink::MEDIA_DEVICE_VIDEO_CAPTURE) {
+  } else if (type == MediaStreamType::DEVICE_VIDEO_CAPTURE) {
     return PickDeviceId(request->salt_and_origin, request->controls.video,
                         devices, device_id);
   } else {
@@ -1031,8 +1033,8 @@
 void MediaStreamManager::TranslateDeviceIdToSourceId(
     DeviceRequest* request,
     MediaStreamDevice* device) {
-  if (request->audio_type() == blink::MEDIA_DEVICE_AUDIO_CAPTURE ||
-      request->video_type() == blink::MEDIA_DEVICE_VIDEO_CAPTURE) {
+  if (request->audio_type() == MediaStreamType::DEVICE_AUDIO_CAPTURE ||
+      request->video_type() == MediaStreamType::DEVICE_VIDEO_CAPTURE) {
     device->id =
         GetHMACForMediaDeviceID(request->salt_and_origin.device_id_salt,
                                 request->salt_and_origin.origin, device->id);
@@ -1052,11 +1054,13 @@
   media_devices_manager_->StartMonitoring();
 
   // Start enumeration for devices of all requested device types.
-  bool request_audio_input = request->audio_type() != blink::MEDIA_NO_SERVICE;
+  bool request_audio_input =
+      request->audio_type() != MediaStreamType::NO_SERVICE;
   if (request_audio_input)
     request->SetState(request->audio_type(), MEDIA_REQUEST_STATE_REQUESTED);
 
-  bool request_video_input = request->video_type() != blink::MEDIA_NO_SERVICE;
+  bool request_video_input =
+      request->video_type() != MediaStreamType::NO_SERVICE;
   if (request_video_input)
     request->SetState(request->video_type(), MEDIA_REQUEST_STATE_REQUESTED);
 
@@ -1123,7 +1127,7 @@
   // MEDIA_GUM_TAB_AUDIO_CAPTURE.
   // TODO(guidou): MEDIA_GUM_TAB_AUDIO_CAPTURE should not be a special
   // case. See https://crbug.com/584287.
-  if (request->audio_type() == blink::MEDIA_GUM_TAB_AUDIO_CAPTURE) {
+  if (request->audio_type() == MediaStreamType::GUM_TAB_AUDIO_CAPTURE) {
     // Using base::Unretained is safe: |audio_system_| will post
     // PostRequestToUI() to IO thread, and MediaStreamManager is deleted on the
     // UI thread, after the IO thread has been stopped.
@@ -1154,9 +1158,9 @@
   const MediaStreamType video_type = request->video_type();
 
   // Post the request to UI and set the state.
-  if (IsAudioInputMediaType(audio_type))
+  if (blink::IsAudioInputMediaType(audio_type))
     request->SetState(audio_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
-  if (IsVideoInputMediaType(video_type))
+  if (blink::IsVideoInputMediaType(video_type))
     request->SetState(video_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
 
   // If using the fake UI, it will just auto-select from the available devices.
@@ -1165,18 +1169,18 @@
   // processing below for MEDIA_GUM_DESKTOP_VIDEO_CAPTURE is for
   // media_stream_dispatcher_host_unittest only.
   if (fake_ui_factory_ &&
-      (request->video_type() != blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE ||
+      (request->video_type() != MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE ||
        !base::CommandLine::ForCurrentProcess()->HasSwitch(
            switches::kUseFakeUIForMediaStream))) {
     MediaStreamDevices devices;
-    if (request->video_type() == blink::MEDIA_DISPLAY_VIDEO_CAPTURE) {
+    if (request->video_type() == MediaStreamType::DISPLAY_VIDEO_CAPTURE) {
       devices = DisplayMediaDevicesFromFakeDeviceConfig(
-          request->audio_type() == blink::MEDIA_DISPLAY_AUDIO_CAPTURE);
+          request->audio_type() == MediaStreamType::DISPLAY_AUDIO_CAPTURE);
     } else if (request->video_type() ==
-               blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE) {
+               MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE) {
       // Cache the |label| in the device name field, for unit test purpose only.
       devices.push_back(MediaStreamDevice(
-          blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
+          MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
           DesktopMediaID(DesktopMediaID::TYPE_SCREEN, DesktopMediaID::kNullId)
               .ToString(),
           label));
@@ -1220,7 +1224,7 @@
   request->SetVideoType(request->controls.video.stream_type);
 
   const bool is_display_capture =
-      request->video_type() == blink::MEDIA_DISPLAY_VIDEO_CAPTURE;
+      request->video_type() == MediaStreamType::DISPLAY_VIDEO_CAPTURE;
   if (is_display_capture && !SetUpDisplayCaptureRequest(request)) {
     FinalizeRequestFailed(label, request,
                           MediaStreamRequestResult::SCREEN_CAPTURE_FAILURE);
@@ -1228,8 +1232,8 @@
   }
 
   const bool is_tab_capture =
-      request->audio_type() == blink::MEDIA_GUM_TAB_AUDIO_CAPTURE ||
-      request->video_type() == blink::MEDIA_GUM_TAB_VIDEO_CAPTURE;
+      request->audio_type() == MediaStreamType::GUM_TAB_AUDIO_CAPTURE ||
+      request->video_type() == MediaStreamType::GUM_TAB_VIDEO_CAPTURE;
   if (is_tab_capture) {
     if (!SetUpTabCaptureRequest(request, label)) {
       FinalizeRequestFailed(label, request,
@@ -1239,7 +1243,7 @@
   }
 
   const bool is_screen_capture =
-      request->video_type() == blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE;
+      request->video_type() == MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE;
   if (is_screen_capture && !SetUpScreenCaptureRequest(request)) {
     FinalizeRequestFailed(label, request,
                           MediaStreamRequestResult::SCREEN_CAPTURE_FAILURE);
@@ -1247,8 +1251,8 @@
   }
 
   if (!is_tab_capture && !is_screen_capture && !is_display_capture) {
-    if (IsDeviceMediaType(request->audio_type()) ||
-        IsDeviceMediaType(request->video_type())) {
+    if (blink::IsDeviceMediaType(request->audio_type()) ||
+        blink::IsDeviceMediaType(request->video_type())) {
       StartEnumeration(request, label);
       return;
     }
@@ -1265,7 +1269,7 @@
 
 bool MediaStreamManager::SetUpDisplayCaptureRequest(DeviceRequest* request) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(request->video_type() == blink::MEDIA_DISPLAY_VIDEO_CAPTURE);
+  DCHECK(request->video_type() == MediaStreamType::DISPLAY_VIDEO_CAPTURE);
 
   // getDisplayMedia function does not permit the use of constraints for
   // selection of a source, see
@@ -1287,10 +1291,10 @@
 bool MediaStreamManager::SetUpDeviceCaptureRequest(
     DeviceRequest* request,
     const MediaDeviceEnumeration& enumeration) {
-  DCHECK((request->audio_type() == blink::MEDIA_DEVICE_AUDIO_CAPTURE ||
-          request->audio_type() == blink::MEDIA_NO_SERVICE) &&
-         (request->video_type() == blink::MEDIA_DEVICE_VIDEO_CAPTURE ||
-          request->video_type() == blink::MEDIA_NO_SERVICE));
+  DCHECK((request->audio_type() == MediaStreamType::DEVICE_AUDIO_CAPTURE ||
+          request->audio_type() == MediaStreamType::NO_SERVICE) &&
+         (request->video_type() == MediaStreamType::DEVICE_VIDEO_CAPTURE ||
+          request->video_type() == MediaStreamType::NO_SERVICE));
   std::string audio_device_id;
   if (request->controls.audio.requested &&
       !GetRequestedDeviceCaptureId(
@@ -1318,8 +1322,8 @@
 
 bool MediaStreamManager::SetUpTabCaptureRequest(DeviceRequest* request,
                                                 const std::string& label) {
-  DCHECK(request->audio_type() == blink::MEDIA_GUM_TAB_AUDIO_CAPTURE ||
-         request->video_type() == blink::MEDIA_GUM_TAB_VIDEO_CAPTURE);
+  DCHECK(request->audio_type() == MediaStreamType::GUM_TAB_AUDIO_CAPTURE ||
+         request->video_type() == MediaStreamType::GUM_TAB_VIDEO_CAPTURE);
 
   std::string capture_device_id;
   if (!request->controls.audio.device_id.empty()) {
@@ -1330,10 +1334,10 @@
     return false;
   }
 
-  if ((request->audio_type() != blink::MEDIA_GUM_TAB_AUDIO_CAPTURE &&
-       request->audio_type() != blink::MEDIA_NO_SERVICE) ||
-      (request->video_type() != blink::MEDIA_GUM_TAB_VIDEO_CAPTURE &&
-       request->video_type() != blink::MEDIA_NO_SERVICE)) {
+  if ((request->audio_type() != MediaStreamType::GUM_TAB_AUDIO_CAPTURE &&
+       request->audio_type() != MediaStreamType::NO_SERVICE) ||
+      (request->video_type() != MediaStreamType::GUM_TAB_VIDEO_CAPTURE &&
+       request->video_type() != MediaStreamType::NO_SERVICE)) {
     return false;
   }
 
@@ -1399,27 +1403,27 @@
 }
 
 bool MediaStreamManager::SetUpScreenCaptureRequest(DeviceRequest* request) {
-  DCHECK(request->audio_type() == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE ||
-         request->video_type() == blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE);
+  DCHECK(request->audio_type() == MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE ||
+         request->video_type() == MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE);
 
   // For screen capture we only support two valid combinations:
   // (1) screen video capture only, or
   // (2) screen video capture with loopback audio capture.
-  if (request->video_type() != blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE ||
-      (request->audio_type() != blink::MEDIA_NO_SERVICE &&
-       request->audio_type() != blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE)) {
+  if (request->video_type() != MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE ||
+      (request->audio_type() != MediaStreamType::NO_SERVICE &&
+       request->audio_type() != MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE)) {
     LOG(ERROR) << "Invalid screen capture request.";
     return false;
   }
 
   std::string video_device_id;
-  if (request->video_type() == blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE &&
+  if (request->video_type() == MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE &&
       !request->controls.video.device_id.empty()) {
     video_device_id = request->controls.video.device_id;
   }
 
   const std::string audio_device_id =
-      request->audio_type() == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE
+      request->audio_type() == MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE
           ? video_device_id
           : "";
 
@@ -1432,7 +1436,7 @@
     const std::string& label,
     const DesktopMediaID& media_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(IsDesktopCaptureMediaType(request->video_type()));
+  DCHECK(blink::IsDesktopCaptureMediaType(request->video_type()));
   DCHECK(request->request_type() == blink::MEDIA_GENERATE_STREAM ||
          request->request_type() == blink::MEDIA_DEVICE_UPDATE);
 
@@ -1501,9 +1505,9 @@
   // Partition the array of devices into audio vs video.
   MediaStreamDevices audio_devices, video_devices;
   for (const MediaStreamDevice& device : request->devices) {
-    if (IsAudioInputMediaType(device.type))
+    if (blink::IsAudioInputMediaType(device.type))
       audio_devices.push_back(device);
-    else if (IsVideoInputMediaType(device.type))
+    else if (blink::IsVideoInputMediaType(device.type))
       video_devices.push_back(device);
     else
       NOTREACHED();
@@ -1544,7 +1548,7 @@
       // Fail to change desktop capture source, keep everything unchanged and
       // bring the previous shared tab to the front.
       for (const auto& device : request->devices) {
-        if (device.type == blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE) {
+        if (device.type == MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE) {
           DesktopMediaID source = DesktopMediaID::Parse(device.id);
           DCHECK(source.type == DesktopMediaID::TYPE_WEB_CONTENTS);
           base::PostTaskWithTraits(
@@ -1579,13 +1583,14 @@
   DCHECK(request->device_changed_cb);
 
   std::vector<std::vector<MediaStreamDevice>> old_devices_by_type(
-      blink::NUM_MEDIA_TYPES);
+      static_cast<size_t>(MediaStreamType::NUM_MEDIA_TYPES));
   for (const auto& old_device : request->old_devices)
-    old_devices_by_type[old_device.type].push_back(old_device);
+    old_devices_by_type[static_cast<size_t>(old_device.type)].push_back(
+        old_device);
 
   for (const auto& new_device : request->devices) {
     MediaStreamDevice old_device;
-    auto& old_devices = old_devices_by_type[new_device.type];
+    auto& old_devices = old_devices_by_type[static_cast<int>(new_device.type)];
     if (!old_devices.empty()) {
       old_device = old_devices.back();
       old_devices.pop_back();
@@ -1675,11 +1680,11 @@
         // We've found a matching request.
         request->SetState(device.type, MEDIA_REQUEST_STATE_DONE);
 
-        if (IsAudioInputMediaType(device.type)) {
+        if (blink::IsAudioInputMediaType(device.type)) {
           // Store the native audio parameters in the device struct.
           // TODO(xians): Handle the tab capture sample rate/channel layout
           // in AudioInputDeviceManager::Open().
-          if (device.type != blink::MEDIA_GUM_TAB_AUDIO_CAPTURE) {
+          if (device.type != MediaStreamType::GUM_TAB_AUDIO_CAPTURE) {
             const MediaStreamDevice* opened_device =
                 audio_input_device_manager_->GetOpenedDeviceById(
                     device.session_id);
@@ -1745,8 +1750,8 @@
     return;
 
   bool requested[] = {requested_audio_input, requested_video_input};
-  MediaStreamType stream_types[] = {blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-                                    blink::MEDIA_DEVICE_VIDEO_CAPTURE};
+  MediaStreamType stream_types[] = {MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                                    MediaStreamType::DEVICE_VIDEO_CAPTURE};
   for (size_t i = 0; i < base::size(requested); ++i) {
     if (!requested[i])
       continue;
@@ -1858,16 +1863,16 @@
   for (const MediaStreamDevice& media_stream_device : devices) {
     MediaStreamDevice device = media_stream_device;
 
-    if (device.type == blink::MEDIA_GUM_TAB_VIDEO_CAPTURE ||
-        device.type == blink::MEDIA_GUM_TAB_AUDIO_CAPTURE) {
+    if (device.type == MediaStreamType::GUM_TAB_VIDEO_CAPTURE ||
+        device.type == MediaStreamType::GUM_TAB_AUDIO_CAPTURE) {
       device.id = request->tab_capture_device_id;
     }
 
     // Initialize the sample_rate and channel_layout here since for audio
     // mirroring, we don't go through EnumerateDevices where these are usually
     // initialized.
-    if (device.type == blink::MEDIA_GUM_TAB_AUDIO_CAPTURE ||
-        device.type == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE) {
+    if (device.type == MediaStreamType::GUM_TAB_AUDIO_CAPTURE ||
+        device.type == MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE) {
       int sample_rate = output_parameters.sample_rate();
       // If we weren't able to get the native sampling rate or the sample_rate
       // is outside the valid range for input devices set reasonable defaults.
@@ -1915,12 +1920,12 @@
   }
 
   // Check whether we've received all stream types requested.
-  if (!found_audio && IsAudioInputMediaType(request->audio_type())) {
+  if (!found_audio && blink::IsAudioInputMediaType(request->audio_type())) {
     request->SetState(request->audio_type(), MEDIA_REQUEST_STATE_ERROR);
     DVLOG(1) << "Set no audio found label " << label;
   }
 
-  if (!found_video && IsVideoInputMediaType(request->video_type()))
+  if (!found_video && blink::IsVideoInputMediaType(request->video_type()))
     request->SetState(request->video_type(), MEDIA_REQUEST_STATE_ERROR);
 
   if (RequestDone(*request))
@@ -1941,7 +1946,7 @@
   bool found_audio = false;
   for (const MediaStreamDevice& media_stream_device : devices) {
     MediaStreamDevice new_device = media_stream_device;
-    found_audio |= IsAudioInputMediaType(new_device.type);
+    found_audio |= blink::IsAudioInputMediaType(new_device.type);
 
     new_device.session_id = GetDeviceManager(new_device.type)->Open(new_device);
     request->SetState(new_device.type, MEDIA_REQUEST_STATE_OPENING);
@@ -1949,7 +1954,7 @@
   }
 
   request->SetAudioType(found_audio ? request->controls.audio.stream_type
-                                    : blink::MEDIA_NO_SERVICE);
+                                    : MediaStreamType::NO_SERVICE);
 }
 
 void MediaStreamManager::StopMediaStreamFromBrowser(const std::string& label) {
@@ -2012,12 +2017,12 @@
   MediaStreamDevices new_devices =
       ConvertToMediaStreamDevices(stream_type, devices);
 
-  if (IsAudioInputMediaType(stream_type)) {
+  if (blink::IsAudioInputMediaType(stream_type)) {
     MediaCaptureDevicesImpl::GetInstance()->OnAudioCaptureDevicesChanged(
         new_devices);
     if (media_observer)
       media_observer->OnAudioCaptureDevicesChanged();
-  } else if (IsVideoInputMediaType(stream_type)) {
+  } else if (blink::IsVideoInputMediaType(stream_type)) {
     MediaCaptureDevicesImpl::GetInstance()->OnVideoCaptureDevicesChanged(
         new_devices);
     if (media_observer)
@@ -2030,8 +2035,10 @@
 bool MediaStreamManager::RequestDone(const DeviceRequest& request) const {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  const bool requested_audio = IsAudioInputMediaType(request.audio_type());
-  const bool requested_video = IsVideoInputMediaType(request.video_type());
+  const bool requested_audio =
+      blink::IsAudioInputMediaType(request.audio_type());
+  const bool requested_video =
+      blink::IsVideoInputMediaType(request.video_type());
 
   const bool audio_done =
       !requested_audio ||
@@ -2052,9 +2059,9 @@
 
 MediaStreamProvider* MediaStreamManager::GetDeviceManager(
     MediaStreamType stream_type) {
-  if (IsVideoInputMediaType(stream_type))
+  if (blink::IsVideoInputMediaType(stream_type))
     return video_capture_manager();
-  else if (IsAudioInputMediaType(stream_type))
+  else if (blink::IsAudioInputMediaType(stream_type))
     return audio_input_device_manager();
   NOTREACHED();
   return nullptr;
@@ -2068,13 +2075,13 @@
   if (!window_id)
     return;
 
-  if (video_type != blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE)
+  if (video_type != MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE)
     return;
 
   // Pass along for desktop screen and window capturing when
   // DesktopCaptureDevice is used.
   for (const MediaStreamDevice& device : devices) {
-    if (device.type != blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE)
+    if (device.type != MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE)
       continue;
 
     DesktopMediaID media_id = DesktopMediaID::Parse(device.id);
@@ -2140,15 +2147,15 @@
 
 // static
 void MediaStreamManager::GetMediaDeviceIDForHMAC(
-    blink::MediaStreamType stream_type,
+    MediaStreamType stream_type,
     std::string salt,
     url::Origin security_origin,
     std::string hmac_device_id,
     scoped_refptr<base::SequencedTaskRunner> task_runner,
     base::OnceCallback<void(const base::Optional<std::string>&)> callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(stream_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE ||
-         stream_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE);
+  DCHECK(stream_type == MediaStreamType::DEVICE_AUDIO_CAPTURE ||
+         stream_type == MediaStreamType::DEVICE_VIDEO_CAPTURE);
   MediaStreamManager* msm = g_media_stream_manager_tls_ptr.Pointer()->Get();
   blink::MediaDeviceType device_type = ConvertToMediaDeviceType(stream_type);
   MediaDevicesManager::BoolDeviceTypes requested_types;
@@ -2230,7 +2237,7 @@
   bool enable_change_source = std::any_of(
       request->devices.cbegin(), request->devices.cend(), [](auto device) {
         DesktopMediaID media_id = DesktopMediaID::Parse(device.id);
-        return device.type == blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE &&
+        return device.type == MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE &&
                media_id.type == DesktopMediaID::TYPE_WEB_CONTENTS;
       });
 
diff --git a/content/browser/renderer_host/media/media_stream_manager.h b/content/browser/renderer_host/media/media_stream_manager.h
index b98347b..8c5e199 100644
--- a/content/browser/renderer_host/media/media_stream_manager.h
+++ b/content/browser/renderer_host/media/media_stream_manager.h
@@ -221,7 +221,7 @@
                   int requester_id,
                   int page_request_id,
                   const std::string& device_id,
-                  blink::MediaStreamType type,
+                  blink::mojom::MediaStreamType type,
                   MediaDeviceSaltAndOrigin salt_and_origin,
                   OpenDeviceCallback open_device_cb,
                   DeviceStoppedCallback device_stopped_cb);
@@ -231,7 +231,7 @@
   // given |source_id|, false if nothing matched it.
   // TODO(guidou): Update to provide a callback-based interface.
   // See http://crbug.com/648155.
-  bool TranslateSourceIdToDeviceId(blink::MediaStreamType stream_type,
+  bool TranslateSourceIdToDeviceId(blink::mojom::MediaStreamType stream_type,
                                    const std::string& salt,
                                    const url::Origin& security_origin,
                                    const std::string& source_id,
@@ -246,11 +246,11 @@
   void EnsureDeviceMonitorStarted();
 
   // Implements MediaStreamProviderListener.
-  void Opened(blink::MediaStreamType stream_type,
+  void Opened(blink::mojom::MediaStreamType stream_type,
               int capture_session_id) override;
-  void Closed(blink::MediaStreamType stream_type,
+  void Closed(blink::mojom::MediaStreamType stream_type,
               int capture_session_id) override;
-  void Aborted(blink::MediaStreamType stream_type,
+  void Aborted(blink::mojom::MediaStreamType stream_type,
                int capture_session_id) override;
 
   // Returns all devices currently opened by a request with label |label|.
@@ -303,10 +303,11 @@
 
   // Convenience method to get the raw device ID from the HMAC |hmac_device_id|
   // for the given |security_origin| and |salt|. |stream_type| must be
-  // blink::MEDIA_DEVICE_AUDIO_CAPTURE or blink::MEDIA_DEVICE_VIDEO_CAPTURE.
-  // The result will be returned via |callback| on the given |task_runner|.
+  // blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE or
+  // blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE. The result will
+  // be returned via |callback| on the given |task_runner|.
   static void GetMediaDeviceIDForHMAC(
-      blink::MediaStreamType stream_type,
+      blink::mojom::MediaStreamType stream_type,
       std::string salt,
       url::Origin security_origin,
       std::string hmac_device_id,
@@ -322,7 +323,7 @@
   // Must be called on the IO thread.
   void SetCapturingLinkSecured(int render_process_id,
                                int session_id,
-                               blink::MediaStreamType type,
+                               blink::mojom::MediaStreamType type,
                                bool is_secure);
 
   // Helper for sending up-to-date device lists to media observer when a
@@ -380,14 +381,15 @@
   // Stop the use of the device associated with |session_id| of type |type| in
   // all |requests_|. The device is removed from the request. If a request
   /// doesn't use any devices as a consequence, the request is deleted.
-  void StopDevice(blink::MediaStreamType type, int session_id);
+  void StopDevice(blink::mojom::MediaStreamType type, int session_id);
   // Calls the correct capture manager and close the device with |session_id|.
   // All requests that uses the device are updated.
-  void CloseDevice(blink::MediaStreamType type, int session_id);
+  void CloseDevice(blink::mojom::MediaStreamType type, int session_id);
   // Returns true if a request for devices has been completed and the devices
   // has either been opened or an error has occurred.
   bool RequestDone(const DeviceRequest& request) const;
-  MediaStreamProvider* GetDeviceManager(blink::MediaStreamType stream_type);
+  MediaStreamProvider* GetDeviceManager(
+      blink::mojom::MediaStreamType stream_type);
   void StartEnumeration(DeviceRequest* request, const std::string& label);
   std::string AddRequest(std::unique_ptr<DeviceRequest> request);
   DeviceRequest* FindRequest(const std::string& label) const;
@@ -474,7 +476,7 @@
   // must be MEDIA_DEVICE_AUDIO_CAPTURE or MEDIA_DEVICE_VIDEO_CAPTURE.
   bool GetRequestedDeviceCaptureId(
       const DeviceRequest* request,
-      blink::MediaStreamType type,
+      blink::mojom::MediaStreamType type,
       const blink::WebMediaDeviceInfoArray& devices,
       std::string* device_id) const;
 
@@ -483,7 +485,7 @@
 
   // Handles the callback from MediaStreamUIProxy to receive the UI window id,
   // used for excluding the notification window in desktop capturing.
-  void OnMediaStreamUIWindowId(blink::MediaStreamType video_type,
+  void OnMediaStreamUIWindowId(blink::mojom::MediaStreamType video_type,
                                const blink::MediaStreamDevices& devices,
                                gfx::NativeViewId window_id);
 
@@ -503,7 +505,7 @@
   // video capture device it also uses cached content from
   // |video_capture_manager_| to set the MediaStreamDevice fields.
   blink::MediaStreamDevices ConvertToMediaStreamDevices(
-      blink::MediaStreamType stream_type,
+      blink::mojom::MediaStreamType stream_type,
       const blink::WebMediaDeviceInfoArray& device_infos);
 
   // Activate the specified tab and bring it to the front.
diff --git a/content/browser/renderer_host/media/media_stream_manager_unittest.cc b/content/browser/renderer_host/media/media_stream_manager_unittest.cc
index bc76d4f..90696db 100644
--- a/content/browser/renderer_host/media/media_stream_manager_unittest.cc
+++ b/content/browser/renderer_host/media/media_stream_manager_unittest.cc
@@ -144,11 +144,11 @@
                     int,
                     int,
                     const GURL&,
-                    blink::MediaStreamType,
+                    blink::mojom::MediaStreamType,
                     MediaRequestState));
   MOCK_METHOD2(OnCreatingAudioStream, void(int, int));
   MOCK_METHOD5(OnSetCapturingLinkSecured,
-               void(int, int, int, blink::MediaStreamType, bool));
+               void(int, int, int, blink::mojom::MediaStreamType, bool));
 };
 
 class TestBrowserClient : public ContentBrowserClient {
@@ -244,9 +244,11 @@
 
     blink::StreamControls controls(request_audio /* request_audio */,
                                    true /* request_video */);
-    controls.video.stream_type = blink::MEDIA_DISPLAY_VIDEO_CAPTURE;
+    controls.video.stream_type =
+        blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE;
     if (request_audio)
-      controls.audio.stream_type = blink::MEDIA_DISPLAY_AUDIO_CAPTURE;
+      controls.audio.stream_type =
+          blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE;
     const int render_process_id = 1;
     const int render_frame_id = 1;
     const int requester_id = 1;
@@ -275,11 +277,13 @@
     MediaStreamManager::DeviceStoppedCallback stopped_callback;
     MediaStreamManager::DeviceChangedCallback changed_callback;
 
-    std::vector<blink::MediaStreamType> expected_types;
-    expected_types.push_back(blink::MEDIA_DISPLAY_VIDEO_CAPTURE);
+    std::vector<blink::mojom::MediaStreamType> expected_types;
+    expected_types.push_back(
+        blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE);
     if (request_audio)
-      expected_types.push_back(blink::MEDIA_DISPLAY_AUDIO_CAPTURE);
-    for (blink::MediaStreamType expected_type : expected_types) {
+      expected_types.push_back(
+          blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE);
+    for (blink::mojom::MediaStreamType expected_type : expected_types) {
       EXPECT_CALL(*media_observer_, OnMediaRequestStateChanged(
                                         _, _, _, _, expected_type,
                                         MEDIA_REQUEST_STATE_PENDING_APPROVAL));
@@ -297,22 +301,26 @@
         std::move(changed_callback));
     run_loop_.Run();
 
-    EXPECT_EQ(blink::MEDIA_DISPLAY_VIDEO_CAPTURE, video_device.type);
+    EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
+              video_device.type);
     if (request_audio)
-      EXPECT_EQ(blink::MEDIA_DISPLAY_AUDIO_CAPTURE, audio_device.type);
+      EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE,
+                audio_device.type);
 
-    EXPECT_CALL(*media_observer_,
-                OnMediaRequestStateChanged(_, _, _, _,
-                                           blink::MEDIA_DISPLAY_VIDEO_CAPTURE,
-                                           MEDIA_REQUEST_STATE_CLOSING));
+    EXPECT_CALL(
+        *media_observer_,
+        OnMediaRequestStateChanged(
+            _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
+            MEDIA_REQUEST_STATE_CLOSING));
     media_stream_manager_->StopStreamDevice(render_process_id, render_frame_id,
                                             requester_id, video_device.id,
                                             video_device.session_id);
     if (request_audio) {
-      EXPECT_CALL(*media_observer_,
-                  OnMediaRequestStateChanged(_, _, _, _,
-                                             blink::MEDIA_DISPLAY_AUDIO_CAPTURE,
-                                             MEDIA_REQUEST_STATE_CLOSING));
+      EXPECT_CALL(
+          *media_observer_,
+          OnMediaRequestStateChanged(
+              _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE,
+              MEDIA_REQUEST_STATE_CLOSING));
       media_stream_manager_->StopStreamDevice(
           render_process_id, render_frame_id, requester_id, audio_device.id,
           audio_device.session_id);
@@ -353,36 +361,44 @@
   // Request cancellation notifies closing of all stream types.
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_CLOSING));
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+          MEDIA_REQUEST_STATE_CLOSING));
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_CLOSING));
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+          MEDIA_REQUEST_STATE_CLOSING));
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_GUM_TAB_AUDIO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_CLOSING));
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE,
+          MEDIA_REQUEST_STATE_CLOSING));
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_GUM_TAB_VIDEO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_CLOSING));
-  EXPECT_CALL(*media_observer_,
-              OnMediaRequestStateChanged(_, _, _, _,
-                                         blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
-                                         MEDIA_REQUEST_STATE_CLOSING));
-  EXPECT_CALL(*media_observer_,
-              OnMediaRequestStateChanged(_, _, _, _,
-                                         blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE,
-                                         MEDIA_REQUEST_STATE_CLOSING));
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE,
+          MEDIA_REQUEST_STATE_CLOSING));
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DISPLAY_VIDEO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_CLOSING));
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+          MEDIA_REQUEST_STATE_CLOSING));
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DISPLAY_AUDIO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_CLOSING));
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE,
+          MEDIA_REQUEST_STATE_CLOSING));
+  EXPECT_CALL(
+      *media_observer_,
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
+          MEDIA_REQUEST_STATE_CLOSING));
+  EXPECT_CALL(
+      *media_observer_,
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE,
+          MEDIA_REQUEST_STATE_CLOSING));
   media_stream_manager_->CancelRequest(label);
   run_loop_.RunUntilIdle();
 }
@@ -419,36 +435,44 @@
   // Cancelled request notifies closing of all stream types.
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_CLOSING));
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+          MEDIA_REQUEST_STATE_CLOSING));
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_CLOSING));
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+          MEDIA_REQUEST_STATE_CLOSING));
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_GUM_TAB_AUDIO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_CLOSING));
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE,
+          MEDIA_REQUEST_STATE_CLOSING));
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_GUM_TAB_VIDEO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_CLOSING));
-  EXPECT_CALL(*media_observer_,
-              OnMediaRequestStateChanged(_, _, _, _,
-                                         blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
-                                         MEDIA_REQUEST_STATE_CLOSING));
-  EXPECT_CALL(*media_observer_,
-              OnMediaRequestStateChanged(_, _, _, _,
-                                         blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE,
-                                         MEDIA_REQUEST_STATE_CLOSING));
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE,
+          MEDIA_REQUEST_STATE_CLOSING));
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DISPLAY_VIDEO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_CLOSING));
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+          MEDIA_REQUEST_STATE_CLOSING));
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DISPLAY_AUDIO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_CLOSING));
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE,
+          MEDIA_REQUEST_STATE_CLOSING));
+  EXPECT_CALL(
+      *media_observer_,
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
+          MEDIA_REQUEST_STATE_CLOSING));
+  EXPECT_CALL(
+      *media_observer_,
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE,
+          MEDIA_REQUEST_STATE_CLOSING));
 
   media_stream_manager_->CancelRequest(label1);
 
@@ -458,21 +482,25 @@
   // the UI.
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_REQUESTED));
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+          MEDIA_REQUEST_STATE_REQUESTED));
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_REQUESTED));
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+          MEDIA_REQUEST_STATE_REQUESTED));
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_PENDING_APPROVAL))
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+          MEDIA_REQUEST_STATE_PENDING_APPROVAL))
       .Times(2);
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_PENDING_APPROVAL))
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+          MEDIA_REQUEST_STATE_PENDING_APPROVAL))
       .Times(2);
 
   // Expecting the callback from the second request will be triggered and
@@ -530,8 +558,9 @@
             .WillOnce(testing::Invoke(
                 [run_loop](std::unique_ptr<MediaStreamRequest>& request,
                            testing::Unused) {
-                  EXPECT_EQ(blink::MEDIA_DISPLAY_VIDEO_CAPTURE,
-                            request->video_type);
+                  EXPECT_EQ(
+                      blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
+                      request->video_type);
                   run_loop->Quit();
                 }));
         return std::unique_ptr<FakeMediaStreamUIProxy>(std::move(mock_ui));
@@ -539,7 +568,8 @@
       &run_loop_));
   blink::StreamControls controls(false /* request_audio */,
                                  true /* request_video */);
-  controls.video.stream_type = blink::MEDIA_DISPLAY_VIDEO_CAPTURE;
+  controls.video.stream_type =
+      blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE;
 
   MediaStreamManager::GenerateStreamCallback generate_stream_callback =
       base::BindOnce([](blink::mojom::MediaStreamRequestResult result,
@@ -548,8 +578,9 @@
                         const blink::MediaStreamDevices& video_devices) {});
   EXPECT_CALL(
       *media_observer_,
-      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DISPLAY_VIDEO_CAPTURE,
-                                 MEDIA_REQUEST_STATE_PENDING_APPROVAL));
+      OnMediaRequestStateChanged(
+          _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
+          MEDIA_REQUEST_STATE_PENDING_APPROVAL));
   const int render_process_id = 0;
   const int render_frame_id = 0;
   const int requester_id = 0;
@@ -575,7 +606,8 @@
 
   blink::StreamControls controls(false /* request_audio */,
                                  true /* request_video */);
-  controls.video.stream_type = blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE;
+  controls.video.stream_type =
+      blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE;
   const int render_process_id = 1;
   const int render_frame_id = 1;
   const int requester_id = 1;
@@ -598,7 +630,8 @@
   MediaStreamManager::DeviceStoppedCallback stopped_callback =
       base::BindRepeating(
           [](const std::string& label, const blink::MediaStreamDevice& device) {
-            EXPECT_EQ(blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, device.type);
+            EXPECT_EQ(blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+                      device.type);
             EXPECT_NE(DesktopMediaID::TYPE_NONE,
                       DesktopMediaID::Parse(device.id).type);
           });
@@ -633,7 +666,8 @@
 
   blink::StreamControls controls(false /* request_audio */,
                                  true /* request_video */);
-  controls.video.stream_type = blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE;
+  controls.video.stream_type =
+      blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE;
   const int render_process_id = 1;
   const int render_frame_id = 1;
   const int requester_id = 1;
@@ -659,10 +693,12 @@
           [](blink::MediaStreamDevice* video_device, const std::string& label,
              const blink::MediaStreamDevice& old_device,
              const blink::MediaStreamDevice& new_device) {
-            EXPECT_EQ(blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, old_device.type);
+            EXPECT_EQ(blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+                      old_device.type);
             EXPECT_NE(DesktopMediaID::TYPE_NONE,
                       DesktopMediaID::Parse(old_device.id).type);
-            EXPECT_EQ(blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, new_device.type);
+            EXPECT_EQ(blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+                      new_device.type);
             EXPECT_NE(DesktopMediaID::TYPE_NONE,
                       DesktopMediaID::Parse(new_device.id).type);
             *video_device = new_device;
@@ -703,8 +739,8 @@
                                                   kExistingRawDeviceId);
 
   MediaStreamManager::GetMediaDeviceIDForHMAC(
-      blink::MEDIA_DEVICE_AUDIO_CAPTURE, kSalt, kOrigin, kExistingHmacDeviceId,
-      base::SequencedTaskRunnerHandle::Get(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, kSalt, kOrigin,
+      kExistingHmacDeviceId, base::SequencedTaskRunnerHandle::Get(),
       base::BindOnce(
           [](const std::string& expected_raw_device_id,
              const base::Optional<std::string>& raw_device_id) {
@@ -716,7 +752,7 @@
 
   const std::string kNonexistingHmacDeviceId = "does not exist";
   MediaStreamManager::GetMediaDeviceIDForHMAC(
-      blink::MEDIA_DEVICE_AUDIO_CAPTURE, kSalt, kOrigin,
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, kSalt, kOrigin,
       kNonexistingHmacDeviceId, base::SequencedTaskRunnerHandle::Get(),
       base::BindOnce([](const base::Optional<std::string>& raw_device_id) {
         EXPECT_FALSE(raw_device_id.has_value());
diff --git a/content/browser/renderer_host/media/media_stream_provider.h b/content/browser/renderer_host/media/media_stream_provider.h
index b0b17cb..9d9aa73 100644
--- a/content/browser/renderer_host/media/media_stream_provider.h
+++ b/content/browser/renderer_host/media/media_stream_provider.h
@@ -37,16 +37,16 @@
 class CONTENT_EXPORT MediaStreamProviderListener {
  public:
   // Called by a MediaStreamProvider when a stream has been opened.
-  virtual void Opened(blink::MediaStreamType stream_type,
+  virtual void Opened(blink::mojom::MediaStreamType stream_type,
                       int capture_session_id) = 0;
 
   // Called by a MediaStreamProvider when a stream has been closed.
-  virtual void Closed(blink::MediaStreamType stream_type,
+  virtual void Closed(blink::mojom::MediaStreamType stream_type,
                       int capture_session_id) = 0;
 
   // Called by a MediaStreamProvider when the device has been aborted due to
   // device error.
-  virtual void Aborted(blink::MediaStreamType stream_type,
+  virtual void Aborted(blink::mojom::MediaStreamType stream_type,
                        int capture_session_id) = 0;
 
  protected:
diff --git a/content/browser/renderer_host/media/media_stream_ui_proxy.cc b/content/browser/renderer_host/media/media_stream_ui_proxy.cc
index aa663059..e3ea07d 100644
--- a/content/browser/renderer_host/media/media_stream_ui_proxy.cc
+++ b/content/browser/renderer_host/media/media_stream_ui_proxy.cc
@@ -161,13 +161,13 @@
   RenderFrameHost* host =
       RenderFrameHost::FromID(render_process_id, render_frame_id);
   for (const blink::MediaStreamDevice& device : devices) {
-    if (device.type == blink::MEDIA_DEVICE_AUDIO_CAPTURE &&
+    if (device.type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE &&
         !IsFeatureEnabled(host, tests_use_fake_render_frame_hosts_,
                           blink::mojom::FeaturePolicyFeature::kMicrophone)) {
       continue;
     }
 
-    if (device.type == blink::MEDIA_DEVICE_VIDEO_CAPTURE &&
+    if (device.type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE &&
         !IsFeatureEnabled(host, tests_use_fake_render_frame_hosts_,
                           blink::mojom::FeaturePolicyFeature::kCamera)) {
       continue;
@@ -353,15 +353,15 @@
   // fake UI.
   for (blink::MediaStreamDevices::const_iterator it = devices_.begin();
        it != devices_.end(); ++it) {
-    if (!accepted_audio &&
-        IsAudioInputMediaType(request->audio_type) &&
-        IsAudioInputMediaType(it->type) &&
+    if (!accepted_audio && blink::IsAudioInputMediaType(request->audio_type) &&
+        blink::IsAudioInputMediaType(it->type) &&
         (request->requested_audio_device_id.empty() ||
          request->requested_audio_device_id == it->id)) {
       devices_to_use.push_back(*it);
       accepted_audio = true;
-    } else if (!accepted_video && IsVideoInputMediaType(request->video_type) &&
-               IsVideoInputMediaType(it->type) &&
+    } else if (!accepted_video &&
+               blink::IsVideoInputMediaType(request->video_type) &&
+               blink::IsVideoInputMediaType(it->type) &&
                (request->requested_video_device_id.empty() ||
                 request->requested_video_device_id == it->id)) {
       devices_to_use.push_back(*it);
@@ -370,8 +370,10 @@
   }
 
   // Fail the request if a device doesn't exist for the requested type.
-  if ((request->audio_type != blink::MEDIA_NO_SERVICE && !accepted_audio) ||
-      (request->video_type != blink::MEDIA_NO_SERVICE && !accepted_video)) {
+  if ((request->audio_type != blink::mojom::MediaStreamType::NO_SERVICE &&
+       !accepted_audio) ||
+      (request->video_type != blink::mojom::MediaStreamType::NO_SERVICE &&
+       !accepted_video)) {
     devices_to_use.clear();
   }
 
diff --git a/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc b/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
index 4131bd29..f6d70b1 100644
--- a/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
+++ b/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
@@ -41,7 +41,7 @@
   MOCK_METHOD3(CheckMediaAccessPermission,
                bool(RenderFrameHost* render_frame_host,
                     const url::Origin& security_origin,
-                    blink::MediaStreamType type));
+                    blink::mojom::MediaStreamType type));
 };
 
 class MockResponseCallback {
@@ -113,8 +113,9 @@
 TEST_F(MediaStreamUIProxyTest, Deny) {
   auto request = std::make_unique<MediaStreamRequest>(
       0, 0, 0, GURL("http://origin/"), false, blink::MEDIA_GENERATE_STREAM,
-      std::string(), std::string(), blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-      blink::MEDIA_DEVICE_VIDEO_CAPTURE, false);
+      std::string(), std::string(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, false);
   MediaStreamRequest* request_ptr = request.get();
   proxy_->RequestAccess(
       std::move(request),
@@ -144,8 +145,9 @@
 TEST_F(MediaStreamUIProxyTest, AcceptAndStart) {
   auto request = std::make_unique<MediaStreamRequest>(
       0, 0, 0, GURL("http://origin/"), false, blink::MEDIA_GENERATE_STREAM,
-      std::string(), std::string(), blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-      blink::MEDIA_DEVICE_VIDEO_CAPTURE, false);
+      std::string(), std::string(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, false);
   MediaStreamRequest* request_ptr = request.get();
   proxy_->RequestAccess(
       std::move(request),
@@ -161,8 +163,8 @@
   ASSERT_FALSE(callback.is_null());
 
   blink::MediaStreamDevices devices;
-  devices.push_back(blink::MediaStreamDevice(blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-                                             "Mic", "Mic"));
+  devices.push_back(blink::MediaStreamDevice(
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, "Mic", "Mic"));
   auto ui = std::make_unique<MockMediaStreamUI>();
   EXPECT_CALL(*ui, MockOnStarted(_, _)).WillOnce(Return(0));
   std::move(callback).Run(devices, blink::mojom::MediaStreamRequestResult::OK,
@@ -184,8 +186,9 @@
 TEST_F(MediaStreamUIProxyTest, DeleteBeforeAccepted) {
   auto request = std::make_unique<MediaStreamRequest>(
       0, 0, 0, GURL("http://origin/"), false, blink::MEDIA_GENERATE_STREAM,
-      std::string(), std::string(), blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-      blink::MEDIA_DEVICE_VIDEO_CAPTURE, false);
+      std::string(), std::string(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, false);
   MediaStreamRequest* request_ptr = request.get();
   proxy_->RequestAccess(
       std::move(request),
@@ -211,8 +214,9 @@
 TEST_F(MediaStreamUIProxyTest, StopFromUI) {
   auto request = std::make_unique<MediaStreamRequest>(
       0, 0, 0, GURL("http://origin/"), false, blink::MEDIA_GENERATE_STREAM,
-      std::string(), std::string(), blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-      blink::MEDIA_DEVICE_VIDEO_CAPTURE, false);
+      std::string(), std::string(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, false);
   MediaStreamRequest* request_ptr = request.get();
   proxy_->RequestAccess(
       std::move(request),
@@ -230,8 +234,8 @@
   base::Closure stop_callback;
 
   blink::MediaStreamDevices devices;
-  devices.push_back(blink::MediaStreamDevice(blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-                                             "Mic", "Mic"));
+  devices.push_back(blink::MediaStreamDevice(
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, "Mic", "Mic"));
   auto ui = std::make_unique<MockMediaStreamUI>();
   EXPECT_CALL(*ui, MockOnStarted(_, _))
       .WillOnce(testing::DoAll(SaveArg<0>(&stop_callback), Return(0)));
@@ -261,8 +265,8 @@
 TEST_F(MediaStreamUIProxyTest, WindowIdCallbackCalled) {
   auto request = std::make_unique<MediaStreamRequest>(
       0, 0, 0, GURL("http://origin/"), false, blink::MEDIA_GENERATE_STREAM,
-      std::string(), std::string(), blink::MEDIA_NO_SERVICE,
-      blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, false);
+      std::string(), std::string(), blink::mojom::MediaStreamType::NO_SERVICE,
+      blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE, false);
   MediaStreamRequest* request_ptr = request.get();
 
   proxy_->RequestAccess(
@@ -300,8 +304,9 @@
 TEST_F(MediaStreamUIProxyTest, ChangeSourceFromUI) {
   auto request = std::make_unique<MediaStreamRequest>(
       0, 0, 0, GURL("http://origin/"), false, blink::MEDIA_GENERATE_STREAM,
-      std::string(), std::string(), blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE,
-      blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, false);
+      std::string(), std::string(),
+      blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE,
+      blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE, false);
   MediaStreamRequest* request_ptr = request.get();
   proxy_->RequestAccess(
       std::move(request),
@@ -320,8 +325,8 @@
 
   blink::MediaStreamDevices devices;
   devices.push_back(blink::MediaStreamDevice(
-      blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, "fake_desktop_video_device",
-      "Fake Desktop Video Device"));
+      blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+      "fake_desktop_video_device", "Fake Desktop Video Device"));
   auto ui = std::make_unique<MockMediaStreamUI>();
   EXPECT_CALL(*ui, MockOnStarted(_, _))
       .WillOnce(testing::DoAll(SaveArg<1>(&source_callback), Return(0)));
@@ -395,8 +400,8 @@
 
   std::unique_ptr<MediaStreamRequest> CreateRequest(
       RenderFrameHost* rfh,
-      blink::MediaStreamType mic_type,
-      blink::MediaStreamType cam_type) {
+      blink::mojom::MediaStreamType mic_type,
+      blink::mojom::MediaStreamType cam_type) {
     return std::make_unique<MediaStreamRequest>(
         rfh->GetProcess()->GetID(), rfh->GetRoutingID(), 0,
         rfh->GetLastCommittedURL(), false, blink::MEDIA_GENERATE_STREAM,
@@ -408,13 +413,16 @@
     void RequestMediaAccessPermission(const MediaStreamRequest& request,
                                       MediaResponseCallback callback) override {
       blink::MediaStreamDevices devices;
-      if (request.audio_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE) {
+      if (request.audio_type ==
+          blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
         devices.push_back(blink::MediaStreamDevice(
-            blink::MEDIA_DEVICE_AUDIO_CAPTURE, "Mic", "Mic"));
+            blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, "Mic", "Mic"));
       }
-      if (request.video_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE) {
+      if (request.video_type ==
+          blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
         devices.push_back(blink::MediaStreamDevice(
-            blink::MEDIA_DEVICE_VIDEO_CAPTURE, "Camera", "Camera"));
+            blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, "Camera",
+            "Camera"));
       }
       auto ui = std::make_unique<MockMediaStreamUI>();
       std::move(callback).Run(
@@ -468,45 +476,53 @@
 
   // Default FP.
   GetResultForRequest(
-      CreateRequest(main_rfh(), blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-                    blink::MEDIA_DEVICE_VIDEO_CAPTURE),
+      CreateRequest(main_rfh(),
+                    blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                    blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE),
       &devices, &result);
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   ASSERT_EQ(2u, devices.size());
-  EXPECT_EQ(blink::MEDIA_DEVICE_AUDIO_CAPTURE, devices[0].type);
-  EXPECT_EQ(blink::MEDIA_DEVICE_VIDEO_CAPTURE, devices[1].type);
+  EXPECT_EQ(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+            devices[0].type);
+  EXPECT_EQ(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+            devices[1].type);
 
   // Mic disabled.
   RefreshPageAndSetHeaderPolicy(main_rfh(),
                                 blink::mojom::FeaturePolicyFeature::kMicrophone,
                                 /*enabled=*/false);
   GetResultForRequest(
-      CreateRequest(main_rfh(), blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-                    blink::MEDIA_DEVICE_VIDEO_CAPTURE),
+      CreateRequest(main_rfh(),
+                    blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                    blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE),
       &devices, &result);
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   ASSERT_EQ(1u, devices.size());
-  EXPECT_EQ(blink::MEDIA_DEVICE_VIDEO_CAPTURE, devices[0].type);
+  EXPECT_EQ(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+            devices[0].type);
 
   // Camera disabled.
   RefreshPageAndSetHeaderPolicy(main_rfh(),
                                 blink::mojom::FeaturePolicyFeature::kCamera,
                                 /*enabled=*/false);
   GetResultForRequest(
-      CreateRequest(main_rfh(), blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-                    blink::MEDIA_DEVICE_VIDEO_CAPTURE),
+      CreateRequest(main_rfh(),
+                    blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                    blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE),
       &devices, &result);
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   ASSERT_EQ(1u, devices.size());
-  EXPECT_EQ(blink::MEDIA_DEVICE_AUDIO_CAPTURE, devices[0].type);
+  EXPECT_EQ(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+            devices[0].type);
 
   // Camera disabled resulting in no devices being returned.
   RefreshPageAndSetHeaderPolicy(main_rfh(),
                                 blink::mojom::FeaturePolicyFeature::kCamera,
                                 /*enabled=*/false);
-  GetResultForRequest(CreateRequest(main_rfh(), blink::MEDIA_NO_SERVICE,
-                                    blink::MEDIA_DEVICE_VIDEO_CAPTURE),
-                      &devices, &result);
+  GetResultForRequest(
+      CreateRequest(main_rfh(), blink::mojom::MediaStreamType::NO_SERVICE,
+                    blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE),
+      &devices, &result);
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED, result);
   ASSERT_EQ(0u, devices.size());
 }
diff --git a/content/browser/renderer_host/media/mock_video_capture_provider.h b/content/browser/renderer_host/media/mock_video_capture_provider.h
index 0049117..868f62f 100644
--- a/content/browser/renderer_host/media/mock_video_capture_provider.h
+++ b/content/browser/renderer_host/media/mock_video_capture_provider.h
@@ -35,7 +35,7 @@
 
   MOCK_METHOD7(DoLaunchDeviceAsync,
                void(const std::string& device_id,
-                    blink::MediaStreamType stream_type,
+                    blink::mojom::MediaStreamType stream_type,
                     const media::VideoCaptureParams& params,
                     base::WeakPtr<media::VideoFrameReceiver>* receiver,
                     base::OnceClosure* connection_lost_cb,
@@ -45,7 +45,7 @@
   MOCK_METHOD0(AbortLaunch, void());
 
   void LaunchDeviceAsync(const std::string& device_id,
-                         blink::MediaStreamType stream_type,
+                         blink::mojom::MediaStreamType stream_type,
                          const media::VideoCaptureParams& params,
                          base::WeakPtr<media::VideoFrameReceiver> receiver,
                          base::OnceClosure connection_lost_cb,
diff --git a/content/browser/renderer_host/media/render_frame_audio_input_stream_factory.cc b/content/browser/renderer_host/media/render_frame_audio_input_stream_factory.cc
index b9690c7..1345bb3e 100644
--- a/content/browser/renderer_host/media/render_frame_audio_input_stream_factory.cc
+++ b/content/browser/renderer_host/media/render_frame_audio_input_stream_factory.cc
@@ -257,7 +257,8 @@
             weak_ptr_factory_.GetWeakPtr(), std::move(client), audio_params,
             shared_memory_count, capture_id.disable_local_echo));
 
-    if (device->type == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE)
+    if (device->type ==
+        blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE)
       IncrementDesktopCaptureCounter(SYSTEM_LOOPBACK_AUDIO_CAPTURER_CREATED);
     return;
   } else {
@@ -268,7 +269,8 @@
 
     // Only count for captures from desktop media picker dialog and system loop
     // back audio.
-    if (device->type == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE &&
+    if (device->type ==
+            blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE &&
         (media::AudioDeviceDescription::IsLoopbackDevice(device->id))) {
       IncrementDesktopCaptureCounter(SYSTEM_LOOPBACK_AUDIO_CAPTURER_CREATED);
     }
diff --git a/content/browser/renderer_host/media/render_frame_audio_input_stream_factory_unittest.cc b/content/browser/renderer_host/media/render_frame_audio_input_stream_factory_unittest.cc
index a70b052..1903c4f 100644
--- a/content/browser/renderer_host/media/render_frame_audio_input_stream_factory_unittest.cc
+++ b/content/browser/renderer_host/media/render_frame_audio_input_stream_factory_unittest.cc
@@ -162,13 +162,13 @@
 
     ~StreamOpenedWaiter() override { aidm_->UnregisterListener(this); }
 
-    void Opened(blink::MediaStreamType stream_type,
+    void Opened(blink::mojom::MediaStreamType stream_type,
                 int capture_session_id) override {
       std::move(cb_).Run();
     }
-    void Closed(blink::MediaStreamType stream_type,
+    void Closed(blink::mojom::MediaStreamType stream_type,
                 int capture_session_id) override {}
-    void Aborted(blink::MediaStreamType stream_type,
+    void Aborted(blink::mojom::MediaStreamType stream_type,
                  int capture_session_id) override {}
 
    private:
@@ -178,7 +178,8 @@
 
   void CallOpenWithTestDeviceAndStoreSessionIdOnIO(int* session_id) {
     *session_id = audio_input_device_manager()->Open(blink::MediaStreamDevice(
-        blink::MEDIA_DEVICE_AUDIO_CAPTURE, kDeviceId, kDeviceName));
+        blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, kDeviceId,
+        kDeviceName));
   }
 
   const media::AudioParameters kParams =
@@ -208,7 +209,8 @@
       mojo::MakeRequest(&factory_ptr), media_stream_manager_.get(), main_rfh());
 
   int session_id = audio_input_device_manager()->Open(blink::MediaStreamDevice(
-      blink::MEDIA_DEVICE_AUDIO_CAPTURE, kDeviceId, kDeviceName));
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, kDeviceId,
+      kDeviceName));
   base::RunLoop().RunUntilIdle();
 
   mojom::RendererAudioInputStreamFactoryClientPtr client;
@@ -232,7 +234,8 @@
   WebContentsMediaCaptureId capture_id(main_frame->GetProcess()->GetID(),
                                        main_frame->GetRoutingID());
   int session_id = audio_input_device_manager()->Open(blink::MediaStreamDevice(
-      blink::MEDIA_GUM_TAB_AUDIO_CAPTURE, capture_id.ToString(), kDeviceName));
+      blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE,
+      capture_id.ToString(), kDeviceName));
   base::RunLoop().RunUntilIdle();
 
   mojom::RendererAudioInputStreamFactoryClientPtr client;
@@ -256,7 +259,8 @@
   WebContentsMediaCaptureId capture_id(main_frame->GetProcess()->GetID(),
                                        main_frame->GetRoutingID());
   int session_id = audio_input_device_manager()->Open(blink::MediaStreamDevice(
-      blink::MEDIA_GUM_TAB_AUDIO_CAPTURE, capture_id.ToString(), kDeviceName));
+      blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE,
+      capture_id.ToString(), kDeviceName));
   base::RunLoop().RunUntilIdle();
 
   source_contents.reset();
diff --git a/content/browser/renderer_host/media/service_video_capture_device_launcher.cc b/content/browser/renderer_host/media/service_video_capture_device_launcher.cc
index 84d2237..1334409 100644
--- a/content/browser/renderer_host/media/service_video_capture_device_launcher.cc
+++ b/content/browser/renderer_host/media/service_video_capture_device_launcher.cc
@@ -64,7 +64,7 @@
 
 void ServiceVideoCaptureDeviceLauncher::LaunchDeviceAsync(
     const std::string& device_id,
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     const media::VideoCaptureParams& params,
     base::WeakPtr<media::VideoFrameReceiver> receiver,
     base::OnceClosure connection_lost_cb,
@@ -73,8 +73,8 @@
   DCHECK(sequence_checker_.CalledOnValidSequence());
   DCHECK(state_ == State::READY_TO_LAUNCH);
 
-  if (stream_type != blink::MEDIA_DEVICE_VIDEO_CAPTURE) {
-    // This launcher only supports MEDIA_DEVICE_VIDEO_CAPTURE.
+  if (stream_type != blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
+    // This launcher only supports MediaStreamType::DEVICE_VIDEO_CAPTURE.
     NOTREACHED();
     return;
   }
diff --git a/content/browser/renderer_host/media/service_video_capture_device_launcher.h b/content/browser/renderer_host/media/service_video_capture_device_launcher.h
index 2936448..7e87e646 100644
--- a/content/browser/renderer_host/media/service_video_capture_device_launcher.h
+++ b/content/browser/renderer_host/media/service_video_capture_device_launcher.h
@@ -28,7 +28,7 @@
 
   // VideoCaptureDeviceLauncher implementation.
   void LaunchDeviceAsync(const std::string& device_id,
-                         blink::MediaStreamType stream_type,
+                         blink::mojom::MediaStreamType stream_type,
                          const media::VideoCaptureParams& params,
                          base::WeakPtr<media::VideoFrameReceiver> receiver,
                          base::OnceClosure connection_lost_cb,
diff --git a/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc b/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc
index e7d641b6..6371542 100644
--- a/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc
+++ b/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc
@@ -159,10 +159,10 @@
           [&wait_for_done_cb]() { wait_for_done_cb.Quit(); }));
 
   // Exercise
-  launcher_->LaunchDeviceAsync(kStubDeviceId, blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                               kArbitraryParams, kNullReceiver,
-                               connection_lost_cb_.Get(), &mock_callbacks_,
-                               done_cb_.Get());
+  launcher_->LaunchDeviceAsync(
+      kStubDeviceId, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      kArbitraryParams, kNullReceiver, connection_lost_cb_.Get(),
+      &mock_callbacks_, done_cb_.Get());
   wait_for_done_cb.Run();
 
   launcher_.reset();
@@ -235,10 +235,10 @@
   }));
 
   // Exercise
-  launcher_->LaunchDeviceAsync(kStubDeviceId, blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                               kArbitraryParams, kNullReceiver,
-                               connection_lost_cb_.Get(), &mock_callbacks_,
-                               done_cb_.Get());
+  launcher_->LaunchDeviceAsync(
+      kStubDeviceId, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      kArbitraryParams, kNullReceiver, connection_lost_cb_.Get(),
+      &mock_callbacks_, done_cb_.Get());
   step_1_run_loop.Run();
   launcher_->AbortLaunch();
 
@@ -296,10 +296,10 @@
   }));
 
   // Exercise
-  launcher_->LaunchDeviceAsync(kStubDeviceId, blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                               kArbitraryParams, kNullReceiver,
-                               connection_lost_cb_.Get(), &mock_callbacks_,
-                               done_cb_.Get());
+  launcher_->LaunchDeviceAsync(
+      kStubDeviceId, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      kArbitraryParams, kNullReceiver, connection_lost_cb_.Get(),
+      &mock_callbacks_, done_cb_.Get());
   run_loop.Run();
 }
 
@@ -318,10 +318,10 @@
   // Exercise
   service_connection_->ReleaseProviderForTesting();
 
-  launcher_->LaunchDeviceAsync(kStubDeviceId, blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                               kArbitraryParams, kNullReceiver,
-                               connection_lost_cb_.Get(), &mock_callbacks_,
-                               done_cb_.Get());
+  launcher_->LaunchDeviceAsync(
+      kStubDeviceId, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      kArbitraryParams, kNullReceiver, connection_lost_cb_.Get(),
+      &mock_callbacks_, done_cb_.Get());
 
   run_loop.Run();
 }
@@ -358,10 +358,10 @@
   }));
 
   // Exercise
-  launcher_->LaunchDeviceAsync(kStubDeviceId, blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                               kArbitraryParams, kNullReceiver,
-                               connection_lost_cb_.Get(), &mock_callbacks_,
-                               done_cb_.Get());
+  launcher_->LaunchDeviceAsync(
+      kStubDeviceId, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      kArbitraryParams, kNullReceiver, connection_lost_cb_.Get(),
+      &mock_callbacks_, done_cb_.Get());
 
   run_loop.Run();
 
@@ -408,10 +408,10 @@
     step_1_run_loop.Quit();
   }));
   // Exercise step 1
-  launcher_->LaunchDeviceAsync(kStubDeviceId, blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                               kArbitraryParams, kNullReceiver,
-                               connection_lost_cb_.Get(), &mock_callbacks_,
-                               done_cb_.Get());
+  launcher_->LaunchDeviceAsync(
+      kStubDeviceId, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      kArbitraryParams, kNullReceiver, connection_lost_cb_.Get(),
+      &mock_callbacks_, done_cb_.Get());
   step_1_run_loop.Run();
 
   base::RunLoop step_2_run_loop;
diff --git a/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc b/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc
index 55a927f..ca82f24 100644
--- a/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc
+++ b/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc
@@ -304,8 +304,8 @@
   auto device_launcher_1 = provider_->CreateDeviceLauncher();
   base::RunLoop wait_for_launch_1;
   device_launcher_1->LaunchDeviceAsync(
-      kStubDeviceId, blink::MEDIA_DEVICE_VIDEO_CAPTURE, kArbitraryParams,
-      kNullReceiver, base::DoNothing(), &mock_callbacks,
+      kStubDeviceId, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      kArbitraryParams, kNullReceiver, base::DoNothing(), &mock_callbacks,
       wait_for_launch_1.QuitClosure());
   wait_for_connection_to_service_.Run();
   wait_for_launch_1.Run();
@@ -355,8 +355,8 @@
   auto device_launcher_2 = provider_->CreateDeviceLauncher();
   base::RunLoop wait_for_launch_2;
   device_launcher_2->LaunchDeviceAsync(
-      kStubDeviceId, blink::MEDIA_DEVICE_VIDEO_CAPTURE, kArbitraryParams,
-      kNullReceiver, base::DoNothing(), &mock_callbacks,
+      kStubDeviceId, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      kArbitraryParams, kNullReceiver, base::DoNothing(), &mock_callbacks,
       wait_for_launch_2.QuitClosure());
   wait_for_launch_2.Run();
   device_launcher_2.reset();
diff --git a/content/browser/renderer_host/media/video_capture_browsertest.cc b/content/browser/renderer_host/media/video_capture_browsertest.cc
index ea6e181..f13b785 100644
--- a/content/browser/renderer_host/media/video_capture_browsertest.cc
+++ b/content/browser/renderer_host/media/video_capture_browsertest.cc
@@ -64,9 +64,9 @@
 
 class MockMediaStreamProviderListener : public MediaStreamProviderListener {
  public:
-  MOCK_METHOD2(Opened, void(blink::MediaStreamType, int));
-  MOCK_METHOD2(Closed, void(blink::MediaStreamType, int));
-  MOCK_METHOD2(Aborted, void(blink::MediaStreamType, int));
+  MOCK_METHOD2(Opened, void(blink::mojom::MediaStreamType, int));
+  MOCK_METHOD2(Closed, void(blink::mojom::MediaStreamType, int));
+  MOCK_METHOD2(Aborted, void(blink::mojom::MediaStreamType, int));
 };
 
 using DeviceIndex = size_t;
@@ -187,8 +187,8 @@
     ASSERT_TRUE(params_.device_index_to_use < descriptors.size());
     const auto& descriptor = descriptors[params_.device_index_to_use];
     blink::MediaStreamDevice media_stream_device(
-        blink::MEDIA_DEVICE_VIDEO_CAPTURE, descriptor.device_id,
-        descriptor.display_name(), descriptor.facing);
+        blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+        descriptor.device_id, descriptor.display_name(), descriptor.facing);
     session_id_ = video_capture_manager_->Open(media_stream_device);
     media::VideoCaptureParams capture_params;
     capture_params.requested_format = media::VideoCaptureFormat(
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc
index 2a99a81..35018e7 100644
--- a/content/browser/renderer_host/media/video_capture_controller.cc
+++ b/content/browser/renderer_host/media/video_capture_controller.cc
@@ -48,24 +48,24 @@
       name, (height) ? ((width)*100) / (height) : kInfiniteRatio);
 
 void LogVideoFrameDrop(media::VideoCaptureFrameDropReason reason,
-                       blink::MediaStreamType stream_type) {
+                       blink::mojom::MediaStreamType stream_type) {
   const int kEnumCount =
       static_cast<int>(media::VideoCaptureFrameDropReason::kMaxValue) + 1;
   UMA_HISTOGRAM_ENUMERATION("Media.VideoCapture.FrameDrop", reason, kEnumCount);
   switch (stream_type) {
-    case blink::MEDIA_DEVICE_VIDEO_CAPTURE:
+    case blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE:
       UMA_HISTOGRAM_ENUMERATION("Media.VideoCapture.FrameDrop.DeviceCapture",
                                 reason, kEnumCount);
       break;
-    case blink::MEDIA_GUM_TAB_VIDEO_CAPTURE:
+    case blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE:
       UMA_HISTOGRAM_ENUMERATION("Media.VideoCapture.FrameDrop.GumTabCapture",
                                 reason, kEnumCount);
       break;
-    case blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE:
+    case blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE:
       UMA_HISTOGRAM_ENUMERATION(
           "Media.VideoCapture.FrameDrop.GumDesktopCapture", reason, kEnumCount);
       break;
-    case blink::MEDIA_DISPLAY_VIDEO_CAPTURE:
+    case blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE:
       UMA_HISTOGRAM_ENUMERATION("Media.VideoCapture.FrameDrop.DisplayCapture",
                                 reason, kEnumCount);
       break;
@@ -77,28 +77,28 @@
 
 void LogMaxConsecutiveVideoFrameDropCountExceeded(
     media::VideoCaptureFrameDropReason reason,
-    blink::MediaStreamType stream_type) {
+    blink::mojom::MediaStreamType stream_type) {
   const int kEnumCount =
       static_cast<int>(media::VideoCaptureFrameDropReason::kMaxValue) + 1;
   UMA_HISTOGRAM_ENUMERATION("Media.VideoCapture.MaxFrameDropExceeded", reason,
                             kEnumCount);
   switch (stream_type) {
-    case blink::MEDIA_DEVICE_VIDEO_CAPTURE:
+    case blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE:
       UMA_HISTOGRAM_ENUMERATION(
           "Media.VideoCapture.MaxFrameDropExceeded.DeviceCapture", reason,
           kEnumCount);
       break;
-    case blink::MEDIA_GUM_TAB_VIDEO_CAPTURE:
+    case blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE:
       UMA_HISTOGRAM_ENUMERATION(
           "Media.VideoCapture.MaxFrameDropExceeded.GumTabCapture", reason,
           kEnumCount);
       break;
-    case blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE:
+    case blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE:
       UMA_HISTOGRAM_ENUMERATION(
           "Media.VideoCapture.MaxFrameDropExceeded.GumDesktopCapture", reason,
           kEnumCount);
       break;
-    case blink::MEDIA_DISPLAY_VIDEO_CAPTURE:
+    case blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE:
       UMA_HISTOGRAM_ENUMERATION(
           "Media.VideoCapture.MaxFrameDropExceeded.DisplayCapture", reason,
           kEnumCount);
@@ -255,7 +255,7 @@
 
 VideoCaptureController::VideoCaptureController(
     const std::string& device_id,
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     const media::VideoCaptureParams& params,
     std::unique_ptr<VideoCaptureDeviceLauncher> device_launcher,
     base::RepeatingCallback<void(const std::string&)> emit_log_message_cb)
diff --git a/content/browser/renderer_host/media/video_capture_controller.h b/content/browser/renderer_host/media/video_capture_controller.h
index dcd98b25..446a961 100644
--- a/content/browser/renderer_host/media/video_capture_controller.h
+++ b/content/browser/renderer_host/media/video_capture_controller.h
@@ -43,7 +43,7 @@
  public:
   VideoCaptureController(
       const std::string& device_id,
-      blink::MediaStreamType stream_type,
+      blink::mojom::MediaStreamType stream_type,
       const media::VideoCaptureParams& params,
       std::unique_ptr<VideoCaptureDeviceLauncher> device_launcher,
       base::RepeatingCallback<void(const std::string&)> emit_log_message_cb);
@@ -147,7 +147,7 @@
                                       base::OnceClosure done_cb);
   int serial_id() const { return serial_id_; }
   const std::string& device_id() const { return device_id_; }
-  blink::MediaStreamType stream_type() const { return stream_type_; }
+  blink::mojom::MediaStreamType stream_type() const { return stream_type_; }
   const media::VideoCaptureParams& parameters() const { return parameters_; }
 
  private:
@@ -244,7 +244,7 @@
 
   const int serial_id_;
   const std::string device_id_;
-  const blink::MediaStreamType stream_type_;
+  const blink::mojom::MediaStreamType stream_type_;
   const media::VideoCaptureParams parameters_;
   std::unique_ptr<VideoCaptureDeviceLauncher> device_launcher_;
   base::RepeatingCallback<void(const std::string&)> emit_log_message_cb_;
diff --git a/content/browser/renderer_host/media/video_capture_controller_unittest.cc b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
index d0e7314..bf83866 100644
--- a/content/browser/renderer_host/media/video_capture_controller_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
@@ -141,8 +141,8 @@
 
   void SetUp() override {
     const std::string arbitrary_device_id = "arbitrary_device_id";
-    const blink::MediaStreamType arbitrary_stream_type =
-        blink::MEDIA_DEVICE_VIDEO_CAPTURE;
+    const blink::mojom::MediaStreamType arbitrary_stream_type =
+        blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE;
     const media::VideoCaptureParams arbitrary_params;
     auto device_launcher = std::make_unique<MockVideoCaptureDeviceLauncher>();
     controller_ = new VideoCaptureController(
diff --git a/content/browser/renderer_host/media/video_capture_manager.cc b/content/browser/renderer_host/media/video_capture_manager.cc
index b1866e97..abc205a8 100644
--- a/content/browser/renderer_host/media/video_capture_manager.cc
+++ b/content/browser/renderer_host/media/video_capture_manager.cc
@@ -285,7 +285,8 @@
   // TODO(chfremer): Check if any production code actually depends on this
   // requirement. If not, relax the requirement in the test and remove the below
   // if block. See crbug.com/708251
-  if (controller->stream_type() == blink::MEDIA_DEVICE_VIDEO_CAPTURE) {
+  if (controller->stream_type() ==
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
     const media::VideoCaptureDeviceInfo* device_info =
         GetDeviceInfoById(controller->device_id());
     if (!device_info) {
@@ -324,7 +325,8 @@
   DCHECK_EQ(controller, device_start_request_queue_.begin()->controller());
   DCHECK(controller);
 
-  if (controller->stream_type() == blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE) {
+  if (controller->stream_type() ==
+      blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE) {
     const media::VideoCaptureSessionId session_id =
         device_start_request_queue_.front().session_id();
     DCHECK(session_id != kFakeSessionId);
@@ -436,7 +438,8 @@
   if (error == media::VideoCaptureError::kNone) {
     if (controller->has_received_frames()) {
       LogVideoCaptureEvent(VIDEO_CAPTURE_STOP_CAPTURE_OK);
-    } else if (controller->stream_type() == blink::MEDIA_DEVICE_VIDEO_CAPTURE) {
+    } else if (controller->stream_type() ==
+               blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
       LogVideoCaptureEvent(
           VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DEVICE);
     } else {
@@ -577,8 +580,9 @@
 }
 
 base::Optional<media::VideoCaptureFormat>
-VideoCaptureManager::GetDeviceFormatInUse(blink::MediaStreamType stream_type,
-                                          const std::string& device_id) {
+VideoCaptureManager::GetDeviceFormatInUse(
+    blink::mojom::MediaStreamType stream_type,
+    const std::string& device_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // Return the currently in-use format of the device, if it's started.
   VideoCaptureController* device_in_use =
@@ -615,7 +619,7 @@
     return;
   }
 
-  DCHECK_EQ(blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
+  DCHECK_EQ(blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
             existing_device->stream_type());
   DesktopMediaID id = DesktopMediaID::Parse(existing_device->device_id());
   if (id.is_null())
@@ -700,7 +704,7 @@
 }
 
 void VideoCaptureManager::OnOpened(
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     media::VideoCaptureSessionId capture_session_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   for (auto& listener : listeners_)
@@ -708,7 +712,7 @@
 }
 
 void VideoCaptureManager::OnClosed(
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     media::VideoCaptureSessionId capture_session_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   for (auto& listener : listeners_)
@@ -796,7 +800,7 @@
 
 VideoCaptureController*
 VideoCaptureManager::LookupControllerByMediaTypeAndDeviceId(
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const std::string& device_id) const {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
@@ -882,7 +886,8 @@
 
   for (auto& controller : controllers_) {
     // Do not stop Content Video Capture devices, e.g. Tab or Screen capture.
-    if (controller->stream_type() != blink::MEDIA_DEVICE_VIDEO_CAPTURE)
+    if (controller->stream_type() !=
+        blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE)
       continue;
 
     DoStopDevice(controller.get());
@@ -895,7 +900,8 @@
   for (auto& controller : controllers_) {
     // Do not resume Content Video Capture devices, e.g. Tab or Screen capture.
     // Do not try to restart already running devices.
-    if (controller->stream_type() != blink::MEDIA_DEVICE_VIDEO_CAPTURE ||
+    if (controller->stream_type() !=
+            blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE ||
         controller->IsDeviceAlive())
       continue;
 
@@ -926,7 +932,7 @@
 
   std::vector<media::VideoCaptureSessionId> desktopcapture_session_ids;
   for (auto it : sessions_) {
-    if (IsDesktopCaptureMediaType(it.second.type))
+    if (blink::IsDesktopCaptureMediaType(it.second.type))
       desktopcapture_session_ids.push_back(it.first);
   }
 
diff --git a/content/browser/renderer_host/media/video_capture_manager.h b/content/browser/renderer_host/media/video_capture_manager.h
index 5ba6b7c5..7d663667 100644
--- a/content/browser/renderer_host/media/video_capture_manager.h
+++ b/content/browser/renderer_host/media/video_capture_manager.h
@@ -153,7 +153,7 @@
   // |stream_type|, |device_id| pair is not found. Returns in-use format of the
   // device otherwise.
   base::Optional<media::VideoCaptureFormat> GetDeviceFormatInUse(
-      blink::MediaStreamType stream_type,
+      blink::mojom::MediaStreamType stream_type,
       const std::string& device_id);
 
   // Sets the platform-dependent window ID for the desktop capture notification
@@ -206,9 +206,9 @@
       const std::vector<media::VideoCaptureDeviceInfo>& device_infos);
 
   // Helpers to report an event to our Listener.
-  void OnOpened(blink::MediaStreamType type,
+  void OnOpened(blink::mojom::MediaStreamType type,
                 media::VideoCaptureSessionId capture_session_id);
-  void OnClosed(blink::MediaStreamType type,
+  void OnClosed(blink::mojom::MediaStreamType type,
                 media::VideoCaptureSessionId capture_session_id);
 
   // Checks to see if |controller| has no clients left. If so, remove it from
@@ -221,7 +221,7 @@
   // its |serial_id|. In all cases, if not found, nullptr is returned.
   VideoCaptureController* LookupControllerBySessionId(int session_id);
   VideoCaptureController* LookupControllerByMediaTypeAndDeviceId(
-      blink::MediaStreamType type,
+      blink::mojom::MediaStreamType type,
       const std::string& device_id) const;
   bool IsControllerPointerValid(const VideoCaptureController* controller) const;
   scoped_refptr<VideoCaptureController> GetControllerSharedRef(
diff --git a/content/browser/renderer_host/media/video_capture_manager_unittest.cc b/content/browser/renderer_host/media/video_capture_manager_unittest.cc
index af1b5c1f..31bd2b3 100644
--- a/content/browser/renderer_host/media/video_capture_manager_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_manager_unittest.cc
@@ -149,9 +149,9 @@
   MockMediaStreamProviderListener() {}
   ~MockMediaStreamProviderListener() override {}
 
-  MOCK_METHOD2(Opened, void(blink::MediaStreamType, int));
-  MOCK_METHOD2(Closed, void(blink::MediaStreamType, int));
-  MOCK_METHOD2(Aborted, void(blink::MediaStreamType, int));
+  MOCK_METHOD2(Opened, void(blink::mojom::MediaStreamType, int));
+  MOCK_METHOD2(Closed, void(blink::mojom::MediaStreamType, int));
+  MOCK_METHOD2(Aborted, void(blink::mojom::MediaStreamType, int));
 };  // class MockMediaStreamProviderListener
 
 // Needed as an input argument to ConnectClient().
@@ -208,7 +208,7 @@
       const media::VideoCaptureDeviceDescriptors& descriptors) {
     blink::MediaStreamDevices devices;
     for (const auto& descriptor : descriptors) {
-      devices.emplace_back(blink::MEDIA_DEVICE_VIDEO_CAPTURE,
+      devices.emplace_back(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
                            descriptor.device_id, descriptor.GetNameAndModel());
     }
     devices_ = devices;
@@ -220,7 +220,7 @@
       const media::VideoCaptureDeviceDescriptors& descriptors) {
     blink::MediaStreamDevices devices;
     for (const auto& descriptor : descriptors) {
-      devices.emplace_back(blink::MEDIA_DISPLAY_VIDEO_CAPTURE,
+      devices.emplace_back(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
                            DesktopMediaID(DesktopMediaID::TYPE_SCREEN,
                                           DesktopMediaID::kFakeId, false)
                                .ToString(),
@@ -350,9 +350,11 @@
 // Try to open, start, stop and close a device.
 TEST_F(VideoCaptureManagerTest, CreateAndClose) {
   InSequence s;
-  EXPECT_CALL(*listener_, Opened(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Opened(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
   EXPECT_CALL(*frame_observer_, OnStarted(_));
-  EXPECT_CALL(*listener_, Closed(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Closed(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
 
   int video_session_id = vcm_->Open(devices_.front());
   VideoCaptureControllerID client_id = StartClient(video_session_id, true);
@@ -368,9 +370,11 @@
 TEST_F(VideoCaptureManagerTest, CreateAndCloseMultipleTimes) {
   InSequence s;
   for (int i = 1 ; i < 3 ; ++i) {
-    EXPECT_CALL(*listener_, Opened(blink::MEDIA_DEVICE_VIDEO_CAPTURE, i));
+    EXPECT_CALL(*listener_,
+                Opened(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, i));
     EXPECT_CALL(*frame_observer_, OnStarted(_));
-    EXPECT_CALL(*listener_, Closed(blink::MEDIA_DEVICE_VIDEO_CAPTURE, i));
+    EXPECT_CALL(*listener_,
+                Closed(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, i));
     int video_session_id = vcm_->Open(devices_.front());
     VideoCaptureControllerID client_id = StartClient(video_session_id, true);
 
@@ -386,9 +390,11 @@
 // Try to open, start, and abort a device.
 TEST_F(VideoCaptureManagerTest, CreateAndAbort) {
   InSequence s;
-  EXPECT_CALL(*listener_, Opened(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Opened(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
   EXPECT_CALL(*frame_observer_, OnStarted(_));
-  EXPECT_CALL(*listener_, Aborted(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Aborted(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
 
   int video_session_id = vcm_->Open(devices_.front());
   VideoCaptureControllerID client_id = StartClient(video_session_id, true);
@@ -429,9 +435,11 @@
 // Open the same device twice.
 TEST_F(VideoCaptureManagerTest, OpenTwice) {
   InSequence s;
-  EXPECT_CALL(*listener_, Opened(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _))
+  EXPECT_CALL(*listener_,
+              Opened(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _))
       .Times(2);
-  EXPECT_CALL(*listener_, Closed(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _))
+  EXPECT_CALL(*listener_,
+              Closed(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _))
       .Times(2);
 
   int video_session_id_first = vcm_->Open(devices_.front());
@@ -489,7 +497,8 @@
       vcm_->GetDeviceSupportedFormats(video_session_id, &supported_formats));
 
   InSequence s;
-  EXPECT_CALL(*listener_, Opened(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Opened(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
   video_session_id = vcm_->Open(devices_.front());
   base::RunLoop().RunUntilIdle();
 
@@ -520,7 +529,8 @@
   EXPECT_GT(supported_formats[1].frame_size.height(), 1);
   EXPECT_GT(supported_formats[1].frame_rate, 1);
 
-  EXPECT_CALL(*listener_, Closed(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Closed(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
   StopClient(client_id);
   supported_formats.clear();
   EXPECT_TRUE(
@@ -557,7 +567,8 @@
   EXPECT_GT(supported_formats[1].frame_rate, 1);
 
   InSequence s;
-  EXPECT_CALL(*listener_, Opened(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Opened(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
   int video_session_id = vcm_->Open(devices_.front());
   base::RunLoop().RunUntilIdle();
 
@@ -586,7 +597,8 @@
   EXPECT_GT(supported_formats[1].frame_size.height(), 1);
   EXPECT_GT(supported_formats[1].frame_rate, 1);
 
-  EXPECT_CALL(*listener_, Closed(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Closed(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
   StopClient(client_id);
   supported_formats.clear();
   EXPECT_TRUE(vcm_->GetDeviceSupportedFormats(device_id, &supported_formats));
@@ -610,7 +622,8 @@
 // use is an empty vector.
 TEST_F(VideoCaptureManagerTest, StartDeviceAndGetDeviceFormatInUse) {
   InSequence s;
-  EXPECT_CALL(*listener_, Opened(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Opened(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
   int video_session_id = vcm_->Open(devices_.front());
   base::RunLoop().RunUntilIdle();
 
@@ -634,7 +647,8 @@
   }
   formats_in_use.clear();
 
-  EXPECT_CALL(*listener_, Closed(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Closed(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
   StopClient(client_id);
   base::RunLoop().RunUntilIdle();
   // After StopClient(), the device's formats in use should be empty again.
@@ -655,32 +669,39 @@
        StartDeviceAndGetDeviceFormatInUseWithDeviceId) {
   std::string device_id = devices_.front().id;
   InSequence s;
-  EXPECT_CALL(*listener_, Opened(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Opened(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
   int video_session_id = vcm_->Open(devices_.front());
   base::RunLoop().RunUntilIdle();
 
   // Right after opening the device, we should see no format in use.
-  EXPECT_EQ(base::nullopt, vcm_->GetDeviceFormatInUse(
-                               blink::MEDIA_DEVICE_VIDEO_CAPTURE, device_id));
+  EXPECT_EQ(
+      base::nullopt,
+      vcm_->GetDeviceFormatInUse(
+          blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, device_id));
 
   EXPECT_CALL(*frame_observer_, OnStarted(_));
   VideoCaptureControllerID client_id = StartClient(video_session_id, true);
   base::RunLoop().RunUntilIdle();
   // After StartClient(), device's format in use should be valid.
   base::Optional<media::VideoCaptureFormat> format_in_use =
-      vcm_->GetDeviceFormatInUse(blink::MEDIA_DEVICE_VIDEO_CAPTURE, device_id);
+      vcm_->GetDeviceFormatInUse(
+          blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, device_id);
   EXPECT_TRUE(format_in_use.has_value());
   EXPECT_TRUE(format_in_use->IsValid());
   EXPECT_GT(format_in_use->frame_size.width(), 1);
   EXPECT_GT(format_in_use->frame_size.height(), 1);
   EXPECT_GT(format_in_use->frame_rate, 1);
 
-  EXPECT_CALL(*listener_, Closed(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Closed(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
   StopClient(client_id);
   base::RunLoop().RunUntilIdle();
   // After StopClient(), the device's format in use should be empty again.
-  EXPECT_EQ(base::nullopt, vcm_->GetDeviceFormatInUse(
-                               blink::MEDIA_DEVICE_VIDEO_CAPTURE, device_id));
+  EXPECT_EQ(
+      base::nullopt,
+      vcm_->GetDeviceFormatInUse(
+          blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, device_id));
 
   vcm_->Close(video_session_id);
   base::RunLoop().RunUntilIdle();
@@ -690,9 +711,11 @@
 // Open two different devices.
 TEST_F(VideoCaptureManagerTest, OpenTwo) {
   InSequence s;
-  EXPECT_CALL(*listener_, Opened(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _))
+  EXPECT_CALL(*listener_,
+              Opened(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _))
       .Times(2);
-  EXPECT_CALL(*listener_, Closed(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _))
+  EXPECT_CALL(*listener_,
+              Closed(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _))
       .Times(2);
 
   auto it = devices_.begin();
@@ -713,10 +736,13 @@
 TEST_F(VideoCaptureManagerTest, OpenNotExisting) {
   InSequence s;
   EXPECT_CALL(*frame_observer_, OnError(_, _));
-  EXPECT_CALL(*listener_, Opened(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
-  EXPECT_CALL(*listener_, Closed(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Opened(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Closed(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
 
-  blink::MediaStreamType stream_type = blink::MEDIA_DEVICE_VIDEO_CAPTURE;
+  blink::mojom::MediaStreamType stream_type =
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE;
   std::string device_name("device_doesnt_exist");
   std::string device_id("id_doesnt_exist");
   blink::MediaStreamDevice dummy_device(stream_type, device_id, device_name);
@@ -745,9 +771,11 @@
 // Open and start a device, close it before calling Stop.
 TEST_F(VideoCaptureManagerTest, CloseWithoutStop) {
   InSequence s;
-  EXPECT_CALL(*listener_, Opened(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Opened(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
   EXPECT_CALL(*frame_observer_, OnStarted(_));
-  EXPECT_CALL(*listener_, Closed(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Closed(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
 
   int video_session_id = vcm_->Open(devices_.front());
 
@@ -767,7 +795,8 @@
 // paused/resumed at the correct times in both single-client and multiple-client
 // scenarios.
 TEST_F(VideoCaptureManagerTest, PauseAndResumeClient) {
-  EXPECT_CALL(*listener_, Opened(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Opened(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
   EXPECT_CALL(*frame_observer_, OnStarted(_));
 
   const int video_session_id = vcm_->Open(devices_.front());
@@ -796,7 +825,8 @@
   StopClient(client_id);
   StopClient(client_id2);
 
-  EXPECT_CALL(*listener_, Closed(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Closed(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
   vcm_->Close(video_session_id);
 
   // Wait to check callbacks before removing the listener.
@@ -808,9 +838,11 @@
 // Try to open, start, pause and resume a device.
 TEST_F(VideoCaptureManagerTest, PauseAndResumeDevice) {
   InSequence s;
-  EXPECT_CALL(*listener_, Opened(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Opened(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
   EXPECT_CALL(*frame_observer_, OnStarted(_));
-  EXPECT_CALL(*listener_, Closed(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Closed(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
 
   int video_session_id = vcm_->Open(devices_.front());
   VideoCaptureControllerID client_id = StartClient(video_session_id, true);
@@ -842,9 +874,11 @@
 TEST_F(VideoCaptureManagerTest, DeviceCaptureDeviceNotClosedOnScreenlock) {
   InSequence s;
   // ScreenLocked event shouldn't affect camera capture device.
-  EXPECT_CALL(*listener_, Opened(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Opened(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
   EXPECT_CALL(*frame_observer_, OnStarted(_));
-  EXPECT_CALL(*listener_, Closed(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _))
+  EXPECT_CALL(*listener_,
+              Closed(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _))
       .Times(0);
 
   int video_session_id = vcm_->Open(devices_.front());
@@ -854,7 +888,8 @@
   screenlock_monitor_source_->GenerateScreenLockedEvent();
   Mock::VerifyAndClearExpectations(listener_.get());
 
-  EXPECT_CALL(*listener_, Closed(blink::MEDIA_DEVICE_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Closed(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, _));
   StopClient(client_id);
   vcm_->Close(video_session_id);
 
@@ -869,9 +904,11 @@
 TEST_F(VideoCaptureManagerTest, DesktopCaptureDeviceClosedOnScreenlock) {
   InSequence s;
   // ScreenLocked event should stop desktop capture device.
-  EXPECT_CALL(*listener_, Opened(blink::MEDIA_DISPLAY_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Opened(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, _));
   EXPECT_CALL(*frame_observer_, OnStarted(_));
-  EXPECT_CALL(*listener_, Closed(blink::MEDIA_DISPLAY_VIDEO_CAPTURE, _));
+  EXPECT_CALL(*listener_,
+              Closed(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, _));
 
   // Simulate we add 1 fake display media device.
   video_capture_device_factory_->SetToDefaultDevicesConfig(1);
diff --git a/content/browser/renderer_host/media/video_capture_provider.h b/content/browser/renderer_host/media/video_capture_provider.h
index 1479fee..f3aa1e1 100644
--- a/content/browser/renderer_host/media/video_capture_provider.h
+++ b/content/browser/renderer_host/media/video_capture_provider.h
@@ -18,7 +18,7 @@
 class VideoCaptureDeviceLauncher;
 
 // Note: GetDeviceInfosAsync is only relevant for devices with
-// MediaStreamType == MEDIA_DEVICE_VIDEO_CAPTURE, i.e. camera devices.
+// MediaStreamType == DEVICE_VIDEO_CAPTURE, i.e. camera devices.
 class CONTENT_EXPORT VideoCaptureProvider {
  public:
   using GetDeviceInfosCallback = base::RepeatingCallback<void(
diff --git a/content/browser/renderer_host/media/video_capture_provider_switcher.cc b/content/browser/renderer_host/media/video_capture_provider_switcher.cc
index 87e9bf3..24cf3da 100644
--- a/content/browser/renderer_host/media/video_capture_provider_switcher.cc
+++ b/content/browser/renderer_host/media/video_capture_provider_switcher.cc
@@ -24,13 +24,13 @@
   ~VideoCaptureDeviceLauncherSwitcher() override {}
 
   void LaunchDeviceAsync(const std::string& device_id,
-                         blink::MediaStreamType stream_type,
+                         blink::mojom::MediaStreamType stream_type,
                          const media::VideoCaptureParams& params,
                          base::WeakPtr<media::VideoFrameReceiver> receiver,
                          base::OnceClosure connection_lost_cb,
                          Callbacks* callbacks,
                          base::OnceClosure done_cb) override {
-    if (stream_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE) {
+    if (stream_type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
       // Use of Unretained() is safe, because |media_device_launcher_| is owned
       // by |this|.
       abort_launch_cb_ =
diff --git a/content/browser/renderer_host/media/video_capture_unittest.cc b/content/browser/renderer_host/media/video_capture_unittest.cc
index 5af6f4a4..87ab4d5e3 100644
--- a/content/browser/renderer_host/media/video_capture_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_unittest.cc
@@ -156,7 +156,8 @@
       base::RunLoop run_loop;
       media_stream_manager_->OpenDevice(
           render_process_id, render_frame_id, requester_id, page_request_id,
-          video_devices[0].device_id, blink::MEDIA_DEVICE_VIDEO_CAPTURE,
+          video_devices[0].device_id,
+          blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
           MediaDeviceSaltAndOrigin{browser_context_.GetMediaDeviceIDSalt(),
                                    browser_context_.GetMediaDeviceIDSalt(),
                                    security_origin},
diff --git a/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc b/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc
index 69f17f0..efd40bb 100644
--- a/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc
+++ b/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc
@@ -6,7 +6,6 @@
 
 #include "base/metrics/histogram_functions.h"
 #include "content/browser/renderer_host/pepper/pepper_message_filter.h"
-#include "content/browser/tracing/trace_message_filter.h"
 #include "content/common/pepper_renderer_instance_data.h"
 #include "content/public/common/process_type.h"
 #include "ipc/ipc_message_macros.h"
@@ -39,7 +38,6 @@
       new PepperMessageFilter());
   channel->AddFilter(pepper_message_filter->GetFilter());
   channel->AddFilter(browser_ppapi_host->message_filter().get());
-  channel->AddFilter((new TraceMessageFilter(render_process_id))->GetFilter());
 
   return browser_ppapi_host;
 }
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index f1e6ad7..c1de080 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -134,7 +134,7 @@
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/site_instance_impl.h"
 #include "content/browser/storage_partition_impl.h"
-#include "content/browser/tracing/trace_message_filter.h"
+#include "content/browser/tracing/background_tracing_manager_impl.h"
 #include "content/browser/webrtc/webrtc_internals.h"
 #include "content/browser/websockets/websocket_manager.h"
 #include "content/browser/webui/web_ui_controller_factory_registry.h"
@@ -1741,6 +1741,10 @@
         base::BindRepeating(&RenderProcessHostImpl::OnMojoError, id_));
     channel_->Pause();
 
+    // In single process mode, browser-side tracing and memory will cover the
+    // whole process including renderers.
+    BackgroundTracingManagerImpl::ActivateForProcess(this);
+
     fast_shutdown_started_ = false;
   }
 
@@ -1914,7 +1918,6 @@
   p2p_socket_dispatcher_host_ =
       std::make_unique<P2PSocketDispatcherHost>(GetID());
 
-  AddFilter(new TraceMessageFilter(GetID()));
   AddFilter(new ResolveProxyMsgHelper(GetID()));
 
   scoped_refptr<ServiceWorkerContextWrapper> service_worker_context(
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index e801cf52..2695f800 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -62,7 +62,7 @@
 #include "third_party/blink/public/mojom/broadcastchannel/broadcast_channel.mojom.h"
 #include "third_party/blink/public/mojom/dom_storage/storage_partition_service.mojom.h"
 #include "third_party/blink/public/mojom/filesystem/file_system.mojom.h"
-#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-shared.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 
diff --git a/content/browser/renderer_host/render_process_host_unittest.cc b/content/browser/renderer_host/render_process_host_unittest.cc
index 2cdae23..ce4231a 100644
--- a/content/browser/renderer_host/render_process_host_unittest.cc
+++ b/content/browser/renderer_host/render_process_host_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
+#include "content/browser/child_process_security_policy_impl.h"
 #include "content/common/frame_messages.h"
 #include "content/common/frame_owner_properties.h"
 #include "content/public/browser/browser_context.h"
@@ -196,7 +197,14 @@
   // the newest unmatched service worker's process (i.e., sw_host2).
   scoped_refptr<SiteInstanceImpl> site_instance1 =
       SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
-  EXPECT_EQ(sw_host2, site_instance1->GetProcess());
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(site_instance1->IsDefaultSiteInstance());
+    // TODO(acolwell): Remove once CreateForServiceWorker() can return a
+    // default SiteInstance.
+    EXPECT_NE(sw_host2, site_instance1->GetProcess());
+  } else {
+    EXPECT_EQ(sw_host2, site_instance1->GetProcess());
+  }
 
   // Getting a RenderProcessHost for a navigation to the same site must reuse
   // the newest unmatched service worker's process (i.e., sw_host1). sw_host2
@@ -204,14 +212,25 @@
   // with a corresponding unmatched service worker.
   scoped_refptr<SiteInstanceImpl> site_instance2 =
       SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
-  EXPECT_EQ(sw_host1, site_instance2->GetProcess());
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(site_instance2->IsDefaultSiteInstance());
+    // TODO(acolwell): Remove once CreateForServiceWorker() can return a
+    // default SiteInstance.
+    EXPECT_NE(sw_host1, site_instance2->GetProcess());
+  } else {
+    EXPECT_EQ(sw_host1, site_instance2->GetProcess());
+  }
 
   // Getting a RenderProcessHost for a navigation should return a new process
   // because there is no unmatched service worker's process.
   scoped_refptr<SiteInstanceImpl> site_instance3 =
       SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
-  EXPECT_NE(sw_host1, site_instance3->GetProcess());
-  EXPECT_NE(sw_host2, site_instance3->GetProcess());
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(site_instance3->IsDefaultSiteInstance());
+  } else {
+    EXPECT_NE(sw_host1, site_instance3->GetProcess());
+    EXPECT_NE(sw_host2, site_instance3->GetProcess());
+  }
 }
 
 class UnsuitableHostContentBrowserClient : public ContentBrowserClient {
@@ -303,7 +322,14 @@
   // the newest unmatched service worker's process (i.e., sw_host2).
   scoped_refptr<SiteInstanceImpl> site_instance1 =
       SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
-  EXPECT_EQ(sw_host2, site_instance1->GetProcess());
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(site_instance1->IsDefaultSiteInstance());
+    // TODO(acolwell): Remove once CreateForServiceWorker() can return
+    // a default SiteInstance.
+    EXPECT_NE(sw_host2, site_instance1->GetProcess());
+  } else {
+    EXPECT_EQ(sw_host2, site_instance1->GetProcess());
+  }
 
   // Getting a RenderProcessHost for a navigation to the same site must reuse
   // the newest unmatched service worker's process (i.e., sw_host1). sw_host2
@@ -311,7 +337,14 @@
   // with a corresponding unmatched service worker.
   scoped_refptr<SiteInstanceImpl> site_instance2 =
       SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
-  EXPECT_EQ(sw_host1, site_instance2->GetProcess());
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(site_instance2->IsDefaultSiteInstance());
+    // TODO(acolwell): Remove once CreateForServiceWorker() can return
+    // a default SiteInstance.
+    EXPECT_NE(sw_host1, site_instance2->GetProcess());
+  } else {
+    EXPECT_EQ(sw_host1, site_instance2->GetProcess());
+  }
 }
 
 TEST_F(RenderProcessHostUnitTest,
@@ -337,14 +370,28 @@
   scoped_refptr<SiteInstanceImpl> sw_site_instance3 =
       SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
   RenderProcessHost* sw_host3 = sw_site_instance3->GetProcess();
-  EXPECT_EQ(sw_host1, sw_host3);
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(sw_site_instance3->IsDefaultSiteInstance());
+    // TODO(acolwell: Remove once CreateForServiceWorker() can return a
+    // default SiteInstance.
+    EXPECT_NE(sw_host1, sw_host3);
+  } else {
+    EXPECT_EQ(sw_host1, sw_host3);
+  }
 
   // Getting a RenderProcessHost for a navigation to the same site again with
   // process-per-site flag should reuse the unmatched service worker's process.
   scoped_refptr<SiteInstanceImpl> sw_site_instance4 =
       SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
   RenderProcessHost* sw_host4 = sw_site_instance4->GetProcess();
-  EXPECT_EQ(sw_host1, sw_host4);
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(sw_site_instance4->IsDefaultSiteInstance());
+    // TODO(acolwell: Remove once CreateForServiceWorker() can return a
+    // default SiteInstance.
+    EXPECT_NE(sw_host1, sw_host4);
+  } else {
+    EXPECT_EQ(sw_host1, sw_host4);
+  }
 }
 
 TEST_F(RenderProcessHostUnitTest, DoNotReuseOtherSiteServiceWorkerProcess) {
@@ -369,6 +416,10 @@
   const GURL kUrl1("http://foo.com");
   const GURL kUrl2("http://bar.com");
 
+  // Isolate |kUrl1| so we can't get a default SiteInstance for it.
+  ChildProcessSecurityPolicyImpl::GetInstance()->AddIsolatedOrigins(
+      {url::Origin::Create(kUrl1)}, browser_context());
+
   // At first, trying to get a RenderProcessHost with the
   // REUSE_PENDING_OR_COMMITTED_SITE policy should return a new process.
   scoped_refptr<SiteInstanceImpl> site_instance =
@@ -558,6 +609,10 @@
   main_test_rfh()->SendBeforeUnloadACK(true);
   int speculative_process_host_id =
       contents()->GetPendingMainFrame()->GetProcess()->GetID();
+  bool speculative_is_default_site_instance = contents()
+                                                  ->GetPendingMainFrame()
+                                                  ->GetSiteInstance()
+                                                  ->IsDefaultSiteInstance();
   site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting(
       browser_context(), kUrl);
   EXPECT_EQ(speculative_process_host_id, site_instance->GetProcess()->GetID());
@@ -582,7 +637,16 @@
   site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting(
       browser_context(), kRedirectUrl2);
   EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess());
-  EXPECT_NE(speculative_process_host_id, site_instance->GetProcess()->GetID());
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(speculative_is_default_site_instance);
+    // The process ID should be the same as the default SiteInstance because
+    // kRedirectUrl1 and kRedirectUrl2 do not require a dedicated process.
+    EXPECT_EQ(speculative_process_host_id,
+              site_instance->GetProcess()->GetID());
+  } else {
+    EXPECT_NE(speculative_process_host_id,
+              site_instance->GetProcess()->GetID());
+  }
 
   // Once the navigation is ready to commit, Getting RenderProcessHost with the
   // REUSE_PENDING_OR_COMMITTED_SITE policy should return the new speculative
@@ -625,7 +689,9 @@
   // Getting a RenderProcessHost with the REUSE_PENDING_OR_COMMITTED_SITE policy
   // should no longer return the process of the main RFH, as the RFH is
   // registered with the normal site URL.
-  EffectiveURLContentBrowserClient modified_client(kUrl, kModifiedSiteUrl);
+  EffectiveURLContentBrowserClient modified_client(
+      kUrl, kModifiedSiteUrl,
+      /* requires_dedicated_process */ false);
   ContentBrowserClient* regular_client =
       SetBrowserClientForTesting(&modified_client);
   site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting(
@@ -699,7 +765,8 @@
   // Getting a RenderProcessHost with the REUSE_PENDING_OR_COMMITTED_SITE policy
   // should no longer return the process of the main RFH, as the RFH is
   // registered with the normal site URL.
-  EffectiveURLContentBrowserClient modified_client(kUrl, kModifiedSiteUrl);
+  EffectiveURLContentBrowserClient modified_client(
+      kUrl, kModifiedSiteUrl, /* requires_dedicated_process */ false);
   ContentBrowserClient* regular_client =
       SetBrowserClientForTesting(&modified_client);
   site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting(
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index d1cf894..8b87570b 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -1600,7 +1600,7 @@
 void RenderWidgetHostImpl::RemoveKeyPressEventCallback(
     const KeyPressEventCallback& callback) {
   for (size_t i = 0; i < key_press_event_callbacks_.size(); ++i) {
-    if (key_press_event_callbacks_[i].Equals(callback)) {
+    if (key_press_event_callbacks_[i] == callback) {
       key_press_event_callbacks_.erase(
           key_press_event_callbacks_.begin() + i);
       return;
@@ -1616,7 +1616,7 @@
 void RenderWidgetHostImpl::RemoveMouseEventCallback(
     const MouseEventCallback& callback) {
   for (size_t i = 0; i < mouse_event_callbacks_.size(); ++i) {
-    if (mouse_event_callbacks_[i].Equals(callback)) {
+    if (mouse_event_callbacks_[i] == callback) {
       mouse_event_callbacks_.erase(mouse_event_callbacks_.begin() + i);
       return;
     }
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc
index e7dee485..81f81bc 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.cc
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc
@@ -1389,6 +1389,13 @@
           .view->GetRenderWidgetHost());
 }
 
+void RenderWidgetHostInputEventRouter::GetRenderWidgetHostAtPointAsynchronously(
+    RenderWidgetHostViewBase* root_view,
+    const gfx::PointF& point,
+    RenderWidgetTargeter::RenderWidgetHostAtPointCallback callback) {
+  event_targeter_->FindTargetAndCallback(root_view, point, std::move(callback));
+}
+
 RenderWidgetTargetResult
 RenderWidgetHostInputEventRouter::FindTouchscreenGestureEventTarget(
     RenderWidgetHostViewBase* root_view,
@@ -1771,6 +1778,15 @@
 }
 
 RenderWidgetTargetResult
+RenderWidgetHostInputEventRouter::FindTargetSynchronouslyAtLocation(
+    RenderWidgetHostViewBase* root_view,
+    const gfx::PointF& location) {
+  gfx::PointF transformed_pt;  // This is already in the result
+  return FindViewAtLocation(root_view, location, gfx::PointF() /* not used */,
+                            viz::EventSource::MOUSE, &transformed_pt);
+}
+
+RenderWidgetTargetResult
 RenderWidgetHostInputEventRouter::FindTargetSynchronously(
     RenderWidgetHostViewBase* root_view,
     const blink::WebInputEvent& event) {
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.h b/content/browser/renderer_host/render_widget_host_input_event_router.h
index e46df79..748e0c5 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.h
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.h
@@ -135,6 +135,17 @@
       const gfx::PointF& point,
       gfx::PointF* transformed_point);
 
+  // Finds the RenderWidgetHostImpl inside the |root_view| at |point| where
+  // |point| is with respect to |root_view|'s coordinates. If a RWHI is found,
+  // it is passed along with the coordinate of the point with
+  // respect to the RWHI's coordinates to the callback function. If
+  // |root_view| is nullptr or RWHI is not found, the callback is called with
+  // nullptr and no location.
+  void GetRenderWidgetHostAtPointAsynchronously(
+      RenderWidgetHostViewBase* root_view,
+      const gfx::PointF& point,
+      RenderWidgetTargeter::RenderWidgetHostAtPointCallback callback);
+
   // RenderWidgetTargeter::Delegate:
   RenderWidgetHostViewBase* FindViewFromFrameSinkId(
       const viz::FrameSinkId& frame_sink_id) const override;
@@ -312,6 +323,10 @@
                                       const RenderWidgetHostViewBase* view);
 
   // RenderWidgetTargeter::Delegate:
+  RenderWidgetTargetResult FindTargetSynchronouslyAtLocation(
+      RenderWidgetHostViewBase* root_view,
+      const gfx::PointF& location) override;
+
   RenderWidgetTargetResult FindTargetSynchronously(
       RenderWidgetHostViewBase* root_view,
       const blink::WebInputEvent& event) override;
diff --git a/content/browser/renderer_host/render_widget_targeter.cc b/content/browser/renderer_host/render_widget_targeter.cc
index 2f540fb..83ee7d4 100644
--- a/content/browser/renderer_host/render_widget_targeter.cc
+++ b/content/browser/renderer_host/render_widget_targeter.cc
@@ -22,17 +22,6 @@
 
 namespace {
 
-bool MergeEventIfPossible(const blink::WebInputEvent& event,
-                          ui::WebScopedInputEvent* blink_event) {
-  if (!blink::WebInputEvent::IsTouchEventType(event.GetType()) &&
-      !blink::WebInputEvent::IsGestureEventType(event.GetType()) &&
-      ui::CanCoalesce(event, **blink_event)) {
-    ui::Coalesce(event, blink_event->get());
-    return true;
-  }
-  return false;
-}
-
 gfx::PointF ComputeEventLocation(const blink::WebInputEvent& event) {
   if (blink::WebInputEvent::IsMouseEventType(event.GetType()) ||
       event.GetType() == blink::WebInputEvent::kMouseWheel) {
@@ -118,7 +107,24 @@
 
 RenderWidgetTargetResult::~RenderWidgetTargetResult() = default;
 
-RenderWidgetTargeter::TargetingRequest::TargetingRequest() = default;
+RenderWidgetTargeter::TargetingRequest::TargetingRequest(
+    base::WeakPtr<RenderWidgetHostViewBase> root_view,
+    const blink::WebInputEvent& event,
+    const ui::LatencyInfo& latency) {
+  this->root_view = std::move(root_view);
+  this->location = ComputeEventLocation(event);
+  this->event = ui::WebInputEventTraits::Clone(event);
+  this->latency = latency;
+}
+
+RenderWidgetTargeter::TargetingRequest::TargetingRequest(
+    base::WeakPtr<RenderWidgetHostViewBase> root_view,
+    const gfx::PointF& location,
+    RenderWidgetHostAtPointCallback callback) {
+  this->root_view = std::move(root_view);
+  this->location = location;
+  this->callback = std::move(callback);
+}
 
 RenderWidgetTargeter::TargetingRequest::TargetingRequest(
     TargetingRequest&& request) = default;
@@ -128,6 +134,68 @@
 
 RenderWidgetTargeter::TargetingRequest::~TargetingRequest() = default;
 
+void RenderWidgetTargeter::TargetingRequest::RunCallback(
+    RenderWidgetHostViewBase* target,
+    base::Optional<gfx::PointF> point) {
+  if (!callback.is_null()) {
+    std::move(callback).Run(target ? target->GetWeakPtr() : nullptr, point);
+  }
+}
+
+bool RenderWidgetTargeter::TargetingRequest::MergeEventIfPossible(
+    const blink::WebInputEvent& new_event) {
+  if (event && !blink::WebInputEvent::IsTouchEventType(new_event.GetType()) &&
+      !blink::WebInputEvent::IsGestureEventType(new_event.GetType()) &&
+      ui::CanCoalesce(new_event, *event.get())) {
+    ui::Coalesce(new_event, event.get());
+    return true;
+  }
+  return false;
+}
+
+void RenderWidgetTargeter::TargetingRequest::StartQueueingTimeTracker() {
+  tracker =
+      std::make_unique<TracingUmaTracker>("Event.AsyncTargeting.TimeInQueue");
+}
+
+void RenderWidgetTargeter::TargetingRequest::StopQueueingTimeTracker() {
+  if (tracker)
+    tracker->Stop();
+}
+
+bool RenderWidgetTargeter::TargetingRequest::IsWebInputEventRequest() const {
+  return !!event;
+}
+
+const blink::WebInputEvent& RenderWidgetTargeter::TargetingRequest::GetEvent()
+    const {
+  return *event.get();
+}
+
+RenderWidgetHostViewBase* RenderWidgetTargeter::TargetingRequest::GetRootView()
+    const {
+  return root_view.get();
+}
+
+gfx::PointF RenderWidgetTargeter::TargetingRequest::GetLocation() const {
+  return location;
+}
+
+viz::FrameSinkId
+RenderWidgetTargeter::TargetingRequest::GetExpectedFrameSinkId() const {
+  return expected_frame_sink_id;
+}
+
+void RenderWidgetTargeter::TargetingRequest::SetExpectedFrameSinkId(
+    const viz::FrameSinkId& id) {
+  expected_frame_sink_id = id;
+}
+
+const ui::LatencyInfo& RenderWidgetTargeter::TargetingRequest::GetLatency()
+    const {
+  return latency;
+}
+
 RenderWidgetTargeter::RenderWidgetTargeter(Delegate* delegate)
     : trace_id_(base::RandUint64()),
       is_viz_hit_testing_debug_enabled_(
@@ -152,35 +220,48 @@
            static_cast<const blink::WebGestureEvent&>(event).SourceDevice() ==
                blink::WebGestureDevice::kTouchpad)));
 
+  if (!requests_.empty()) {
+    auto& request = requests_.back();
+    if (request.MergeEventIfPossible(event))
+      return;
+  }
+
+  TargetingRequest request(root_view->GetWeakPtr(), event, latency);
+
+  ResolveTargetingRequest(std::move(request));
+}
+
+void RenderWidgetTargeter::FindTargetAndCallback(
+    RenderWidgetHostViewBase* root_view,
+    const gfx::PointF& point,
+    RenderWidgetHostAtPointCallback callback) {
+  TargetingRequest request(root_view->GetWeakPtr(), point, std::move(callback));
+
+  ResolveTargetingRequest(std::move(request));
+}
+
+void RenderWidgetTargeter::ResolveTargetingRequest(TargetingRequest request) {
   if (request_in_flight_) {
-    if (!requests_.empty()) {
-      auto& request = requests_.back();
-      if (MergeEventIfPossible(event, &request.event))
-        return;
-    }
-    TargetingRequest request;
-    request.root_view = root_view->GetWeakPtr();
-    request.event = ui::WebInputEventTraits::Clone(event);
-    request.latency = latency;
-    request.tracker =
-        std::make_unique<TracingUmaTracker>("Event.AsyncTargeting.TimeInQueue");
+    request.StartQueueingTimeTracker();
     requests_.push(std::move(request));
     return;
   }
 
-  RenderWidgetTargetResult result =
-      delegate_->FindTargetSynchronously(root_view, event);
-
-  const gfx::PointF event_location = ComputeEventLocation(event);
-
+  RenderWidgetTargetResult result;
+  if (request.IsWebInputEventRequest()) {
+    result = delegate_->FindTargetSynchronously(request.GetRootView(),
+                                                request.GetEvent());
+  } else {
+    result = delegate_->FindTargetSynchronouslyAtLocation(
+        request.GetRootView(), request.GetLocation());
+  }
   RenderWidgetHostViewBase* target = result.view;
-  auto* event_ptr = &event;
   async_depth_ = 0;
   if (result.should_query_view) {
     TRACE_EVENT_WITH_FLOW2(
         "viz,benchmark", "Event.Pipeline", TRACE_ID_GLOBAL(trace_id_),
         TRACE_EVENT_FLAG_FLOW_OUT, "step", "QueryClient(Start)",
-        "event_location", event_location.ToString());
+        "event_location", request.GetLocation().ToString());
 
     // TODO(kenrb, sadrul): When all event types support asynchronous hit
     // testing, we should be able to have FindTargetSynchronously return the
@@ -190,17 +271,15 @@
     // root_view and the original event location for the initial query.
     // Do not compare hit test results if we are forced to do async hit testing
     // by HitTestQuery.
-    QueryClient(root_view, root_view, *event_ptr, latency, event_location,
-                nullptr, gfx::PointF());
+    QueryClient(std::move(request));
   } else {
-    FoundTarget(root_view, target, *event_ptr, latency, result.target_location,
-                result.latched_target, viz::FrameSinkId());
+    FoundTarget(target, result.target_location, result.latched_target,
+                &request);
     // Verify the event targeting results from surface layer viz hit testing if
     // --use-viz-hit-test-surface-layer is enabled.
     if (result.should_verify_result && !target->IsRenderWidgetHostViewGuest()) {
-      QueryAndVerifyClient(root_view, root_view, *event_ptr, latency,
-                           event_location, nullptr, gfx::PointF(),
-                           target->GetFrameSinkId());
+      request.SetExpectedFrameSinkId(target->GetFrameSinkId());
+      QueryAndVerifyClient(std::move(request));
     }
   }
 }
@@ -214,17 +293,14 @@
 }
 
 void RenderWidgetTargeter::QueryClientInternal(
-    RenderWidgetHostViewBase* root_view,
     RenderWidgetHostViewBase* target,
-    const blink::WebInputEvent& event,
-    const ui::LatencyInfo& latency,
     const gfx::PointF& target_location,
     RenderWidgetHostViewBase* last_request_target,
     const gfx::PointF& last_target_location,
-    const viz::FrameSinkId& expected_frame_sink_id) {
+    TargetingRequest request) {
   // Async event targeting and verifying use two different queues, so they don't
   // block each other.
-  bool is_verifying = expected_frame_sink_id.is_valid();
+  bool is_verifying = request.GetExpectedFrameSinkId().is_valid();
   DCHECK((!is_verifying && !request_in_flight_) ||
          (is_verifying && !verify_request_in_flight_));
 
@@ -233,15 +309,14 @@
   // understand why this happens. https://crbug.com/859492.
   // We do not verify hit testing result under this circumstance.
   if (!target_client) {
-    FoundTarget(root_view, target, event, latency, target_location, false,
-                viz::FrameSinkId());
+    FoundTarget(target, target_location, false, &request);
     return;
   }
 
   if (is_verifying) {
-    verify_request_in_flight_ = true;
+    verify_request_in_flight_ = std::move(request);
   } else {
-    request_in_flight_ = true;
+    request_in_flight_ = std::move(request);
     async_depth_++;
   }
   TracingUmaTracker tracker("Event.AsyncTargeting.ResponseTime");
@@ -250,67 +325,46 @@
   hit_test_timeout.reset(new OneShotTimeoutMonitor(
       base::BindOnce(
           &RenderWidgetTargeter::AsyncHitTestTimedOut,
-          weak_ptr_factory_.GetWeakPtr(), root_view->GetWeakPtr(),
-          target->GetWeakPtr(), target_location,
+          weak_ptr_factory_.GetWeakPtr(), target->GetWeakPtr(), target_location,
           last_request_target ? last_request_target->GetWeakPtr() : nullptr,
-          last_target_location, ui::WebInputEventTraits::Clone(event), latency,
-          expected_frame_sink_id),
+          last_target_location, is_verifying),
       async_hit_test_timeout_delay_));
 
   TRACE_EVENT_WITH_FLOW2(
       "viz,benchmark", "Event.Pipeline", TRACE_ID_GLOBAL(trace_id_),
       TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
-      "QueryClient", "event", blink::WebInputEvent::GetName(event.GetType()));
+      "QueryClient", "event_location", request.GetLocation().ToString());
 
   target_client->FrameSinkIdAt(
       target_location, trace_id_,
       base::BindOnce(
           &RenderWidgetTargeter::FoundFrameSinkId,
-          weak_ptr_factory_.GetWeakPtr(), root_view->GetWeakPtr(),
-          target->GetWeakPtr(), ui::WebInputEventTraits::Clone(event), latency,
+          weak_ptr_factory_.GetWeakPtr(), target->GetWeakPtr(),
           is_verifying ? ++last_verify_request_id_ : ++last_request_id_,
-          target_location, std::move(tracker), expected_frame_sink_id));
+          target_location, std::move(tracker), is_verifying));
 }
 
-void RenderWidgetTargeter::QueryClient(
-    RenderWidgetHostViewBase* root_view,
-    RenderWidgetHostViewBase* target,
-    const blink::WebInputEvent& event,
-    const ui::LatencyInfo& latency,
-    const gfx::PointF& target_location,
-    RenderWidgetHostViewBase* last_request_target,
-    const gfx::PointF& last_target_location) {
-  QueryClientInternal(root_view, target, event, latency, target_location,
-                      last_request_target, last_target_location,
-                      viz::FrameSinkId());
+void RenderWidgetTargeter::QueryClient(TargetingRequest request) {
+  auto* target = request.GetRootView();
+  auto target_location = request.GetLocation();
+  QueryClientInternal(target, target_location, nullptr, gfx::PointF(),
+                      std::move(request));
 }
 
-void RenderWidgetTargeter::QueryAndVerifyClient(
-    RenderWidgetHostViewBase* root_view,
-    RenderWidgetHostViewBase* target,
-    const blink::WebInputEvent& event,
-    const ui::LatencyInfo& latency,
-    const gfx::PointF& target_location,
-    RenderWidgetHostViewBase* last_request_target,
-    const gfx::PointF& last_target_location,
-    const viz::FrameSinkId& expected_frame_sink_id) {
+void RenderWidgetTargeter::QueryAndVerifyClient(TargetingRequest request) {
   if (verify_request_in_flight_) {
-    TargetingRequest request;
-    request.root_view = root_view->GetWeakPtr();
-    request.event = ui::WebInputEventTraits::Clone(event);
-    request.latency = latency;
-    request.expected_frame_sink_id = expected_frame_sink_id;
     verify_requests_.push(std::move(request));
     return;
   }
-  QueryClientInternal(root_view, target, event, latency, target_location,
-                      last_request_target, last_target_location,
-                      expected_frame_sink_id);
+  auto* target = request.GetRootView();
+  auto target_location = request.GetLocation();
+  QueryClientInternal(target, target_location, nullptr, gfx::PointF(),
+                      std::move(request));
 }
 
 void RenderWidgetTargeter::FlushEventQueue(bool is_verifying) {
   bool events_being_flushed = false;
-  bool& request_in_flight =
+  base::Optional<TargetingRequest>& request_in_flight =
       is_verifying ? verify_request_in_flight_ : request_in_flight_;
   auto* requests = is_verifying ? &verify_requests_ : &requests_;
   while (!request_in_flight && !requests->empty()) {
@@ -318,11 +372,10 @@
     requests->pop();
     // The root-view has gone away. Ignore this event, and try to process the
     // next event.
-    if (!request.root_view) {
+    if (!request.GetRootView()) {
       continue;
     }
-    if (request.tracker)
-      request.tracker->Stop();
+    request.StopQueueingTimeTracker();
     // Only notify the delegate once that the current event queue is being
     // flushed. Once all the events are flushed, notify the delegate again.
     if (!is_verifying && !events_being_flushed) {
@@ -330,13 +383,9 @@
       events_being_flushed = true;
     }
     if (is_verifying) {
-      QueryAndVerifyClient(request.root_view.get(), request.root_view.get(),
-                           *request.event, request.latency,
-                           ComputeEventLocation(*request.event), nullptr,
-                           gfx::PointF(), request.expected_frame_sink_id);
+      QueryAndVerifyClient(std::move(request));
     } else {
-      FindTargetAndDispatch(request.root_view.get(), *request.event,
-                            request.latency);
+      ResolveTargetingRequest(std::move(request));
     }
   }
   if (!is_verifying)
@@ -344,25 +393,24 @@
 }
 
 void RenderWidgetTargeter::FoundFrameSinkId(
-    base::WeakPtr<RenderWidgetHostViewBase> root_view,
     base::WeakPtr<RenderWidgetHostViewBase> target,
-    ui::WebScopedInputEvent event,
-    const ui::LatencyInfo& latency,
     uint32_t request_id,
     const gfx::PointF& target_location,
     TracingUmaTracker tracker,
-    const viz::FrameSinkId& expected_frame_sink_id,
+    const bool is_verification_request,
     const viz::FrameSinkId& frame_sink_id,
     const gfx::PointF& transformed_location) {
-  if (expected_frame_sink_id.is_valid()) {
+  if (is_verification_request) {
     tracker.Stop();
   } else {
     tracker.StopAndRecord();
   }
-  uint32_t last_id = expected_frame_sink_id.is_valid() ? last_verify_request_id_
-                                                       : last_request_id_;
-  bool in_flight = expected_frame_sink_id.is_valid() ? verify_request_in_flight_
-                                                     : request_in_flight_;
+
+  uint32_t last_id =
+      is_verification_request ? last_verify_request_id_ : last_request_id_;
+  bool in_flight = is_verification_request
+                       ? verify_request_in_flight_.has_value()
+                       : request_in_flight_.has_value();
   if (request_id != last_id || !in_flight) {
     // This is a response to a request that already timed out, so the event
     // should have already been dispatched. Mark the renderer as responsive
@@ -371,15 +419,20 @@
     return;
   }
 
-  if (expected_frame_sink_id.is_valid()) {
-    verify_request_in_flight_ = false;
+  TargetingRequest request = is_verification_request
+                                 ? std::move(verify_request_in_flight_.value())
+                                 : std::move(request_in_flight_.value());
+
+  if (request.GetExpectedFrameSinkId().is_valid()) {
+    verify_request_in_flight_.reset();
     async_verify_hit_test_timeout_.reset(nullptr);
   } else {
-    request_in_flight_ = false;
+    request_in_flight_.reset();
     async_hit_test_timeout_.reset(nullptr);
 
-    if (is_viz_hit_testing_debug_enabled_ &&
-        event->GetType() == blink::WebInputEvent::Type::kMouseDown) {
+    if (is_viz_hit_testing_debug_enabled_ && request.IsWebInputEventRequest() &&
+        request.GetEvent().GetType() ==
+            blink::WebInputEvent::Type::kMouseDown) {
       hit_test_async_queried_debug_queue_.push_back(target->GetFrameSinkId());
     }
   }
@@ -400,46 +453,45 @@
                              TRACE_EVENT_FLAG_FLOW_IN, "step", "FoundTarget");
     }
 
-    FoundTarget(root_view.get(), view, *event, latency, transformed_location,
-                false, expected_frame_sink_id);
+    FoundTarget(view, transformed_location, false, &request);
   } else {
-    QueryClientInternal(root_view.get(), view, *event, latency,
-                        transformed_location, target.get(), target_location,
-                        expected_frame_sink_id);
+    QueryClientInternal(view, transformed_location, target.get(),
+                        target_location, std::move(request));
   }
 }
 
 void RenderWidgetTargeter::FoundTarget(
-    RenderWidgetHostViewBase* root_view,
     RenderWidgetHostViewBase* target,
-    const blink::WebInputEvent& event,
-    const ui::LatencyInfo& latency,
     const base::Optional<gfx::PointF>& target_location,
     bool latched_target,
-    const viz::FrameSinkId& expected_frame_sink_id) {
+    TargetingRequest* request) {
+  DCHECK(request);
+
   if (SiteIsolationPolicy::UseDedicatedProcessesForAllSites() &&
-      !latched_target && !expected_frame_sink_id.is_valid()) {
+      !latched_target && !request->GetExpectedFrameSinkId().is_valid()) {
     UMA_HISTOGRAM_COUNTS_100("Event.AsyncTargeting.AsyncClientDepth",
                              async_depth_);
   }
 
   // RenderWidgetHostViewMac can be deleted asynchronously, in which case the
   // View will be valid but there will no longer be a RenderWidgetHostImpl.
-  if (!root_view || !root_view->GetRenderWidgetHost())
+  if (!request->GetRootView() || !request->GetRootView()->GetRenderWidgetHost())
     return;
 
   if (is_viz_hit_testing_debug_enabled_ &&
       !hit_test_async_queried_debug_queue_.empty()) {
     GetHostFrameSinkManager()->SetHitTestAsyncQueriedDebugRegions(
-        root_view->GetRootFrameSinkId(), hit_test_async_queried_debug_queue_);
+        request->GetRootView()->GetRootFrameSinkId(),
+        hit_test_async_queried_debug_queue_);
     hit_test_async_queried_debug_queue_.clear();
   }
 
   if (features::IsVizHitTestingSurfaceLayerEnabled() &&
-      expected_frame_sink_id.is_valid()) {
+      request->GetExpectedFrameSinkId().is_valid()) {
     static const char* kResultsMatchHistogramName =
         "Event.VizHitTestSurfaceLayer.ResultsMatch";
-    bool results_match = target->GetFrameSinkId() == expected_frame_sink_id;
+    bool results_match =
+        target->GetFrameSinkId() == request->GetExpectedFrameSinkId();
     HitTestResultsMatch match_result =
         HitTestResultsMatch::kHitTestResultChanged;
     if (results_match) {
@@ -448,10 +500,17 @@
       // If the results do not match, it is possible that the hit test data
       // changed during verification. We do synchronous hit test again to make
       // sure the result is reliable.
-      RenderWidgetTargetResult result =
-          delegate_->FindTargetSynchronously(root_view, event);
+      RenderWidgetTargetResult result;
+      if (request->IsWebInputEventRequest()) {
+        result = delegate_->FindTargetSynchronously(request->GetRootView(),
+                                                    request->GetEvent());
+      } else {
+        result = delegate_->FindTargetSynchronouslyAtLocation(
+            request->GetRootView(), request->GetLocation());
+      }
+
       if (!result.should_query_view && result.view &&
-          expected_frame_sink_id == result.view->GetFrameSinkId()) {
+          request->GetExpectedFrameSinkId() == result.view->GetFrameSinkId()) {
         // If the result did not change, it is likely that viz hit test finds
         // the wrong target.
         match_result = HitTestResultsMatch::kDoNotMatch;
@@ -465,48 +524,53 @@
     FlushEventQueue(true);
     return;
   }
-  delegate_->DispatchEventToTarget(root_view, target, event, latency,
-                                   target_location);
+  if (request->IsWebInputEventRequest()) {
+    delegate_->DispatchEventToTarget(request->GetRootView(), target,
+                                     request->GetEvent(), request->GetLatency(),
+                                     target_location);
+  } else {
+    request->RunCallback(target, target_location);
+  }
   FlushEventQueue(false);
 }
 
 void RenderWidgetTargeter::AsyncHitTestTimedOut(
-    base::WeakPtr<RenderWidgetHostViewBase> current_request_root_view,
     base::WeakPtr<RenderWidgetHostViewBase> current_request_target,
     const gfx::PointF& current_target_location,
     base::WeakPtr<RenderWidgetHostViewBase> last_request_target,
     const gfx::PointF& last_target_location,
-    ui::WebScopedInputEvent event,
-    const ui::LatencyInfo& latency,
-    const viz::FrameSinkId& expected_frame_sink_id) {
+    const bool is_verification_request) {
   DCHECK(request_in_flight_ || verify_request_in_flight_);
+
+  TargetingRequest request = is_verification_request
+                                 ? std::move(verify_request_in_flight_.value())
+                                 : std::move(request_in_flight_.value());
+
   // If we time out during a verification, we early out to avoid dispatching
   // event to root frame.
-  if (expected_frame_sink_id.is_valid()) {
-    verify_request_in_flight_ = false;
+  if (request.GetExpectedFrameSinkId().is_valid()) {
+    verify_request_in_flight_.reset();
     return;
   } else {
-    request_in_flight_ = false;
+    request_in_flight_.reset();
   }
 
-  if (!current_request_root_view)
+  if (!request.GetRootView())
     return;
 
   // Mark view as unresponsive so further events will not be sent to it.
   if (current_request_target)
     unresponsive_views_.insert(current_request_target.get());
 
-  if (current_request_root_view.get() == current_request_target.get()) {
+  if (request.GetRootView() == current_request_target.get()) {
     // When a request to the top-level frame times out then the event gets
     // sent there anyway. It will trigger the hung renderer dialog if the
     // renderer fails to process it.
-    FoundTarget(current_request_root_view.get(),
-                current_request_root_view.get(), *event, latency,
-                current_target_location, false, viz::FrameSinkId());
+    FoundTarget(current_request_target.get(), current_target_location, false,
+                &request);
   } else {
-    FoundTarget(current_request_root_view.get(), last_request_target.get(),
-                *event, latency, last_target_location, false,
-                viz::FrameSinkId());
+    FoundTarget(last_request_target.get(), last_target_location, false,
+                &request);
   }
 }
 
diff --git a/content/browser/renderer_host/render_widget_targeter.h b/content/browser/renderer_host/render_widget_targeter.h
index d6b9834..866dd3f1 100644
--- a/content/browser/renderer_host/render_widget_targeter.h
+++ b/content/browser/renderer_host/render_widget_targeter.h
@@ -6,12 +6,12 @@
 #define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_TARGETER_H_
 
 #include <queue>
-#include <unordered_set>
 
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/time/time.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
 #include "content/common/content_constants_internal.h"
 #include "content/common/content_export.h"
 #include "ui/events/blink/web_input_event_traits.h"
@@ -60,10 +60,18 @@
 
 class RenderWidgetTargeter {
  public:
+  using RenderWidgetHostAtPointCallback =
+      base::OnceCallback<void(base::WeakPtr<RenderWidgetHostViewBase>,
+                              base::Optional<gfx::PointF>)>;
+
   class Delegate {
    public:
     virtual ~Delegate() {}
 
+    virtual RenderWidgetTargetResult FindTargetSynchronouslyAtLocation(
+        RenderWidgetHostViewBase* root_view,
+        const gfx::PointF& location) = 0;
+
     virtual RenderWidgetTargetResult FindTargetSynchronously(
         RenderWidgetHostViewBase* root_view,
         const blink::WebInputEvent& event) = 0;
@@ -97,6 +105,13 @@
                              const blink::WebInputEvent& event,
                              const ui::LatencyInfo& latency);
 
+  // Finds the appropriate target inside |root_view| for |point|, and passes the
+  // target along with the transformed coordinates of the point with respect to
+  // the target's coordinates.
+  void FindTargetAndCallback(RenderWidgetHostViewBase* root_view,
+                             const gfx::PointF& point,
+                             RenderWidgetHostAtPointCallback callback);
+
   void ViewWillBeDestroyed(RenderWidgetHostViewBase* view);
 
   bool HasEventsPendingDispatch() const;
@@ -107,9 +122,65 @@
   }
 
   size_t num_requests_in_queue_for_testing() { return requests_.size(); }
-  bool is_request_in_flight_for_testing() { return request_in_flight_; }
+  bool is_request_in_flight_for_testing() {
+    return request_in_flight_.has_value();
+  }
 
  private:
+  class TargetingRequest {
+   public:
+    TargetingRequest(base::WeakPtr<RenderWidgetHostViewBase>,
+                     const blink::WebInputEvent&,
+                     const ui::LatencyInfo&);
+    TargetingRequest(base::WeakPtr<RenderWidgetHostViewBase>,
+                     const gfx::PointF&,
+                     RenderWidgetHostAtPointCallback);
+    TargetingRequest(TargetingRequest&& request);
+    TargetingRequest& operator=(TargetingRequest&& other);
+    ~TargetingRequest();
+
+    void RunCallback(RenderWidgetHostViewBase* target,
+                     base::Optional<gfx::PointF> point);
+
+    bool MergeEventIfPossible(const blink::WebInputEvent& event);
+    bool IsWebInputEventRequest() const;
+    const blink::WebInputEvent& GetEvent() const;
+    RenderWidgetHostViewBase* GetRootView() const;
+    gfx::PointF GetLocation() const;
+    const ui::LatencyInfo& GetLatency() const;
+
+    // Queued TragetingRequest
+    void StartQueueingTimeTracker();
+    void StopQueueingTimeTracker();
+
+    // Verification TargetingRequest
+    viz::FrameSinkId GetExpectedFrameSinkId() const;
+    void SetExpectedFrameSinkId(const viz::FrameSinkId& id);
+
+   private:
+    base::WeakPtr<RenderWidgetHostViewBase> root_view;
+
+    RenderWidgetHostAtPointCallback callback;
+
+    // |location| is in the coordinate space of |root_view| which is
+    // either set directly when event is null or calculated from the event.
+    gfx::PointF location;
+    // |event| if set is in the coordinate space of |root_view|.
+    ui::WebScopedInputEvent event;
+    ui::LatencyInfo latency;
+
+    // |expected_frame_sink_id| is only valid if the request is for
+    // verification.
+    viz::FrameSinkId expected_frame_sink_id;
+
+    // To track how long the request has been queued.
+    std::unique_ptr<TracingUmaTracker> tracker;
+
+    DISALLOW_COPY_AND_ASSIGN(TargetingRequest);
+  };
+
+  void ResolveTargetingRequest(TargetingRequest);
+
   // Attempts to target and dispatch all events in the queue. It stops if it has
   // to query a client, or if the queue becomes empty.
   void FlushEventQueue(bool is_verifying);
@@ -127,33 +198,17 @@
   // correctly.
   // TODO(sunxd): Remove |expected_frame_sink_id| after verifying synchronous
   // hit testing correctness. See https://crbug.com/871996.
-  void QueryClientInternal(RenderWidgetHostViewBase* root_view,
-                           RenderWidgetHostViewBase* target,
-                           const blink::WebInputEvent& event,
-                           const ui::LatencyInfo& latency,
+  void QueryClientInternal(RenderWidgetHostViewBase* target,
                            const gfx::PointF& target_location,
                            RenderWidgetHostViewBase* last_request_target,
                            const gfx::PointF& last_target_location,
-                           const viz::FrameSinkId& expected_frame_sink_id);
+                           TargetingRequest request);
 
-  void QueryClient(RenderWidgetHostViewBase* root_view,
-                   RenderWidgetHostViewBase* target,
-                   const blink::WebInputEvent& event,
-                   const ui::LatencyInfo& latency,
-                   const gfx::PointF& target_location,
-                   RenderWidgetHostViewBase* last_request_target,
-                   const gfx::PointF& last_target_location);
+  void QueryClient(TargetingRequest request);
 
-  void QueryAndVerifyClient(RenderWidgetHostViewBase* root_view,
-                            RenderWidgetHostViewBase* target,
-                            const blink::WebInputEvent& event,
-                            const ui::LatencyInfo& latency,
-                            const gfx::PointF& target_location,
-                            RenderWidgetHostViewBase* last_request_target,
-                            const gfx::PointF& last_target_location,
-                            const viz::FrameSinkId& expected_frame_sink_id);
+  void QueryAndVerifyClient(TargetingRequest request);
 
-  // |event| is in the coordinate space of |root_view|. |target_location|, if
+  // |target_location|, if
   // set, is the location in |target|'s coordinate space.
   // |target| is the current target that will be queried using its
   // InputTargetClient interface.
@@ -162,66 +217,44 @@
   // that new target's coordinate space.
   // |expected_frame_sink_id| is the expected hit test result based on
   // synchronous event targeting with cc generated data.
-  void FoundFrameSinkId(base::WeakPtr<RenderWidgetHostViewBase> root_view,
-                        base::WeakPtr<RenderWidgetHostViewBase> target,
-                        ui::WebScopedInputEvent event,
-                        const ui::LatencyInfo& latency,
+  void FoundFrameSinkId(base::WeakPtr<RenderWidgetHostViewBase> target,
                         uint32_t request_id,
                         const gfx::PointF& target_location,
                         TracingUmaTracker tracker,
-                        const viz::FrameSinkId& expected_frame_sink_id,
+                        const bool is_verification_request,
                         const viz::FrameSinkId& frame_sink_id,
                         const gfx::PointF& transformed_location);
 
-  // |event| is in the coordinate space of |root_view|. |target_location|, if
+  // |target_location|, if
   // set, is the location in |target|'s coordinate space. If |latched_target| is
   // false, we explicitly did hit-testing for this event, instead of using a
   // known target.
-  void FoundTarget(RenderWidgetHostViewBase* root_view,
-                   RenderWidgetHostViewBase* target,
-                   const blink::WebInputEvent& event,
-                   const ui::LatencyInfo& latency,
+  void FoundTarget(RenderWidgetHostViewBase* target,
                    const base::Optional<gfx::PointF>& target_location,
                    bool latched_target,
-                   const viz::FrameSinkId& expected_frame_sink_id);
+                   TargetingRequest* request);
 
   // Callback when the hit testing timer fires, to resume event processing
   // without further waiting for a response to the last targeting request.
   void AsyncHitTestTimedOut(
-      base::WeakPtr<RenderWidgetHostViewBase> current_request_root_view,
       base::WeakPtr<RenderWidgetHostViewBase> current_request_target,
       const gfx::PointF& current_target_location,
       base::WeakPtr<RenderWidgetHostViewBase> last_request_target,
       const gfx::PointF& last_target_location,
-      ui::WebScopedInputEvent event,
-      const ui::LatencyInfo& latency,
-      const viz::FrameSinkId& expected_frame_sink_id);
+      const bool is_verification_request);
 
   base::TimeDelta async_hit_test_timeout_delay() {
     return async_hit_test_timeout_delay_;
   }
 
-  struct TargetingRequest {
-    TargetingRequest();
-    TargetingRequest(TargetingRequest&& request);
-    TargetingRequest& operator=(TargetingRequest&& other);
-    ~TargetingRequest();
-
-    base::WeakPtr<RenderWidgetHostViewBase> root_view;
-    ui::WebScopedInputEvent event;
-    ui::LatencyInfo latency;
-    viz::FrameSinkId expected_frame_sink_id;
-    std::unique_ptr<TracingUmaTracker> tracker;
-  };
-
-  bool request_in_flight_ = false;
+  base::Optional<TargetingRequest> request_in_flight_;
   uint32_t last_request_id_ = 0;
   std::queue<TargetingRequest> requests_;
 
   // With viz-hit-testing-surface-layer being enabled, we do async hit testing
   // for already dispatched events for verification. These verification requests
   // should not block normal hit testing requests.
-  bool verify_request_in_flight_ = false;
+  base::Optional<TargetingRequest> verify_request_in_flight_;
   uint32_t last_verify_request_id_ = 0;
   std::queue<TargetingRequest> verify_requests_;
 
diff --git a/content/browser/service_worker/service_worker_navigation_loader_unittest.cc b/content/browser/service_worker/service_worker_navigation_loader_unittest.cc
index 4552ec99..05bd96b 100644
--- a/content/browser/service_worker/service_worker_navigation_loader_unittest.cc
+++ b/content/browser/service_worker/service_worker_navigation_loader_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "content/browser/loader/navigation_loader_interceptor.h"
+#include "content/browser/loader/single_request_url_loader_factory.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/browser/service_worker/fake_embedded_worker_instance_client.h"
 #include "content/browser/service_worker/fake_service_worker.h"
@@ -21,7 +22,6 @@
 #include "content/browser/service_worker/service_worker_test_utils.h"
 #include "content/browser/service_worker/service_worker_version.h"
 #include "content/common/service_worker/service_worker_utils.h"
-#include "content/common/single_request_url_loader_factory.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
index fd184108..0fffada 100644
--- a/content/browser/site_instance_impl.cc
+++ b/content/browser/site_instance_impl.cc
@@ -31,6 +31,17 @@
 
 namespace content {
 
+namespace {
+
+// Returns true if CreateForURL() and related functions should be allowed to
+// return a default SiteInstance.
+bool ShouldAllowDefaultSiteInstance() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kEnableDefaultSiteInstance);
+}
+
+}  // namespace
+
 int32_t SiteInstanceImpl::next_site_instance_id_ = 1;
 
 // static
@@ -90,8 +101,7 @@
   // This will create a new SiteInstance and BrowsingInstance.
   scoped_refptr<BrowsingInstance> instance(
       new BrowsingInstance(browser_context));
-  return instance->GetSiteInstanceForURL(url,
-                                         /* allow_default_instance */ false);
+  return instance->GetSiteInstanceForURL(url, ShouldAllowDefaultSiteInstance());
 }
 
 // static
@@ -593,6 +603,14 @@
   return true;
 }
 
+bool SiteInstanceImpl::DoesSiteForURLMatch(const GURL& url) {
+  // Note: The |allow_default_site_url| value used here MUST match the value
+  // used in CreateForURL().
+  return site_ == GetSiteForURLInternal(GetIsolationContext(), url,
+                                        true /* should_use_effective_urls */,
+                                        ShouldAllowDefaultSiteInstance());
+}
+
 // static
 GURL SiteInstance::GetSiteForURL(BrowserContext* browser_context,
                                  const GURL& url) {
diff --git a/content/browser/site_instance_impl.h b/content/browser/site_instance_impl.h
index a4690105..b1f7dc24 100644
--- a/content/browser/site_instance_impl.h
+++ b/content/browser/site_instance_impl.h
@@ -309,6 +309,10 @@
   // Returns true if this object was constructed as a default site instance.
   bool IsDefaultSiteInstance();
 
+  // Returns true if the the site URL for |url| matches the site URL
+  // for this instance (i.e. GetSiteURL()). Otherwise returns false.
+  bool DoesSiteForURLMatch(const GURL& url);
+
  private:
   friend class BrowsingInstance;
   friend class SiteInstanceTestBrowserClient;
diff --git a/content/browser/site_instance_impl_unittest.cc b/content/browser/site_instance_impl_unittest.cc
index 03fb996..e4869f3 100644
--- a/content/browser/site_instance_impl_unittest.cc
+++ b/content/browser/site_instance_impl_unittest.cc
@@ -261,20 +261,28 @@
 
   // Ensure that default SiteInstances are deleted when all references to them
   // are gone.
-  auto site_instance1 =
+  auto site_instance =
       SiteInstanceImpl::CreateForURL(&browser_context, GURL("http://foo.com"));
-  // TODO(958060): Remove the creation of this second instance and update
-  // the deletion count below once CreateForURL() starts returning a default
-  // SiteInstance for sites that don't require a dedicated process.
-  auto site_instance2 =
-      site_instance1->GetRelatedSiteInstance(GURL("http://bar.com"));
-  EXPECT_FALSE(site_instance1->IsDefaultSiteInstance());
-  EXPECT_TRUE(static_cast<SiteInstanceImpl*>(site_instance2.get())
-                  ->IsDefaultSiteInstance());
-  site_instance1.reset();
-  site_instance2.reset();
+  if (AreDefaultSiteInstancesEnabled()) {
+    EXPECT_TRUE(site_instance->IsDefaultSiteInstance());
+  } else {
+    // TODO(958060): Remove the creation of this second instance once
+    // CreateForURL() starts returning a default SiteInstance without
+    // the need to specify a command-line flag.
+    EXPECT_FALSE(site_instance->IsDefaultSiteInstance());
+    auto related_instance =
+        site_instance->GetRelatedSiteInstance(GURL("http://bar.com"));
+    EXPECT_TRUE(static_cast<SiteInstanceImpl*>(related_instance.get())
+                    ->IsDefaultSiteInstance());
 
-  EXPECT_EQ(2, browser_client()->GetAndClearSiteInstanceDeleteCount());
+    related_instance.reset();
+
+    EXPECT_EQ(1, browser_client()->GetAndClearSiteInstanceDeleteCount());
+    EXPECT_EQ(0, browser_client()->GetAndClearBrowsingInstanceDeleteCount());
+  }
+  site_instance.reset();
+
+  EXPECT_EQ(1, browser_client()->GetAndClearSiteInstanceDeleteCount());
   EXPECT_EQ(1, browser_client()->GetAndClearBrowsingInstanceDeleteCount());
 }
 
@@ -466,7 +474,8 @@
   GURL test_url("https://some.app.foo.com/");
   GURL nonapp_site_url("https://foo.com/");
   GURL app_url("https://app.com/");
-  EffectiveURLContentBrowserClient modified_client(test_url, app_url);
+  EffectiveURLContentBrowserClient modified_client(
+      test_url, app_url, /* requires_dedicated_process */ true);
   ContentBrowserClient* regular_client =
       SetBrowserClientForTesting(&modified_client);
   std::unique_ptr<TestBrowserContext> browser_context(new TestBrowserContext());
@@ -506,12 +515,8 @@
         bar_site_instance->GetRelatedSiteInstance(test_url);
     auto* site_instance_impl =
         static_cast<SiteInstanceImpl*>(site_instance.get());
-    if (AreAllSitesIsolatedForTesting()) {
-      EXPECT_EQ(expected_app_site_url, site_instance->GetSiteURL());
-      EXPECT_EQ(nonapp_site_url, site_instance_impl->lock_url());
-    } else {
-      EXPECT_TRUE(site_instance_impl->IsDefaultSiteInstance());
-    }
+    EXPECT_EQ(expected_app_site_url, site_instance->GetSiteURL());
+    EXPECT_EQ(nonapp_site_url, site_instance_impl->lock_url());
   }
 
   // New SiteInstance with a lazily assigned site URL.
@@ -992,8 +997,9 @@
 
   const GURL kOriginalUrl("https://original.com");
   const GURL kTranslatedUrl(GetWebUIURL("translated"));
-  EffectiveURLContentBrowserClient modified_client(kOriginalUrl,
-                                                   kTranslatedUrl);
+  EffectiveURLContentBrowserClient modified_client(
+      kOriginalUrl, kTranslatedUrl,
+      /* requires_dedicated_process */ true);
   ContentBrowserClient* regular_client =
       SetBrowserClientForTesting(&modified_client);
 
@@ -1302,7 +1308,8 @@
 TEST_F(SiteInstanceTest, OriginalURL) {
   GURL original_url("https://foo.com/");
   GURL app_url("https://app.com/");
-  EffectiveURLContentBrowserClient modified_client(original_url, app_url);
+  EffectiveURLContentBrowserClient modified_client(
+      original_url, app_url, /* requires_dedicated_process */ true);
   ContentBrowserClient* regular_client =
       SetBrowserClientForTesting(&modified_client);
   std::unique_ptr<TestBrowserContext> browser_context(new TestBrowserContext());
@@ -1329,12 +1336,8 @@
         bar_site_instance->GetRelatedSiteInstance(original_url);
     auto* site_instance_impl =
         static_cast<SiteInstanceImpl*>(site_instance.get());
-    if (AreAllSitesIsolatedForTesting()) {
-      EXPECT_EQ(expected_site_url, site_instance->GetSiteURL());
-      EXPECT_EQ(original_url, site_instance_impl->original_url());
-    } else {
-      EXPECT_TRUE(site_instance_impl->IsDefaultSiteInstance());
-    }
+    EXPECT_EQ(expected_site_url, site_instance->GetSiteURL());
+    EXPECT_EQ(original_url, site_instance_impl->original_url());
   }
 
   // New SiteInstance with a lazily assigned site URL.
diff --git a/content/browser/speech/speech_recognition_manager_impl.cc b/content/browser/speech/speech_recognition_manager_impl.cc
index 62d49f62..7ff8290f 100644
--- a/content/browser/speech/speech_recognition_manager_impl.cc
+++ b/content/browser/speech/speech_recognition_manager_impl.cc
@@ -731,7 +731,8 @@
   } else {
     // From the ask_user=true path, use the selected device.
     DCHECK_EQ(1u, devices.size());
-    DCHECK_EQ(blink::MEDIA_DEVICE_AUDIO_CAPTURE, devices.front().type);
+    DCHECK_EQ(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+              devices.front().type);
     device_id = devices.front().id;
   }
 
diff --git a/content/browser/tracing/background_tracing_active_scenario.cc b/content/browser/tracing/background_tracing_active_scenario.cc
index ac4b043..a644aef 100644
--- a/content/browser/tracing/background_tracing_active_scenario.cc
+++ b/content/browser/tracing/background_tracing_active_scenario.cc
@@ -28,10 +28,6 @@
 using base::trace_event::TraceConfig;
 using Metrics = content::BackgroundTracingManagerImpl::Metrics;
 
-namespace {
-const size_t kDefaultTraceBufferSizeInKb = 10 * 1024;
-}  // namespace
-
 namespace content {
 
 class BackgroundTracingActiveScenario::TracingTimer {
@@ -75,10 +71,8 @@
       public mojo::DataPipeDrainer::Client {
  public:
   PerfettoTracingSession(BackgroundTracingActiveScenario* parent_scenario,
-                         const TraceConfig& chrome_config,
-                         BackgroundTracingConfigImpl::CategoryPreset preset)
+                         const TraceConfig& chrome_config)
       : parent_scenario_(parent_scenario),
-        category_preset_(preset),
         raw_data_(std::make_unique<std::string>()) {
 #if !defined(OS_ANDROID)
     // TODO(crbug.com/941318): Re-enable startup tracing for Android once all
@@ -143,7 +137,7 @@
   // tracing::mojom::TracingSession implementation:
   void OnTracingEnabled() override {
     BackgroundTracingManagerImpl::GetInstance()->OnStartTracingDone(
-        category_preset_);
+        parent_scenario_->GetConfig()->category_preset());
   }
 
   void OnTracingDisabled() override {
@@ -182,7 +176,6 @@
   tracing::mojom::TracingSessionHostPtr tracing_session_host_;
   std::unique_ptr<mojo::DataPipeDrainer> drainer_;
   tracing::mojom::ConsumerHostPtr consumer_host_;
-  BackgroundTracingConfigImpl::CategoryPreset category_preset_;
   std::unique_ptr<std::string> raw_data_;
   bool has_finished_read_buffers_ = false;
   bool has_finished_receiving_data_ = false;
@@ -192,8 +185,7 @@
     : public BackgroundTracingActiveScenario::TracingSession {
  public:
   LegacyTracingSession(BackgroundTracingActiveScenario* parent_scenario,
-                       const TraceConfig& chrome_config,
-                       BackgroundTracingConfigImpl::CategoryPreset preset)
+                       const TraceConfig& chrome_config)
       : parent_scenario_(parent_scenario) {
 #if !defined(OS_ANDROID)
     // TODO(crbug.com/941318): Re-enable startup tracing for Android once all
@@ -210,7 +202,7 @@
         base::BindOnce(
             &BackgroundTracingManagerImpl::OnStartTracingDone,
             base::Unretained(BackgroundTracingManagerImpl::GetInstance()),
-            preset));
+            parent_scenario->GetConfig()->category_preset()));
     // We check IsEnabled() before creating the LegacyTracingSession,
     // so any failures to start tracing at this point would be due to invalid
     // configs which we treat as a failure scenario.
@@ -309,18 +301,8 @@
     // which means that we're left in a state where the Mojo interface doesn't
     // think we're tracing but TraceLog is still enabled. If that's the case,
     // we abort tracing here.
-    auto record_mode =
-        (config_->tracing_mode() == BackgroundTracingConfigImpl::PREEMPTIVE)
-            ? base::trace_event::RECORD_CONTINUOUSLY
-            : base::trace_event::RECORD_UNTIL_FULL;
-    TraceConfig config =
-        BackgroundTracingConfigImpl::GetConfigForCategoryPreset(
-            config_->category_preset(), record_mode);
-
-    uint8_t modes = base::trace_event::TraceLog::RECORDING_MODE;
-    if (!config.event_filters().empty())
-      modes |= base::trace_event::TraceLog::FILTERING_MODE;
-    base::trace_event::TraceLog::GetInstance()->SetDisabled(modes);
+    base::trace_event::TraceLog::GetInstance()->SetDisabled(
+        base::trace_event::TraceLog::GetInstance()->enabled_modes());
   }
 
   if (scenario_state_ == State::kAborted) {
@@ -347,32 +329,15 @@
 void BackgroundTracingActiveScenario::StartTracingIfConfigNeedsIt() {
   DCHECK(config_);
   if (config_->tracing_mode() == BackgroundTracingConfigImpl::PREEMPTIVE) {
-    StartTracing(config_->category_preset(),
-                 base::trace_event::RECORD_CONTINUOUSLY);
+    StartTracing();
   }
 
   // There is nothing to do in case of reactive tracing.
 }
 
-bool BackgroundTracingActiveScenario::StartTracing(
-    BackgroundTracingConfigImpl::CategoryPreset preset,
-    base::trace_event::TraceRecordMode record_mode) {
+bool BackgroundTracingActiveScenario::StartTracing() {
   TraceConfig chrome_config =
-      BackgroundTracingConfigImpl::GetConfigForCategoryPreset(preset,
-                                                              record_mode);
-  if (requires_anonymized_data_) {
-    chrome_config.EnableArgumentFilter();
-  }
-
-  chrome_config.SetTraceBufferSizeInKb(kDefaultTraceBufferSizeInKb);
-
-#if defined(OS_ANDROID)
-  // Set low trace buffer size on Android in order to upload small trace files.
-  if (config_->tracing_mode() == BackgroundTracingConfigImpl::PREEMPTIVE) {
-    chrome_config.SetTraceBufferSizeInEvents(20000);
-    chrome_config.SetTraceBufferSizeInKb(500);
-  }
-#endif
+      config_->GetTraceConfig(requires_anonymized_data_);
 
   // If the tracing controller is tracing, i.e. DevTools or about://tracing,
   // we don't start background tracing to not interfere with the user activity.
@@ -393,10 +358,10 @@
   DCHECK(!tracing_session_);
   if (base::FeatureList::IsEnabled(features::kBackgroundTracingProtoOutput)) {
     tracing_session_ =
-        std::make_unique<PerfettoTracingSession>(this, chrome_config, preset);
+        std::make_unique<PerfettoTracingSession>(this, chrome_config);
   } else {
     tracing_session_ =
-        std::make_unique<LegacyTracingSession>(this, chrome_config, preset);
+        std::make_unique<LegacyTracingSession>(this, chrome_config);
   }
 
   SetState(State::kTracing);
@@ -574,8 +539,7 @@
 
     if (state() != State::kTracing) {
       // It was not already tracing, start a new trace.
-      if (!StartTracing(triggered_rule->category_preset(),
-                        base::trace_event::RECORD_UNTIL_FULL)) {
+      if (!StartTracing()) {
         return;
       }
     } else {
diff --git a/content/browser/tracing/background_tracing_active_scenario.h b/content/browser/tracing/background_tracing_active_scenario.h
index 5b9a6db..89f6d41c 100644
--- a/content/browser/tracing/background_tracing_active_scenario.h
+++ b/content/browser/tracing/background_tracing_active_scenario.h
@@ -68,8 +68,7 @@
       const base::RepeatingClosure& callback);
 
  private:
-  bool StartTracing(BackgroundTracingConfigImpl::CategoryPreset,
-                    base::trace_event::TraceRecordMode);
+  bool StartTracing();
   void BeginFinalizing(
       BackgroundTracingManager::StartedFinalizingCallback callback);
 
diff --git a/content/browser/tracing/background_tracing_agent_client_impl.cc b/content/browser/tracing/background_tracing_agent_client_impl.cc
new file mode 100644
index 0000000..422043a
--- /dev/null
+++ b/content/browser/tracing/background_tracing_agent_client_impl.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/tracing/background_tracing_agent_client_impl.h"
+
+#include <stdint.h>
+
+#include "base/memory/ptr_util.h"
+#include "content/browser/tracing/background_tracing_manager_impl.h"
+#include "content/common/child_process_host_impl.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+
+namespace content {
+
+// static
+void BackgroundTracingAgentClientImpl::Create(
+    int child_process_id,
+    mojo::PendingRemote<tracing::mojom::BackgroundTracingAgent> pending_agent) {
+  uint64_t tracing_process_id =
+      ChildProcessHostImpl::ChildProcessUniqueIdToTracingProcessId(
+          child_process_id);
+
+  mojo::PendingRemote<tracing::mojom::BackgroundTracingAgentClient> client;
+  auto receiver = client.InitWithNewPipeAndPassReceiver();
+
+  mojo::Remote<tracing::mojom::BackgroundTracingAgent> agent(
+      std::move(pending_agent));
+  agent->Initialize(tracing_process_id, std::move(client));
+
+  // Lifetime bound to the agent, which means it is bound to the lifetime of
+  // the child process. Will be cleaned up when the process exits.
+  mojo::MakeSelfOwnedReceiver(
+      base::WrapUnique(new BackgroundTracingAgentClientImpl(std::move(agent))),
+      std::move(receiver));
+}
+
+BackgroundTracingAgentClientImpl::~BackgroundTracingAgentClientImpl() {
+  BackgroundTracingManagerImpl::GetInstance()->RemoveAgent(agent_.get());
+}
+
+void BackgroundTracingAgentClientImpl::OnInitialized() {
+  BackgroundTracingManagerImpl::GetInstance()->AddAgent(agent_.get());
+}
+
+void BackgroundTracingAgentClientImpl::OnTriggerBackgroundTrace(
+    const std::string& name) {
+  BackgroundTracingManagerImpl::GetInstance()->OnHistogramTrigger(name);
+}
+
+void BackgroundTracingAgentClientImpl::OnAbortBackgroundTrace() {
+  BackgroundTracingManagerImpl::GetInstance()->AbortScenario();
+}
+
+BackgroundTracingAgentClientImpl::BackgroundTracingAgentClientImpl(
+    mojo::Remote<tracing::mojom::BackgroundTracingAgent> agent)
+    : agent_(std::move(agent)) {
+  DCHECK(agent_);
+}
+
+}  // namespace content
diff --git a/content/browser/tracing/background_tracing_agent_client_impl.h b/content/browser/tracing/background_tracing_agent_client_impl.h
new file mode 100644
index 0000000..d7ce9ec
--- /dev/null
+++ b/content/browser/tracing/background_tracing_agent_client_impl.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_TRACING_BACKGROUND_TRACING_AGENT_CLIENT_IMPL_H_
+#define CONTENT_BROWSER_TRACING_BACKGROUND_TRACING_AGENT_CLIENT_IMPL_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/trace_event/trace_event.h"
+#include "components/tracing/common/background_tracing_agent.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace content {
+
+class BackgroundTracingAgentClientImpl
+    : public tracing::mojom::BackgroundTracingAgentClient {
+ public:
+  static void Create(int child_process_id,
+                     mojo::PendingRemote<tracing::mojom::BackgroundTracingAgent>
+                         pending_agent);
+
+  ~BackgroundTracingAgentClientImpl() override;
+
+  // tracing::mojom::BackgroundTracingAgentClient methods:
+  void OnInitialized() override;
+  void OnTriggerBackgroundTrace(const std::string& histogram_name) override;
+  void OnAbortBackgroundTrace() override;
+
+ private:
+  explicit BackgroundTracingAgentClientImpl(
+      mojo::Remote<tracing::mojom::BackgroundTracingAgent> agent);
+
+  mojo::Remote<tracing::mojom::BackgroundTracingAgent> agent_;
+
+  DISALLOW_COPY_AND_ASSIGN(BackgroundTracingAgentClientImpl);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_TRACING_BACKGROUND_TRACING_AGENT_CLIENT_IMPL_H_
diff --git a/content/browser/tracing/background_tracing_config_impl.cc b/content/browser/tracing/background_tracing_config_impl.cc
index d0a4efdb..4e48aea 100644
--- a/content/browser/tracing/background_tracing_config_impl.cc
+++ b/content/browser/tracing/background_tracing_config_impl.cc
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "components/tracing/common/trace_startup_config.h"
 #include "content/browser/tracing/background_tracing_rule.h"
 
@@ -27,6 +28,7 @@
 const char kConfigScenarioName[] = "scenario_name";
 
 const char kConfigCategoryKey[] = "category";
+const char kConfigCustomCategoriesKey[] = "custom_categories";
 const char kConfigCategoryBenchmark[] = "BENCHMARK";
 const char kConfigCategoryBenchmarkDeep[] = "BENCHMARK_DEEP";
 const char kConfigCategoryBenchmarkGPU[] = "BENCHMARK_GPU";
@@ -42,6 +44,9 @@
 const char kConfigCategoryBenchmarkServiceworker[] = "BENCHMARK_SERVICEWORKER";
 const char kConfigCategoryBenchmarkPower[] = "BENCHMARK_POWER";
 const char kConfigCategoryBlinkStyle[] = "BLINK_STYLE";
+const char kConfigCategoryCustom[] = "CUSTOM";
+
+const size_t kDefaultTraceBufferSizeInKb = 10 * 1024;
 
 }  // namespace
 
@@ -84,6 +89,8 @@
       return kConfigCategoryBenchmarkPower;
     case BackgroundTracingConfigImpl::BLINK_STYLE:
       return kConfigCategoryBlinkStyle;
+    case BackgroundTracingConfigImpl::CUSTOM_CATEGORY_PRESET:
+      return kConfigCategoryCustom;
     case BackgroundTracingConfigImpl::CATEGORY_PRESET_UNSET:
       NOTREACHED();
   }
@@ -169,6 +176,10 @@
 }
 
 void BackgroundTracingConfigImpl::IntoDict(base::DictionaryValue* dict) {
+  if (category_preset_ == CUSTOM_CATEGORY_PRESET) {
+    dict->SetString(kConfigCustomCategoriesKey, custom_categories_);
+  }
+
   switch (tracing_mode()) {
     case BackgroundTracingConfigImpl::PREEMPTIVE:
       dict->SetString(kConfigModeKey, kConfigModePreemptive);
@@ -214,6 +225,35 @@
   }
 }
 
+base::trace_event::TraceConfig BackgroundTracingConfigImpl::GetTraceConfig(
+    bool requires_anonymized_data) {
+  base::trace_event::TraceRecordMode record_mode =
+      (tracing_mode() == BackgroundTracingConfigImpl::REACTIVE)
+          ? base::trace_event::RECORD_UNTIL_FULL
+          : base::trace_event::RECORD_CONTINUOUSLY;
+
+  base::trace_event::TraceConfig chrome_config =
+      (category_preset() == CUSTOM_CATEGORY_PRESET
+           ? base::trace_event::TraceConfig(custom_categories_, record_mode)
+           : GetConfigForCategoryPreset(category_preset(), record_mode));
+
+  if (requires_anonymized_data) {
+    chrome_config.EnableArgumentFilter();
+  }
+
+  chrome_config.SetTraceBufferSizeInKb(kDefaultTraceBufferSizeInKb);
+
+#if defined(OS_ANDROID)
+  // Set low trace buffer size on Android in order to upload small trace files.
+  if (tracing_mode() == BackgroundTracingConfigImpl::PREEMPTIVE) {
+    chrome_config.SetTraceBufferSizeInEvents(20000);
+    chrome_config.SetTraceBufferSizeInKb(500);
+  }
+#endif
+
+  return chrome_config;
+}
+
 // static
 std::unique_ptr<BackgroundTracingConfigImpl>
 BackgroundTracingConfigImpl::FromDict(const base::DictionaryValue* dict) {
@@ -249,13 +289,19 @@
   std::unique_ptr<BackgroundTracingConfigImpl> config(
       new BackgroundTracingConfigImpl(BackgroundTracingConfigImpl::PREEMPTIVE));
 
-  std::string category_preset_string;
-  if (!dict->GetString(kConfigCategoryKey, &category_preset_string))
-    return nullptr;
+  if (dict->GetString(kConfigCustomCategoriesKey,
+                      &config->custom_categories_)) {
+    config->category_preset_ = CUSTOM_CATEGORY_PRESET;
+  } else {
+    std::string category_preset_string;
+    if (!dict->GetString(kConfigCategoryKey, &category_preset_string))
+      return nullptr;
 
-  if (!StringToCategoryPreset(category_preset_string,
-                              &config->category_preset_))
-    return nullptr;
+    if (!StringToCategoryPreset(category_preset_string,
+                                &config->category_preset_)) {
+      return nullptr;
+    }
+  }
 
   const base::ListValue* configs_list = nullptr;
   if (!dict->GetList(kConfigsKey, &configs_list))
@@ -284,6 +330,20 @@
   std::unique_ptr<BackgroundTracingConfigImpl> config(
       new BackgroundTracingConfigImpl(BackgroundTracingConfigImpl::REACTIVE));
 
+  std::string category_preset_string;
+  bool has_global_categories = false;
+  if (dict->GetString(kConfigCustomCategoriesKey,
+                      &config->custom_categories_)) {
+    config->category_preset_ = CUSTOM_CATEGORY_PRESET;
+    has_global_categories = true;
+  } else if (dict->GetString(kConfigCategoryKey, &category_preset_string)) {
+    if (!StringToCategoryPreset(category_preset_string,
+                                &config->category_preset_)) {
+      return nullptr;
+    }
+    has_global_categories = true;
+  }
+
   const base::ListValue* configs_list = nullptr;
   if (!dict->GetList(kConfigsKey, &configs_list))
     return nullptr;
@@ -293,15 +353,17 @@
     if (!it.GetAsDictionary(&config_dict))
       return nullptr;
 
-    std::string category_preset_string;
-    if (!config_dict->GetString(kConfigCategoryKey, &category_preset_string))
-      return nullptr;
+    // TODO(oysteine): Remove the per-rule category preset when configs have
+    // been updated to just specify the per-config category preset.
+    if (!has_global_categories &&
+        config_dict->GetString(kConfigCategoryKey, &category_preset_string)) {
+      if (!StringToCategoryPreset(category_preset_string,
+                                  &config->category_preset_)) {
+        return nullptr;
+      }
+    }
 
-    BackgroundTracingConfigImpl::CategoryPreset new_category_preset;
-    if (!StringToCategoryPreset(category_preset_string, &new_category_preset))
-      return nullptr;
-
-    config->AddReactiveRule(config_dict, new_category_preset);
+    config->AddReactiveRule(config_dict, config->category_preset_);
   }
 
   if (config->rules().empty())
@@ -391,6 +453,7 @@
       return config;
     }
     case BackgroundTracingConfigImpl::CategoryPreset::CATEGORY_PRESET_UNSET:
+    case BackgroundTracingConfigImpl::CategoryPreset::CUSTOM_CATEGORY_PRESET:
       NOTREACHED();
   }
   NOTREACHED();
diff --git a/content/browser/tracing/background_tracing_config_impl.h b/content/browser/tracing/background_tracing_config_impl.h
index 2b35272..8c6281da 100644
--- a/content/browser/tracing/background_tracing_config_impl.h
+++ b/content/browser/tracing/background_tracing_config_impl.h
@@ -30,6 +30,7 @@
 
   enum CategoryPreset {
     CATEGORY_PRESET_UNSET,
+    CUSTOM_CATEGORY_PRESET,
     BENCHMARK,
     BENCHMARK_DEEP,
     BENCHMARK_GPU,
@@ -61,6 +62,8 @@
       const base::DictionaryValue* dict,
       BackgroundTracingConfigImpl::CategoryPreset category_preset);
 
+  base::trace_event::TraceConfig GetTraceConfig(bool requires_anonymized_data);
+
   static std::unique_ptr<BackgroundTracingConfigImpl> PreemptiveFromDict(
       const base::DictionaryValue* dict);
   static std::unique_ptr<BackgroundTracingConfigImpl> ReactiveFromDict(
@@ -75,17 +78,18 @@
       const std::string& category_preset_string,
       BackgroundTracingConfigImpl::CategoryPreset* category_preset);
 
-  static base::trace_event::TraceConfig GetConfigForCategoryPreset(
-      BackgroundTracingConfigImpl::CategoryPreset,
-      base::trace_event::TraceRecordMode);
-
  private:
   FRIEND_TEST_ALL_PREFIXES(BackgroundTracingConfigTest,
                            ValidPreemptiveConfigToString);
 
+  static base::trace_event::TraceConfig GetConfigForCategoryPreset(
+      BackgroundTracingConfigImpl::CategoryPreset,
+      base::trace_event::TraceRecordMode);
+
   CategoryPreset category_preset_;
   std::vector<std::unique_ptr<BackgroundTracingRule>> rules_;
   std::string scenario_name_;
+  std::string custom_categories_;
 
   DISALLOW_COPY_AND_ASSIGN(BackgroundTracingConfigImpl);
 };
diff --git a/content/browser/tracing/background_tracing_config_unittest.cc b/content/browser/tracing/background_tracing_config_unittest.cc
index f568dbd..8a76cd3 100644
--- a/content/browser/tracing/background_tracing_config_unittest.cc
+++ b/content/browser/tracing/background_tracing_config_unittest.cc
@@ -256,6 +256,21 @@
   EXPECT_EQ(RuleToString(config->rules()[1]),
             "{\"rule\":\"MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED\","
             "\"trigger_name\":\"foo2\"}");
+
+  config = ReadFromJSONString(
+      "{\"mode\":\"PREEMPTIVE_TRACING_MODE\", \"custom_categories\": "
+      "\"toplevel,benchmark\",\"configs\": [{\"rule\": "
+      "\"MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED\", \"trigger_name\":\"foo1\"}]}");
+  EXPECT_TRUE(config);
+  EXPECT_EQ(config->tracing_mode(), BackgroundTracingConfig::PREEMPTIVE);
+  EXPECT_EQ(config->category_preset(),
+            BackgroundTracingConfigImpl::CUSTOM_CATEGORY_PRESET);
+  EXPECT_EQ(config->rules().size(), 1u);
+  EXPECT_EQ(
+      ConfigToString(config.get()),
+      "{\"category\":\"CUSTOM\",\"configs\":[{\"rule\":\"MONITOR_AND_DUMP_WHEN_"
+      "TRIGGER_NAMED\",\"trigger_name\":\"foo1\"}],\"custom_categories\":"
+      "\"toplevel,benchmark\",\"mode\":\"PREEMPTIVE_TRACING_MODE\"}");
 }
 
 TEST_F(BackgroundTracingConfigTest, ValidPreemptiveCategoryToString) {
@@ -375,6 +390,7 @@
             "{\"category\":\"BENCHMARK_DEEP\","
             "\"rule\":\"TRACE_ON_NAVIGATION_UNTIL_TRIGGER_OR_FULL\","
             "\"trigger_delay\":30,\"trigger_name\":\"foo2\"}");
+
   config = ReadFromJSONString(
       "{\"mode\":\"REACTIVE_TRACING_MODE\",\"configs\": [{\"rule\": "
       "\"TRACE_AT_RANDOM_INTERVALS\","
@@ -388,6 +404,22 @@
             "{\"category\":\"BENCHMARK_DEEP\",\"rule\":\"TRACE_AT_RANDOM_"
             "INTERVALS\",\"stop_tracing_on_repeated_reactive\":true,"
             "\"timeout_max\":20,\"timeout_min\":10}");
+
+  config = ReadFromJSONString(
+      "{\"mode\":\"REACTIVE_TRACING_MODE\","
+      "\"custom_categories\": \"benchmark,toplevel\","
+      "\"configs\": [{\"rule\": "
+      "\"TRACE_AT_RANDOM_INTERVALS\","
+      "\"stop_tracing_on_repeated_reactive\": true, "
+      "\"timeout_max\":20,\"timeout_min\":10}]}");
+  EXPECT_TRUE(config);
+  EXPECT_EQ(config->tracing_mode(), BackgroundTracingConfig::REACTIVE);
+  EXPECT_EQ(config->rules().size(), 1u);
+  EXPECT_EQ(ConfigToString(config.get()),
+            "{\"configs\":[{\"category\":\"CUSTOM\",\"rule\":\"TRACE_AT_RANDOM_"
+            "INTERVALS\",\"stop_tracing_on_repeated_reactive\":true,\"timeout_"
+            "max\":20,\"timeout_min\":10}],\"custom_categories\":\"benchmark,"
+            "toplevel\",\"mode\":\"REACTIVE_TRACING_MODE\"}");
 }
 
 TEST_F(BackgroundTracingConfigTest, ValidPreemptiveConfigToString) {
@@ -502,6 +534,28 @@
               "SPECIFIC_HISTOGRAM_AND_VALUE\",\"trigger_delay\":10}],\"mode\":"
               "\"PREEMPTIVE_TRACING_MODE\"}");
   }
+
+  {
+    config.reset(
+        new BackgroundTracingConfigImpl(BackgroundTracingConfig::PREEMPTIVE));
+
+    std::unique_ptr<base::DictionaryValue> second_dict(
+        new base::DictionaryValue());
+    second_dict->SetString(
+        "rule", "MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE");
+    second_dict->SetString("histogram_name", "foo");
+    second_dict->SetInteger("histogram_lower_value", 1);
+    second_dict->SetInteger("histogram_upper_value", 2);
+    second_dict->SetInteger("trigger_delay", 10);
+    config->AddPreemptiveRule(second_dict.get());
+
+    EXPECT_EQ(ConfigToString(config.get()),
+              "{\"category\":\"BENCHMARK\",\"configs\":[{\"histogram_lower_"
+              "value\":1,\"histogram_name\":\"foo\",\"histogram_repeat\":true,"
+              "\"histogram_upper_value\":2,\"rule\":\"MONITOR_AND_DUMP_WHEN_"
+              "SPECIFIC_HISTOGRAM_AND_VALUE\",\"trigger_delay\":10}],\"mode\":"
+              "\"PREEMPTIVE_TRACING_MODE\"}");
+  }
 }
 
 TEST_F(BackgroundTracingConfigTest, InvalidPreemptiveConfigToString) {
diff --git a/content/browser/tracing/background_tracing_manager_impl.cc b/content/browser/tracing/background_tracing_manager_impl.cc
index 0acb99c..0af1721 100644
--- a/content/browser/tracing/background_tracing_manager_impl.cc
+++ b/content/browser/tracing/background_tracing_manager_impl.cc
@@ -22,13 +22,18 @@
 #include "content/browser/tracing/background_memory_tracing_observer.h"
 #include "content/browser/tracing/background_startup_tracing_observer.h"
 #include "content/browser/tracing/background_tracing_active_scenario.h"
+#include "content/browser/tracing/background_tracing_agent_client_impl.h"
 #include "content/browser/tracing/background_tracing_rule.h"
-#include "content/browser/tracing/trace_message_filter.h"
 #include "content/browser/tracing/tracing_controller_impl.h"
+#include "content/public/browser/browser_child_process_host.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/child_process_data.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/render_process_host.h"
 #include "content/public/browser/tracing_delegate.h"
+#include "content/public/common/bind_interface_helpers.h"
+#include "content/public/common/child_process_host.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_switches.h"
 #include "services/tracing/public/cpp/perfetto/trace_event_data_source.h"
@@ -56,6 +61,35 @@
   return manager.get();
 }
 
+// static
+void BackgroundTracingManagerImpl::ActivateForProcess(
+    BrowserChildProcessHost* host) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  tracing::mojom::BackgroundTracingAgentPtr agent;
+  content::BindInterface(host->GetHost(), &agent);
+
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::UI},
+      base::BindOnce(
+          [](int child_process_id,
+             tracing::mojom::BackgroundTracingAgentPtrInfo info) {
+            BackgroundTracingAgentClientImpl::Create(child_process_id,
+                                                     std::move(info));
+          },
+          host->GetData().id, agent.PassInterface()));
+}
+
+// static
+void BackgroundTracingManagerImpl::ActivateForProcess(RenderProcessHost* host) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  tracing::mojom::BackgroundTracingAgentPtr agent;
+  content::BindInterface(host, &agent);
+  BackgroundTracingAgentClientImpl::Create(host->GetID(),
+                                           agent.PassInterface());
+}
+
 BackgroundTracingManagerImpl::BackgroundTracingManagerImpl()
     : delegate_(GetContentClient()->browser()->GetTracingDelegate()),
       trigger_handle_ids_(0) {
@@ -190,43 +224,42 @@
   background_tracing_observers_.erase(observer);
 }
 
-void BackgroundTracingManagerImpl::AddTraceMessageFilter(
-    TraceMessageFilter* trace_message_filter) {
+void BackgroundTracingManagerImpl::AddAgent(
+    tracing::mojom::BackgroundTracingAgent* agent) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  trace_message_filters_.insert(trace_message_filter);
+  agents_.insert(agent);
 
-  for (auto* observer : trace_message_filter_observers_) {
-    observer->OnTraceMessageFilterAdded(trace_message_filter);
+  for (auto* observer : agent_observers_) {
+    observer->OnAgentAdded(agent);
   }
 }
 
-void BackgroundTracingManagerImpl::RemoveTraceMessageFilter(
-    TraceMessageFilter* trace_message_filter) {
+void BackgroundTracingManagerImpl::RemoveAgent(
+    tracing::mojom::BackgroundTracingAgent* agent) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  for (auto* observer : trace_message_filter_observers_) {
-    observer->OnTraceMessageFilterRemoved(trace_message_filter);
+  for (auto* observer : agent_observers_) {
+    observer->OnAgentRemoved(agent);
   }
 
-  trace_message_filters_.erase(trace_message_filter);
+  agents_.erase(agent);
 }
 
-void BackgroundTracingManagerImpl::AddTraceMessageFilterObserver(
-    TraceMessageFilterObserver* observer) {
+void BackgroundTracingManagerImpl::AddAgentObserver(AgentObserver* observer) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  trace_message_filter_observers_.insert(observer);
+  agent_observers_.insert(observer);
 
-  for (auto& filter : trace_message_filters_) {
-    observer->OnTraceMessageFilterAdded(filter.get());
+  for (auto* agent : agents_) {
+    observer->OnAgentAdded(agent);
   }
 }
 
-void BackgroundTracingManagerImpl::RemoveTraceMessageFilterObserver(
-    TraceMessageFilterObserver* observer) {
+void BackgroundTracingManagerImpl::RemoveAgentObserver(
+    AgentObserver* observer) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  trace_message_filter_observers_.erase(observer);
+  agent_observers_.erase(observer);
 
-  for (auto& filter : trace_message_filters_) {
-    observer->OnTraceMessageFilterRemoved(filter.get());
+  for (auto* agent : agents_) {
+    observer->OnAgentRemoved(agent);
   }
 }
 
@@ -271,14 +304,7 @@
 
 void BackgroundTracingManagerImpl::OnHistogramTrigger(
     const std::string& histogram_name) {
-  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
-    base::PostTaskWithTraits(
-        FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(&BackgroundTracingManagerImpl::OnHistogramTrigger,
-                       base::Unretained(this), histogram_name));
-    return;
-  }
-
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (active_scenario_) {
     active_scenario_->OnHistogramTrigger(histogram_name);
   }
diff --git a/content/browser/tracing/background_tracing_manager_impl.h b/content/browser/tracing/background_tracing_manager_impl.h
index b021c54..a092f68 100644
--- a/content/browser/tracing/background_tracing_manager_impl.h
+++ b/content/browser/tracing/background_tracing_manager_impl.h
@@ -19,11 +19,18 @@
 class NoDestructor;
 }  // namespace base
 
+namespace tracing {
+namespace mojom {
+class BackgroundTracingAgent;
+}  // namespace mojom
+}  // namespace tracing
+
 namespace content {
 
 class BackgroundTracingRule;
 class BackgroundTracingActiveScenario;
-class TraceMessageFilter;
+class BrowserChildProcessHost;
+class RenderProcessHost;
 class TracingDelegate;
 
 class BackgroundTracingManagerImpl : public BackgroundTracingManager {
@@ -48,10 +55,12 @@
     virtual ~EnabledStateObserver() = default;
   };
 
-  class TraceMessageFilterObserver {
+  class AgentObserver {
    public:
-    virtual void OnTraceMessageFilterAdded(TraceMessageFilter* filter) = 0;
-    virtual void OnTraceMessageFilterRemoved(TraceMessageFilter* filter) = 0;
+    virtual void OnAgentAdded(
+        tracing::mojom::BackgroundTracingAgent* agent) = 0;
+    virtual void OnAgentRemoved(
+        tracing::mojom::BackgroundTracingAgent* agent) = 0;
   };
 
   // These values are used for a histogram. Do not reorder.
@@ -75,6 +84,9 @@
 
   CONTENT_EXPORT static BackgroundTracingManagerImpl* GetInstance();
 
+  static void ActivateForProcess(BrowserChildProcessHost* host);
+  static void ActivateForProcess(RenderProcessHost* host);
+
   bool SetActiveScenario(std::unique_ptr<BackgroundTracingConfig>,
                          ReceiveCallback,
                          DataFiltering data_filtering) override;
@@ -99,11 +111,11 @@
   CONTENT_EXPORT void RemoveEnabledStateObserver(
       EnabledStateObserver* observer);
 
-  // Add/remove TraceMessageFilter{Observer}.
-  void AddTraceMessageFilter(TraceMessageFilter* trace_message_filter);
-  void RemoveTraceMessageFilter(TraceMessageFilter* trace_message_filter);
-  void AddTraceMessageFilterObserver(TraceMessageFilterObserver* observer);
-  void RemoveTraceMessageFilterObserver(TraceMessageFilterObserver* observer);
+  // Add/remove Agent{Observer}.
+  void AddAgent(tracing::mojom::BackgroundTracingAgent* agent);
+  void RemoveAgent(tracing::mojom::BackgroundTracingAgent* agent);
+  void AddAgentObserver(AgentObserver* observer);
+  void RemoveAgentObserver(AgentObserver* observer);
 
   void AddMetadataGeneratorFunction();
 
@@ -139,12 +151,11 @@
   std::map<TriggerHandle, std::string> trigger_handles_;
   int trigger_handle_ids_;
 
-  // There is no need to use base::ObserverList to store observers because we
-  // only access |background_tracing_observers_| and
-  // |trace_message_filter_observers_| from the UI thread.
+  // Note, these sets are not mutated during iteration so it is okay to not use
+  // base::ObserverList.
   std::set<EnabledStateObserver*> background_tracing_observers_;
-  std::set<scoped_refptr<TraceMessageFilter>> trace_message_filters_;
-  std::set<TraceMessageFilterObserver*> trace_message_filter_observers_;
+  std::set<tracing::mojom::BackgroundTracingAgent*> agents_;
+  std::set<AgentObserver*> agent_observers_;
 
   IdleCallback idle_callback_;
   base::RepeatingClosure tracing_enabled_callback_for_testing_;
diff --git a/content/browser/tracing/background_tracing_rule.cc b/content/browser/tracing/background_tracing_rule.cc
index 341ca54..b21a479 100644
--- a/content/browser/tracing/background_tracing_rule.cc
+++ b/content/browser/tracing/background_tracing_rule.cc
@@ -14,9 +14,8 @@
 #include "base/task/post_task.h"
 #include "base/timer/timer.h"
 #include "base/values.h"
-#include "components/tracing/common/tracing_messages.h"
+#include "components/tracing/common/background_tracing_agent.mojom.h"
 #include "content/browser/tracing/background_tracing_manager_impl.h"
-#include "content/browser/tracing/trace_message_filter.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -146,9 +145,8 @@
   std::string named_event_;
 };
 
-class HistogramRule
-    : public BackgroundTracingRule,
-      public BackgroundTracingManagerImpl::TraceMessageFilterObserver {
+class HistogramRule : public BackgroundTracingRule,
+                      public BackgroundTracingManagerImpl::AgentObserver {
  private:
   HistogramRule(const std::string& histogram_name,
                 int histogram_lower_value,
@@ -199,8 +197,7 @@
   ~HistogramRule() override {
     base::StatisticsRecorder::ClearCallback(histogram_name_);
     if (installed_) {
-      BackgroundTracingManagerImpl::GetInstance()
-          ->RemoveTraceMessageFilterObserver(this);
+      BackgroundTracingManagerImpl::GetInstance()->RemoveAgentObserver(this);
     }
   }
 
@@ -212,8 +209,7 @@
                    base::Unretained(this), histogram_name_,
                    histogram_lower_value_, histogram_upper_value_, repeat_));
 
-    BackgroundTracingManagerImpl::GetInstance()->AddTraceMessageFilterObserver(
-        this);
+    BackgroundTracingManagerImpl::GetInstance()->AddAgentObserver(this);
     installed_ = true;
   }
 
@@ -247,15 +243,14 @@
             base::Unretained(BackgroundTracingManagerImpl::GetInstance())));
   }
 
-  // BackgroundTracingManagerImpl::TraceMessageFilterObserver implementation
-  void OnTraceMessageFilterAdded(TraceMessageFilter* filter) override {
-    filter->Send(
-        new TracingMsg_SetUMACallback(histogram_name_, histogram_lower_value_,
-                                      histogram_upper_value_, repeat_));
+  // BackgroundTracingManagerImpl::AgentObserver implementation
+  void OnAgentAdded(tracing::mojom::BackgroundTracingAgent* agent) override {
+    agent->SetUMACallback(histogram_name_, histogram_lower_value_,
+                          histogram_upper_value_, repeat_);
   }
 
-  void OnTraceMessageFilterRemoved(TraceMessageFilter* filter) override {
-    filter->Send(new TracingMsg_ClearUMACallback(histogram_name_));
+  void OnAgentRemoved(tracing::mojom::BackgroundTracingAgent* agent) override {
+    agent->ClearUMACallback(histogram_name_);
   }
 
   void OnHistogramChangedCallback(const std::string& histogram_name,
diff --git a/content/browser/tracing/trace_message_filter.cc b/content/browser/tracing/trace_message_filter.cc
deleted file mode 100644
index dbc18f8a..0000000
--- a/content/browser/tracing/trace_message_filter.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/tracing/trace_message_filter.h"
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/task/post_task.h"
-#include "components/tracing/common/tracing_messages.h"
-#include "content/browser/tracing/background_tracing_manager_impl.h"
-#include "content/common/child_process_host_impl.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-
-namespace content {
-
-TraceMessageFilter::TraceMessageFilter(int child_process_id)
-    : BrowserMessageFilter(TracingMsgStart),
-      has_child_(false),
-      tracing_process_id_(
-          ChildProcessHostImpl::ChildProcessUniqueIdToTracingProcessId(
-              child_process_id)) {}
-
-TraceMessageFilter::~TraceMessageFilter() {}
-
-void TraceMessageFilter::OnChannelConnected(int32_t peer_pid) {
-  Send(new TracingMsg_SetTracingProcessId(tracing_process_id_));
-}
-
-void TraceMessageFilter::OnChannelClosing() {
-  if (has_child_) {
-    base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
-                             base::BindOnce(&TraceMessageFilter::Unregister,
-                                            base::RetainedRef(this)));
-  }
-}
-
-bool TraceMessageFilter::OnMessageReceived(const IPC::Message& message) {
-  // Always on IO thread (BrowserMessageFilter guarantee).
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(TraceMessageFilter, message)
-    IPC_MESSAGE_HANDLER(TracingHostMsg_ChildSupportsTracing,
-                        OnChildSupportsTracing)
-    IPC_MESSAGE_HANDLER(TracingHostMsg_TriggerBackgroundTrace,
-                        OnTriggerBackgroundTrace)
-    IPC_MESSAGE_HANDLER(TracingHostMsg_AbortBackgroundTrace,
-                        OnAbortBackgroundTrace)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
-
-void TraceMessageFilter::OnChildSupportsTracing() {
-  has_child_ = true;
-  base::PostTaskWithTraits(
-      FROM_HERE, {BrowserThread::UI},
-      base::BindOnce(&TraceMessageFilter::Register, base::RetainedRef(this)));
-}
-
-void TraceMessageFilter::Register() {
-  BackgroundTracingManagerImpl::GetInstance()->AddTraceMessageFilter(this);
-}
-
-void TraceMessageFilter::Unregister() {
-  BackgroundTracingManagerImpl::GetInstance()->RemoveTraceMessageFilter(this);
-}
-
-void TraceMessageFilter::OnTriggerBackgroundTrace(const std::string& name) {
-  BackgroundTracingManagerImpl::GetInstance()->OnHistogramTrigger(name);
-}
-
-void TraceMessageFilter::OnAbortBackgroundTrace() {
-  BackgroundTracingManagerImpl::GetInstance()->AbortScenario();
-}
-
-}  // namespace content
diff --git a/content/browser/tracing/trace_message_filter.h b/content/browser/tracing/trace_message_filter.h
deleted file mode 100644
index 6727b35..0000000
--- a/content/browser/tracing/trace_message_filter.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_TRACING_TRACE_MESSAGE_FILTER_H_
-#define CONTENT_BROWSER_TRACING_TRACE_MESSAGE_FILTER_H_
-
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/trace_event/trace_event.h"
-#include "content/public/browser/browser_message_filter.h"
-
-namespace content {
-
-// This class sends and receives trace messages on the browser process.
-// See also: tracing_controller.h
-// See also: child_trace_message_filter.h
-class TraceMessageFilter : public BrowserMessageFilter {
- public:
-  explicit TraceMessageFilter(int child_process_id);
-
-  // BrowserMessageFilter implementation.
-  void OnChannelConnected(int32_t peer_pid) override;
-  void OnChannelClosing() override;
-  bool OnMessageReceived(const IPC::Message& message) override;
-
-  void SendSetWatchEvent(const std::string& category_name,
-                         const std::string& event_name);
-  void SendCancelWatchEvent();
-
- protected:
-  ~TraceMessageFilter() override;
-
- private:
-  // Message handlers.
-  void OnChildSupportsTracing();
-  void OnWatchEventMatched();
-  void OnTriggerBackgroundTrace(const std::string& histogram_name);
-  void OnAbortBackgroundTrace();
-
-  // For registering/unregistering the filter to different tracing
-  // managers/controllers.
-  void Register();
-  void Unregister();
-
-  // ChildTraceMessageFilter exists:
-  bool has_child_;
-
-  // Hash of id of the child process.
-  uint64_t tracing_process_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(TraceMessageFilter);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_TRACING_TRACE_MESSAGE_FILTER_H_
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index eec35654..62b2fa86 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -349,7 +349,7 @@
 void WebContentsImpl::FriendWrapper::RemoveCreatedCallbackForTesting(
     const CreatedCallback& callback) {
   for (size_t i = 0; i < g_created_callbacks.Get().size(); ++i) {
-    if (g_created_callbacks.Get().at(i).Equals(callback)) {
+    if (g_created_callbacks.Get().at(i) == callback) {
       g_created_callbacks.Get().erase(g_created_callbacks.Get().begin() + i);
       return;
     }
@@ -3105,15 +3105,15 @@
 bool WebContentsImpl::CheckMediaAccessPermission(
     RenderFrameHost* render_frame_host,
     const url::Origin& security_origin,
-    blink::MediaStreamType type) {
-  DCHECK(type == blink::MEDIA_DEVICE_AUDIO_CAPTURE ||
-         type == blink::MEDIA_DEVICE_VIDEO_CAPTURE);
+    blink::mojom::MediaStreamType type) {
+  DCHECK(type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE ||
+         type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
   return delegate_ && delegate_->CheckMediaAccessPermission(
                           render_frame_host, security_origin.GetURL(), type);
 }
 
 std::string WebContentsImpl::GetDefaultMediaDeviceID(
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   if (!delegate_)
     return std::string();
   return delegate_->GetDefaultMediaDeviceID(this, type);
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 415d4a1..9aca7efe 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -655,8 +655,9 @@
                                     MediaResponseCallback callback) override;
   bool CheckMediaAccessPermission(RenderFrameHost* render_frame_host,
                                   const url::Origin& security_origin,
-                                  blink::MediaStreamType type) override;
-  std::string GetDefaultMediaDeviceID(blink::MediaStreamType type) override;
+                                  blink::mojom::MediaStreamType type) override;
+  std::string GetDefaultMediaDeviceID(
+      blink::mojom::MediaStreamType type) override;
   SessionStorageNamespace* GetSessionStorageNamespace(
       SiteInstance* instance) override;
   SessionStorageNamespaceMap GetSessionStorageNamespaceMap() override;
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index ee5760e..49efaee4 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -1232,25 +1232,22 @@
 ////////////////////////////////////////////////////////////////////////////////
 // WebContentsViewAura, aura::client::DragDropDelegate implementation:
 
-void WebContentsViewAura::OnDragEntered(const ui::DropTargetEvent& event) {
-#if defined(OS_WIN)
-  async_drop_navigation_observer_.reset();
-#endif
-
-  gfx::PointF transformed_pt;
+void WebContentsViewAura::DragEnteredCallback(
+    const ui::DropTargetEvent& event,
+    std::unique_ptr<DropData> drop_data,
+    base::WeakPtr<RenderWidgetHostViewBase> target,
+    base::Optional<gfx::PointF> transformed_pt) {
+  if (!target)
+    return;
   RenderWidgetHostImpl* target_rwh =
-      web_contents_->GetInputEventRouter()->GetRenderWidgetHostAtPoint(
-          web_contents_->GetRenderViewHost()->GetWidget()->GetView(),
-          event.location_f(), &transformed_pt);
-
+      RenderWidgetHostImpl::From(target->GetRenderWidgetHost());
   if (!IsValidDragTarget(target_rwh))
     return;
 
   current_rwh_for_drag_ = target_rwh->GetWeakPtr();
   current_rvh_for_drag_ =
       GetRenderViewHostID(web_contents_->GetRenderViewHost());
-  current_drop_data_.reset(new DropData());
-  PrepareDropData(current_drop_data_.get(), event.data());
+  current_drop_data_.reset(drop_data.release());
   current_rwh_for_drag_->FilterDropData(current_drop_data_.get());
 
   blink::WebDragOperationsMask op = ConvertToWeb(event.source_operations());
@@ -1266,26 +1263,50 @@
   if (drag_dest_delegate_)
     drag_dest_delegate_->DragInitialize(web_contents_);
 
+  DCHECK(transformed_pt.has_value());
   gfx::PointF screen_pt(display::Screen::GetScreen()->GetCursorScreenPoint());
   current_rwh_for_drag_->DragTargetDragEnter(
-      *current_drop_data_, transformed_pt, screen_pt, op,
+      *current_drop_data_, transformed_pt.value(), screen_pt, op,
       ui::EventFlagsToWebEventModifiers(event.flags()));
 
   if (drag_dest_delegate_) {
-    drag_dest_delegate_->OnReceiveDragData(event.data());
     drag_dest_delegate_->OnDragEnter();
   }
 }
 
-int WebContentsViewAura::OnDragUpdated(const ui::DropTargetEvent& event) {
-  gfx::PointF transformed_pt;
-  RenderWidgetHostImpl* target_rwh =
-      web_contents_->GetInputEventRouter()->GetRenderWidgetHostAtPoint(
-          web_contents_->GetRenderViewHost()->GetWidget()->GetView(),
-          event.location_f(), &transformed_pt);
+void WebContentsViewAura::OnDragEntered(const ui::DropTargetEvent& event) {
+#if defined(OS_WIN)
+  async_drop_navigation_observer_.reset();
+#endif
 
+  std::unique_ptr<DropData> drop_data = std::make_unique<DropData>();
+  // Calling this here as event.data might become invalid inside the callback.
+  PrepareDropData(drop_data.get(), event.data());
+
+  if (drag_dest_delegate_) {
+    drag_dest_delegate_->OnReceiveDragData(event.data());
+  }
+
+  web_contents_->GetInputEventRouter()
+      ->GetRenderWidgetHostAtPointAsynchronously(
+          web_contents_->GetRenderViewHost()->GetWidget()->GetView(),
+          event.location_f(),
+          base::BindOnce(&WebContentsViewAura::DragEnteredCallback,
+                         weak_ptr_factory_.GetWeakPtr(), event,
+                         std::move(drop_data)));
+}
+
+void WebContentsViewAura::DragUpdatedCallback(
+    const ui::DropTargetEvent& event,
+    std::unique_ptr<DropData> drop_data,
+    base::WeakPtr<RenderWidgetHostViewBase> target,
+    base::Optional<gfx::PointF> transformed_pt) {
+  if (!target)
+    return;
+  RenderWidgetHostImpl* target_rwh =
+      RenderWidgetHostImpl::From(target->GetRenderWidgetHost());
   if (!IsValidDragTarget(target_rwh))
-    return ui::DragDropTypes::DRAG_NONE;
+    return;
 
   aura::Window* root_window = GetNativeView()->GetRootWindow();
   aura::client::ScreenPositionClient* screen_position_client =
@@ -1307,20 +1328,35 @@
       current_rwh_for_drag_->DragTargetDragLeave(transformed_leave_point,
                                                  screen_pt);
     }
-    OnDragEntered(event);
+    DragEnteredCallback(event, std::move(drop_data), target, transformed_pt);
   }
 
-  if (!current_drop_data_)
-    return ui::DragDropTypes::DRAG_NONE;
+  if (!current_drop_data_) {
+    return;
+  }
 
+  DCHECK(transformed_pt.has_value());
   blink::WebDragOperationsMask op = ConvertToWeb(event.source_operations());
   target_rwh->DragTargetDragOver(
-      transformed_pt, screen_pt, op,
+      transformed_pt.value(), screen_pt, op,
       ui::EventFlagsToWebEventModifiers(event.flags()));
 
   if (drag_dest_delegate_)
     drag_dest_delegate_->OnDragOver();
+}
 
+int WebContentsViewAura::OnDragUpdated(const ui::DropTargetEvent& event) {
+  std::unique_ptr<DropData> drop_data = std::make_unique<DropData>();
+  // Calling this here as event.data might become invalid inside the callback.
+  PrepareDropData(drop_data.get(), event.data());
+
+  web_contents_->GetInputEventRouter()
+      ->GetRenderWidgetHostAtPointAsynchronously(
+          web_contents_->GetRenderViewHost()->GetWidget()->GetView(),
+          event.location_f(),
+          base::BindOnce(&WebContentsViewAura::DragUpdatedCallback,
+                         weak_ptr_factory_.GetWeakPtr(), event,
+                         std::move(drop_data)));
   return ConvertFromWeb(current_drag_op_);
 }
 
@@ -1342,25 +1378,31 @@
   current_drop_data_.reset();
 }
 
-int WebContentsViewAura::OnPerformDrop(const ui::DropTargetEvent& event) {
-  gfx::PointF transformed_pt;
+void WebContentsViewAura::PerformDropCallback(
+    const ui::DropTargetEvent& event,
+    std::unique_ptr<DropData> drop_data,
+    base::WeakPtr<RenderWidgetHostViewBase> target,
+    base::Optional<gfx::PointF> transformed_pt) {
+  if (!target)
+    return;
   RenderWidgetHostImpl* target_rwh =
-      web_contents_->GetInputEventRouter()->GetRenderWidgetHostAtPoint(
-          web_contents_->GetRenderViewHost()->GetWidget()->GetView(),
-          event.location_f(), &transformed_pt);
-
+      RenderWidgetHostImpl::From(target->GetRenderWidgetHost());
   if (!IsValidDragTarget(target_rwh))
-    return ui::DragDropTypes::DRAG_NONE;
+    return;
+
+  DCHECK(transformed_pt.has_value());
 
   gfx::PointF screen_pt(display::Screen::GetScreen()->GetCursorScreenPoint());
   if (target_rwh != current_rwh_for_drag_.get()) {
     if (current_rwh_for_drag_)
-      current_rwh_for_drag_->DragTargetDragLeave(transformed_pt, screen_pt);
-    OnDragEntered(event);
+      current_rwh_for_drag_->DragTargetDragLeave(transformed_pt.value(),
+                                                 screen_pt);
+    DragEnteredCallback(event, std::move(drop_data), target, transformed_pt);
   }
 
-  if (!current_drop_data_)
-    return ui::DragDropTypes::DRAG_NONE;
+  if (!current_drop_data_) {
+    return;
+  }
 
   const int key_modifiers = ui::EventFlagsToWebEventModifiers(event.flags());
 #if defined(OS_WIN)
@@ -1386,16 +1428,29 @@
       async_drop_navigation_observer_ =
           std::make_unique<AsyncDropNavigationObserver>(
               web_contents_, std::move(current_drop_data_), target_rwh,
-              transformed_pt, screen_pt, key_modifiers);
-
-      return ConvertFromWeb(current_drag_op_);
+              transformed_pt.value(), screen_pt, key_modifiers);
+      return;
     }
   }
 
 #endif
-  CompleteDrop(target_rwh, *current_drop_data_, transformed_pt, screen_pt,
-               key_modifiers);
+  CompleteDrop(target_rwh, *current_drop_data_, transformed_pt.value(),
+               screen_pt, key_modifiers);
   current_drop_data_.reset();
+}
+
+int WebContentsViewAura::OnPerformDrop(const ui::DropTargetEvent& event) {
+  std::unique_ptr<DropData> drop_data = std::make_unique<DropData>();
+  // Calling this here as event.data might become invalid inside the callback.
+  PrepareDropData(drop_data.get(), event.data());
+
+  web_contents_->GetInputEventRouter()
+      ->GetRenderWidgetHostAtPointAsynchronously(
+          web_contents_->GetRenderViewHost()->GetWidget()->GetView(),
+          event.location_f(),
+          base::BindOnce(&WebContentsViewAura::PerformDropCallback,
+                         weak_ptr_factory_.GetWeakPtr(), event,
+                         std::move(drop_data)));
   return ConvertFromWeb(current_drag_op_);
 }
 
diff --git a/content/browser/web_contents/web_contents_view_aura.h b/content/browser/web_contents/web_contents_view_aura.h
index 46ca496..7037ca5 100644
--- a/content/browser/web_contents/web_contents_view_aura.h
+++ b/content/browser/web_contents/web_contents_view_aura.h
@@ -73,6 +73,7 @@
   FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest,
                            DragDropVirtualFilesOriginateFromRenderer);
   FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, DragDropUrlData);
+  FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, DragDropOnOopif);
 
   class WindowObserver;
 
@@ -193,6 +194,19 @@
   void OnDragExited() override;
   int OnPerformDrop(const ui::DropTargetEvent& event) override;
 
+  void DragEnteredCallback(const ui::DropTargetEvent& event,
+                           std::unique_ptr<DropData> drop_data,
+                           base::WeakPtr<RenderWidgetHostViewBase> target,
+                           base::Optional<gfx::PointF> transformed_pt);
+  void DragUpdatedCallback(const ui::DropTargetEvent& event,
+                           std::unique_ptr<DropData> drop_data,
+                           base::WeakPtr<RenderWidgetHostViewBase> target,
+                           base::Optional<gfx::PointF> transformed_pt);
+  void PerformDropCallback(const ui::DropTargetEvent& event,
+                           std::unique_ptr<DropData> drop_data,
+                           base::WeakPtr<RenderWidgetHostViewBase> target,
+                           base::Optional<gfx::PointF> transformed_pt);
+
   // Completes a drop operation by communicating the drop data to the renderer
   // process.
   void CompleteDrop(RenderWidgetHostImpl* target_rwh,
@@ -270,11 +284,9 @@
 
   bool init_rwhv_with_null_parent_for_testing_;
 
-#if defined(OS_WIN)
-  // Used to ensure that the virtual files retrieval callback bound to this
+  // Used to ensure the drag and drop callbacks bound to this
   // object is canceled when this object is destroyed.
   base::WeakPtrFactory<WebContentsViewAura> weak_ptr_factory_{this};
-#endif
 
   DISALLOW_COPY_AND_ASSIGN(WebContentsViewAura);
 };
diff --git a/content/browser/web_contents/web_contents_view_aura_browsertest.cc b/content/browser/web_contents/web_contents_view_aura_browsertest.cc
index fd229b8..ed00327 100644
--- a/content/browser/web_contents/web_contents_view_aura_browsertest.cc
+++ b/content/browser/web_contents/web_contents_view_aura_browsertest.cc
@@ -41,9 +41,12 @@
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/test_utils.h"
 #include "content/shell/browser/shell.h"
+#include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
+#include "ui/base/dragdrop/drop_target_event.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/event_sink.h"
@@ -87,11 +90,28 @@
         shell()->web_contents());
   }
 
+  void SetUpOnMainThread() override {
+    // Setup the server to allow serving separate sites, so we can perform
+    // cross-process navigation.
+    host_resolver()->AddRule("*", "127.0.0.1");
+  }
+
   void SetUpCommandLine(base::CommandLine* cmd) override {
     cmd->AppendSwitchASCII(switches::kTouchEventFeatureDetection,
                            switches::kTouchEventFeatureDetectionEnabled);
   }
 
+  void OnDropComplete(RenderWidgetHostImpl* target_rwh,
+                      const DropData& drop_data,
+                      const gfx::PointF& client_pt,
+                      const gfx::PointF& screen_pt,
+                      int key_modifiers,
+                      bool drop_allowed) {
+    // Cache the data for verification.
+    drop_target_widget_ = target_rwh;
+    std::move(async_drop_closure_).Run();
+  }
+
   void TestOverscrollNavigation(bool touch_handler) {
     ASSERT_NO_FATAL_FAILURE(StartTestWithPage("/overscroll_navigation.html"));
     WebContentsImpl* web_contents =
@@ -231,6 +251,11 @@
     ContentBrowserTest::PostRunTestOnMainThread();
   }
 
+  RenderWidgetHostImpl* drop_target_widget_;
+
+  // A closure indicating that async drop operation has completed.
+  base::OnceClosure async_drop_closure_;
+
  private:
   std::unique_ptr<RenderFrameSubmissionObserver> frame_observer_;
 
@@ -487,6 +512,68 @@
   window->AddChild(shell()->web_contents()->GetContentNativeView());
 }
 
+IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, DragDropOnOopif) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url = embedded_test_server()->GetURL(
+      "a.com", "/overlapping_cross_site_iframe.html");
+  EXPECT_TRUE(NavigateToURL(shell(), url));
+
+  WebContentsImpl* contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+  WebContentsViewAura* view =
+      static_cast<WebContentsViewAura*>(contents->GetView());
+  ui::OSExchangeData data;
+
+  // Drop on the root frame.
+  {
+    view->RegisterDropCallbackForTesting(base::BindOnce(
+        &WebContentsViewAuraTest::OnDropComplete, base::Unretained(this)));
+    base::RunLoop run_loop;
+    async_drop_closure_ = run_loop.QuitClosure();
+
+    gfx::PointF point = {10, 10};
+    ui::DropTargetEvent event(data, point, point, ui::DragDropTypes::DRAG_COPY);
+    view->OnDragEntered(event);
+    view->OnPerformDrop(event);
+
+    run_loop.Run();
+
+    EXPECT_EQ(drop_target_widget_,
+              RenderWidgetHostImpl::From(contents->GetFrameTree()
+                                             ->root()
+                                             ->current_frame_host()
+                                             ->GetRenderWidgetHost()));
+  }
+  // Drop on the element in the root frame overlapping the embeded OOPIF.
+  {
+    view->RegisterDropCallbackForTesting(base::BindOnce(
+        &WebContentsViewAuraTest::OnDropComplete, base::Unretained(this)));
+    base::RunLoop run_loop;
+    async_drop_closure_ = run_loop.QuitClosure();
+
+    int left =
+        EvalJs(contents,
+               "document.getElementById('target').getBoundingClientRect().left")
+            .ExtractInt();
+    int top =
+        EvalJs(contents,
+               "document.getElementById('target').getBoundingClientRect().top")
+            .ExtractInt();
+    gfx::PointF point = {left + 5, top + 5};
+    ui::DropTargetEvent event(data, point, point, ui::DragDropTypes::DRAG_COPY);
+    view->OnDragEntered(event);
+    view->OnPerformDrop(event);
+
+    run_loop.Run();
+
+    EXPECT_EQ(drop_target_widget_,
+              RenderWidgetHostImpl::From(contents->GetFrameTree()
+                                             ->root()
+                                             ->current_frame_host()
+                                             ->GetRenderWidgetHost()));
+  }
+}
+
 IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, ContentWindowClose) {
   ASSERT_NO_FATAL_FAILURE(StartTestWithPage("/overscroll_navigation.html"));
 
diff --git a/content/browser/web_package/signed_exchange_cert_fetcher.cc b/content/browser/web_package/signed_exchange_cert_fetcher.cc
index 8537b8b..2699338 100644
--- a/content/browser/web_package/signed_exchange_cert_fetcher.cc
+++ b/content/browser/web_package/signed_exchange_cert_fetcher.cc
@@ -14,11 +14,11 @@
 #include "base/trace_event/trace_event.h"
 #include "content/browser/data_url_loader_factory.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
+#include "content/browser/loader/single_request_url_loader_factory.h"
 #include "content/browser/web_package/signed_exchange_consts.h"
 #include "content/browser/web_package/signed_exchange_devtools_proxy.h"
 #include "content/browser/web_package/signed_exchange_reporter.h"
 #include "content/browser/web_package/signed_exchange_utils.h"
-#include "content/common/single_request_url_loader_factory.h"
 #include "content/common/throttling_url_loader.h"
 #include "content/public/common/resource_type.h"
 #include "content/public/common/url_loader_throttle.h"
diff --git a/content/browser/worker_host/worker_script_loader.h b/content/browser/worker_host/worker_script_loader.h
index ecb5e78b..5b2e25bb 100644
--- a/content/browser/worker_host/worker_script_loader.h
+++ b/content/browser/worker_host/worker_script_loader.h
@@ -12,8 +12,8 @@
 
 #include "base/macros.h"
 #include "base/optional.h"
+#include "content/browser/loader/single_request_url_loader_factory.h"
 #include "content/browser/navigation_subresource_loader_params.h"
-#include "content/common/single_request_url_loader_factory.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/resource_request.h"
diff --git a/content/child/BUILD.gn b/content/child/BUILD.gn
index d696471..e630dbbb 100644
--- a/content/child/BUILD.gn
+++ b/content/child/BUILD.gn
@@ -89,6 +89,7 @@
     "//components/discardable_memory/public/interfaces",
     "//components/tracing",
     "//components/tracing:startup_tracing",
+    "//components/tracing/common:interfaces",
     "//components/variations",
     "//components/webcrypto",
     "//content/app/resources",
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc
index e7807e56..2c058a2 100644
--- a/content/child/child_thread_impl.cc
+++ b/content/child/child_thread_impl.cc
@@ -37,7 +37,7 @@
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
-#include "components/tracing/child/child_trace_message_filter.h"
+#include "components/tracing/child/background_tracing_agent_impl.h"
 #include "content/child/child_histogram_fetcher_impl.h"
 #include "content/child/child_process.h"
 #include "content/child/thread_safe_sender.h"
@@ -446,11 +446,12 @@
   registry->AddInterface(base::Bind(&ChildThreadImpl::OnChildControlRequest,
                                     base::Unretained(this)),
                          base::ThreadTaskRunnerHandle::Get());
+  registry->AddInterface(
+      base::Bind(&tracing::BackgroundTracingAgentImpl::CreateFromRequest),
+      base::ThreadTaskRunnerHandle::Get());
   GetServiceManagerConnection()->AddConnectionFilter(
       std::make_unique<SimpleConnectionFilter>(std::move(registry)));
 
-  InitTracing();
-
   // In single process mode, browser-side tracing and memory will cover the
   // whole process including renderers.
   if (!IsInBrowserProcess()) {
@@ -539,25 +540,6 @@
   }
 }
 
-void ChildThreadImpl::InitTracing() {
-  // In single process mode, browser-side tracing and memory will cover the
-  // whole process including renderers.
-  if (IsInBrowserProcess())
-    return;
-
-  // Tracing adds too much overhead to the profiling service. The only
-  // way to determine if this is the profiling service is by checking the
-  // sandbox type.
-  service_manager::SandboxType sandbox_type =
-      service_manager::SandboxTypeFromCommandLine(
-          *base::CommandLine::ForCurrentProcess());
-  if (sandbox_type == service_manager::SANDBOX_TYPE_PROFILING)
-    return;
-
-  channel_->AddFilter(new tracing::ChildTraceMessageFilter(
-      ChildProcess::current()->io_task_runner()));
-}
-
 ChildThreadImpl::~ChildThreadImpl() {
 #if BUILDFLAG(IPC_MESSAGE_LOG_ENABLED)
   IPC::Logging::GetInstance()->SetIPCSender(NULL);
diff --git a/content/child/child_thread_impl.h b/content/child/child_thread_impl.h
index a10be95..3245d77 100644
--- a/content/child/child_thread_impl.h
+++ b/content/child/child_thread_impl.h
@@ -179,9 +179,6 @@
 
   void Init(const Options& options);
 
-  // Initializes tracing if necessary.
-  void InitTracing();
-
   // We create the channel first without connecting it so we can add filters
   // prior to any messages being received, then connect it afterwards.
   void ConnectChannel();
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 57fc7c9..b44db133 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -293,9 +293,6 @@
       base::FeatureList::IsEnabled(
           features::kPaymentRequestHasEnrolledInstrument));
 
-  WebRuntimeFeatures::EnablePaymentRetry(
-      base::FeatureList::IsEnabled(features::kPaymentResponseRetry));
-
   if (base::FeatureList::IsEnabled(features::kServiceWorkerPaymentApps))
     WebRuntimeFeatures::EnablePaymentApp(true);
 
@@ -440,8 +437,11 @@
   if (command_line.HasSwitch(switches::kEnableAccessibilityObjectModel))
     WebRuntimeFeatures::EnableAccessibilityObjectModel(true);
 
-  if (base::FeatureList::IsEnabled(blink::features::kNativeFileSystemAPI))
+  if (base::FeatureList::IsEnabled(blink::features::kNativeFileSystemAPI)) {
     WebRuntimeFeatures::EnableFeatureFromString("NativeFileSystem", true);
+    if (base::FeatureList::IsEnabled(blink::features::kFileHandlingAPI))
+      WebRuntimeFeatures::EnableFeatureFromString("FileHandling", true);
+  }
 
   if (base::FeatureList::IsEnabled(
           blink::features::kForbidSyncXHRInPageDismissal)) {
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index a46d4f4..b4779ea 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -227,8 +227,6 @@
     "service_worker/service_worker_types.h",
     "service_worker/service_worker_utils.cc",
     "service_worker/service_worker_utils.h",
-    "single_request_url_loader_factory.cc",
-    "single_request_url_loader_factory.h",
     "skia_utils.cc",
     "skia_utils.h",
     "swapped_out_messages.cc",
diff --git a/content/common/DEPS b/content/common/DEPS
index f571556b..1d1d07c 100644
--- a/content/common/DEPS
+++ b/content/common/DEPS
@@ -54,7 +54,6 @@
   "+third_party/blink/public/platform/modules/device_orientation/WebDeviceMotionData.h",
   "+third_party/blink/public/platform/modules/device_orientation/WebDeviceOrientationData.h",
   "+third_party/blink/public/platform/modules/mediastream/media_devices.mojom.h",
-  "+third_party/blink/public/platform/modules/push_messaging/web_push_error.h",
   "+third_party/blink/public/platform/modules/remoteplayback/web_remote_playback_availability.h",
   "+third_party/blink/public/platform/modules/payments/WebPaymentAppRequest.h",
   "+third_party/blink/public/platform/modules/service_worker/WebServiceWorkerClientType.h",
diff --git a/content/common/cursors/webcursor_aura.cc b/content/common/cursors/webcursor_aura.cc
index 6e665d2..5a3b5c0 100644
--- a/content/common/cursors/webcursor_aura.cc
+++ b/content/common/cursors/webcursor_aura.cc
@@ -57,6 +57,10 @@
       return ui::CursorType::kRowResize;
     case WebCursorInfo::kTypeMiddlePanning:
       return ui::CursorType::kMiddlePanning;
+    case WebCursorInfo::kTypeMiddlePanningVertical:
+      return ui::CursorType::kMiddlePanningVertical;
+    case WebCursorInfo::kTypeMiddlePanningHorizontal:
+      return ui::CursorType::kMiddlePanningHorizontal;
     case WebCursorInfo::kTypeEastPanning:
       return ui::CursorType::kEastPanning;
     case WebCursorInfo::kTypeNorthPanning:
diff --git a/content/common/cursors/webcursor_mac.mm b/content/common/cursors/webcursor_mac.mm
index ef1b280..cd5ebb3 100644
--- a/content/common/cursors/webcursor_mac.mm
+++ b/content/common/cursors/webcursor_mac.mm
@@ -224,6 +224,8 @@
     case WebCursorInfo::kTypeRowResize:
       return [NSCursor resizeUpDownCursor];
     case WebCursorInfo::kTypeMiddlePanning:
+    case WebCursorInfo::kTypeMiddlePanningVertical:
+    case WebCursorInfo::kTypeMiddlePanningHorizontal:
     case WebCursorInfo::kTypeMove:
       return GetCoreCursorWithFallback(kMoveCursor,
                                        IDR_MOVE_CURSOR, 7, 7);
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
index dbc5791..9dca1fda 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
@@ -946,9 +946,7 @@
 
     @CalledByNative
     private void handleSliderChanged(int id) {
-        // Sending a TYPE_VIEW_SELECTED event triggers talkback to report new
-        // slider value
-        sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_SELECTED);
+        sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_SCROLLED);
     }
 
     @CalledByNative
diff --git a/content/public/app/content_gpu_manifest.cc b/content/public/app/content_gpu_manifest.cc
index 06b9be3..586eaedc 100644
--- a/content/public/app/content_gpu_manifest.cc
+++ b/content/public/app/content_gpu_manifest.cc
@@ -23,6 +23,7 @@
                                 "content.mojom.ChildHistogramFetcherFactory",
                                 "content.mojom.ResourceUsageReporter",
                                 "IPC.mojom.ChannelBootstrap",
+                                "tracing.mojom.BackgroundTracingAgent",
                                 "ui.ozone.mojom.DeviceCursor",
                                 "ui.ozone.mojom.DrmDevice",
                                 "ui.ozone.mojom.WaylandBufferManagerGpu",
diff --git a/content/public/app/content_plugin_manifest.cc b/content/public/app/content_plugin_manifest.cc
index 6d17755..c3dd3fb 100644
--- a/content/public/app/content_plugin_manifest.cc
+++ b/content/public/app/content_plugin_manifest.cc
@@ -23,6 +23,7 @@
                                 "content.mojom.ChildHistogramFetcherFactory",
                                 "content.mojom.ResourceUsageReporter",
                                 "IPC.mojom.ChannelBootstrap",
+                                "tracing.mojom.BackgroundTracingAgent",
                             })
           .RequireCapability("device", "device:power_monitor")
           .RequireCapability(mojom::kSystemServiceName, "dwrite_font_proxy")
diff --git a/content/public/app/content_renderer_manifest.cc b/content/public/app/content_renderer_manifest.cc
index 6d6781a2..3bbecbe 100644
--- a/content/public/app/content_renderer_manifest.cc
+++ b/content/public/app/content_renderer_manifest.cc
@@ -34,6 +34,7 @@
                   "content.mojom.RenderWidgetWindowTreeClientFactory",
                   "content.mojom.ResourceUsageReporter",
                   "IPC.mojom.ChannelBootstrap",
+                  "tracing.mojom.BackgroundTracingAgent",
                   "visitedlink.mojom.VisitedLinkNotificationSink",
                   "web_cache.mojom.WebCache",
               })
diff --git a/content/public/app/content_utility_manifest.cc b/content/public/app/content_utility_manifest.cc
index e3eafb7..575a2a37 100644
--- a/content/public/app/content_utility_manifest.cc
+++ b/content/public/app/content_utility_manifest.cc
@@ -24,6 +24,7 @@
                                 "content.mojom.ChildHistogramFetcherFactory",
                                 "content.mojom.ResourceUsageReporter",
                                 "IPC.mojom.ChannelBootstrap",
+                                "tracing.mojom.BackgroundTracingAgent",
                                 "printing.mojom.PdfToEmfConverterFactory",
                                 "printing.mojom.PdfToPwgRasterConverter",
                             })
diff --git a/content/public/browser/media_device_id.cc b/content/public/browser/media_device_id.cc
index 8a7651e..72d011f 100644
--- a/content/public/browser/media_device_id.cc
+++ b/content/public/browser/media_device_id.cc
@@ -26,7 +26,7 @@
       salt, security_origin, device_guid, raw_unique_id);
 }
 
-bool GetMediaDeviceIDForHMAC(blink::MediaStreamType stream_type,
+bool GetMediaDeviceIDForHMAC(blink::mojom::MediaStreamType stream_type,
                              const std::string& salt,
                              const url::Origin& security_origin,
                              const std::string& source_id,
@@ -34,13 +34,13 @@
   content::MediaStreamManager* manager =
       content::BrowserMainLoop::GetInstance()->media_stream_manager();
 
-  return manager->TranslateSourceIdToDeviceId(blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                                              salt, security_origin, source_id,
-                                              device_id);
+  return manager->TranslateSourceIdToDeviceId(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, salt,
+      security_origin, source_id, device_id);
 }
 
 void GetMediaDeviceIDForHMAC(
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     std::string salt,
     url::Origin security_origin,
     std::string hmac_device_id,
diff --git a/content/public/browser/media_device_id.h b/content/public/browser/media_device_id.h
index 6d3b1ce..7b49a7b 100644
--- a/content/public/browser/media_device_id.h
+++ b/content/public/browser/media_device_id.h
@@ -35,18 +35,19 @@
     const std::string& raw_unique_id);
 
 // This function is deprecated. Use the callback version below instead.
-CONTENT_EXPORT bool GetMediaDeviceIDForHMAC(blink::MediaStreamType stream_type,
-                                            const std::string& salt,
-                                            const url::Origin& security_origin,
-                                            const std::string& source_id,
-                                            std::string* device_id);
+CONTENT_EXPORT bool GetMediaDeviceIDForHMAC(
+    blink::mojom::MediaStreamType stream_type,
+    const std::string& salt,
+    const url::Origin& security_origin,
+    const std::string& source_id,
+    std::string* device_id);
 
 // Returns the raw device ID for the given HMAC |hmac_device_id| for the given
 // |security_origin| and |salt|. The result is passed via |callback| on the
 // task runner where this function is called. If |hmac_device_id| is not a
 // valid device ID nullopt is returned.
 CONTENT_EXPORT void GetMediaDeviceIDForHMAC(
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     std::string salt,
     url::Origin security_origin,
     std::string hmac_device_id,
diff --git a/content/public/browser/media_observer.h b/content/public/browser/media_observer.h
index 29553ba..fea4350 100644
--- a/content/public/browser/media_observer.h
+++ b/content/public/browser/media_observer.h
@@ -24,12 +24,13 @@
   virtual void OnVideoCaptureDevicesChanged() = 0;
 
   // Called when a media request changes state.
-  virtual void OnMediaRequestStateChanged(int render_process_id,
-                                          int render_frame_id,
-                                          int page_request_id,
-                                          const GURL& security_origin,
-                                          blink::MediaStreamType stream_type,
-                                          MediaRequestState state) = 0;
+  virtual void OnMediaRequestStateChanged(
+      int render_process_id,
+      int render_frame_id,
+      int page_request_id,
+      const GURL& security_origin,
+      blink::mojom::MediaStreamType stream_type,
+      MediaRequestState state) = 0;
 
   // Called when an audio stream is being created.
   virtual void OnCreatingAudioStream(int render_process_id,
@@ -37,11 +38,12 @@
 
   // Called when the secure display link status of one or more consumers of this
   // media stream has changed.
-  virtual void OnSetCapturingLinkSecured(int render_process_id,
-                                         int render_frame_id,
-                                         int page_request_id,
-                                         blink::MediaStreamType stream_type,
-                                         bool is_secure) = 0;
+  virtual void OnSetCapturingLinkSecured(
+      int render_process_id,
+      int render_frame_id,
+      int page_request_id,
+      blink::mojom::MediaStreamType stream_type,
+      bool is_secure) = 0;
 
  protected:
   virtual ~MediaObserver() {}
diff --git a/content/public/browser/media_stream_request.cc b/content/public/browser/media_stream_request.cc
index 098abee5..c0ba844c 100644
--- a/content/public/browser/media_stream_request.cc
+++ b/content/public/browser/media_stream_request.cc
@@ -15,8 +15,8 @@
     blink::MediaStreamRequestType request_type,
     const std::string& requested_audio_device_id,
     const std::string& requested_video_device_id,
-    blink::MediaStreamType audio_type,
-    blink::MediaStreamType video_type,
+    blink::mojom::MediaStreamType audio_type,
+    blink::mojom::MediaStreamType video_type,
     bool disable_local_echo)
     : render_process_id(render_process_id),
       render_frame_id(render_frame_id),
diff --git a/content/public/browser/media_stream_request.h b/content/public/browser/media_stream_request.h
index fc1f6b7..24bc0cb 100644
--- a/content/public/browser/media_stream_request.h
+++ b/content/public/browser/media_stream_request.h
@@ -31,8 +31,8 @@
                      blink::MediaStreamRequestType request_type,
                      const std::string& requested_audio_device_id,
                      const std::string& requested_video_device_id,
-                     blink::MediaStreamType audio_type,
-                     blink::MediaStreamType video_type,
+                     blink::mojom::MediaStreamType audio_type,
+                     blink::mojom::MediaStreamType video_type,
                      bool disable_local_echo);
 
   MediaStreamRequest(const MediaStreamRequest& other);
@@ -70,10 +70,10 @@
   std::string requested_video_device_id;
 
   // Flag to indicate if the request contains audio.
-  blink::MediaStreamType audio_type;
+  blink::mojom::MediaStreamType audio_type;
 
   // Flag to indicate if the request contains video.
-  blink::MediaStreamType video_type;
+  blink::mojom::MediaStreamType video_type;
 
   // Flag for desktop or tab share to indicate whether to prevent the captured
   // audio being played out locally.
diff --git a/content/public/browser/video_capture_device_launcher.h b/content/public/browser/video_capture_device_launcher.h
index 2483b591..3d468502 100644
--- a/content/public/browser/video_capture_device_launcher.h
+++ b/content/public/browser/video_capture_device_launcher.h
@@ -45,7 +45,7 @@
   // during the asynchronous processing stays alive.
   virtual void LaunchDeviceAsync(
       const std::string& device_id,
-      blink::MediaStreamType stream_type,
+      blink::mojom::MediaStreamType stream_type,
       const media::VideoCaptureParams& params,
       base::WeakPtr<media::VideoFrameReceiver> receiver,
       base::OnceClosure connection_lost_cb,
diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc
index 5f832d9..276af7b 100644
--- a/content/public/browser/web_contents_delegate.cc
+++ b/content/public/browser/web_contents_delegate.cc
@@ -200,7 +200,7 @@
 bool WebContentsDelegate::CheckMediaAccessPermission(
     RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   LOG(ERROR) << "WebContentsDelegate::CheckMediaAccessPermission: "
              << "Not supported.";
   return false;
@@ -208,7 +208,7 @@
 
 std::string WebContentsDelegate::GetDefaultMediaDeviceID(
     WebContents* web_contents,
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   return std::string();
 }
 
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index e99b9c521..5c24dc8dd 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -509,13 +509,14 @@
   // or MEDIA_DEVICE_VIDEO_CAPTURE.
   virtual bool CheckMediaAccessPermission(RenderFrameHost* render_frame_host,
                                           const GURL& security_origin,
-                                          blink::MediaStreamType type);
+                                          blink::mojom::MediaStreamType type);
 
   // Returns the ID of the default device for the given media device |type|.
   // If the returned value is an empty string, it means that there is no
   // default device for the given |type|.
-  virtual std::string GetDefaultMediaDeviceID(WebContents* web_contents,
-                                              blink::MediaStreamType type);
+  virtual std::string GetDefaultMediaDeviceID(
+      WebContents* web_contents,
+      blink::mojom::MediaStreamType type);
 
 #if defined(OS_ANDROID)
   // Returns true if the given media should be blocked to load.
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 4555cda..35fbcc09 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -321,10 +321,6 @@
 const base::Feature kPaymentRequestHasEnrolledInstrument = {
     "PaymentRequestHasEnrolledInstrument", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Whether PaymentResponse exposes retry API.
-const base::Feature kPaymentResponseRetry = {"PaymentResponseRetry",
-                                             base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Whether ExecutionContext is paused (and workers) on background freeze.
 const base::Feature kPauseExecutionContextOnBackgroundFreeze = {
     "PauseExecutionContextOnBackgroundFreeze",
@@ -701,10 +697,6 @@
 const base::Feature kWebXrPlaneDetection{"WebXRPlaneDetection",
                                          base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Wipe corrupt v2 IndexedDB databases.
-const base::Feature kWipeCorruptV2IDBDatabases{
-    "WipeCorruptV2IDBDatabases", base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Start streaming scripts on script preload.
 const base::Feature kScriptStreamingOnPreload{
     "ScriptStreamingOnPreload", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index c61453f..ad4041e 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -80,7 +80,6 @@
 CONTENT_EXPORT extern const base::Feature
     kPauseExecutionContextOnBackgroundFreeze;
 CONTENT_EXPORT extern const base::Feature kPaymentRequestHasEnrolledInstrument;
-CONTENT_EXPORT extern const base::Feature kPaymentResponseRetry;
 CONTENT_EXPORT extern const base::Feature kPdfIsolation;
 CONTENT_EXPORT extern const base::Feature kPeriodicBackgroundSync;
 CONTENT_EXPORT extern const base::Feature kPerNavigationMojoInterface;
@@ -149,7 +148,6 @@
 CONTENT_EXPORT extern const base::Feature kWebXr;
 CONTENT_EXPORT extern const base::Feature kWebXrHitTest;
 CONTENT_EXPORT extern const base::Feature kWebXrPlaneDetection;
-CONTENT_EXPORT extern const base::Feature kWipeCorruptV2IDBDatabases;
 CONTENT_EXPORT extern const base::Feature kScriptStreamingOnPreload;
 
 #if defined(OS_ANDROID)
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 14b5876..7763570 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -340,6 +340,9 @@
 // features.
 const char kEnableBlinkFeatures[]           = "enable-blink-features";
 
+// Enable default SiteInstance to be used for all unisolated sites.
+const char kEnableDefaultSiteInstance[] = "enable-default-site-instance";
+
 // Enables Web Platform features that are in development.
 const char kEnableExperimentalWebPlatformFeatures[] =
     "enable-experimental-web-platform-features";
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index d98d2fea..8a49f65 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -111,6 +111,7 @@
 CONTENT_EXPORT extern const char kEnablePreferCompositingToLCDText[];
 CONTENT_EXPORT extern const char kEnableBlinkFeatures[];
 CONTENT_EXPORT extern const char kEnableDisplayList2dCanvas[];
+CONTENT_EXPORT extern const char kEnableDefaultSiteInstance[];
 CONTENT_EXPORT extern const char kEnableExperimentalWebPlatformFeatures[];
 CONTENT_EXPORT extern const char kEnableGpuMemoryBufferCompositorResources[];
 CONTENT_EXPORT extern const char kEnableGpuMemoryBufferVideoFrames[];
diff --git a/content/public/test/test_launcher.cc b/content/public/test/test_launcher.cc
index ca55c798..cf8db90 100644
--- a/content/public/test/test_launcher.cc
+++ b/content/public/test/test_launcher.cc
@@ -143,8 +143,6 @@
   bool GetTests(std::vector<base::TestIdentifier>* output) override;
   bool WillRunTest(const std::string& test_case_name,
                    const std::string& test_name) override;
-  bool ShouldRunTest(const std::string& test_case_name,
-                     const std::string& test_name) override;
   size_t RunTests(base::TestLauncher* test_launcher,
                   const std::vector<std::string>& test_names) override;
   size_t RetryTests(base::TestLauncher* test_launcher,
@@ -198,12 +196,6 @@
   void DoRunTests(base::TestLauncher* test_launcher,
                   const std::vector<std::string>& test_names);
 
-  // Launches test named |test_name| using parallel launcher,
-  // given result of PRE_ test |pre_test_result|.
-  void RunDependentTest(base::TestLauncher* test_launcher,
-                        const std::string test_name,
-                        const base::TestResult& pre_test_result);
-
   // Relays timeout notification from the TestLauncher (by way of a
   // ProcessLifetimeObserver) to the caller's content::TestLauncherDelegate.
   void OnTestTimedOut(const base::CommandLine& command_line);
@@ -225,11 +217,6 @@
 
   content::TestLauncherDelegate* launcher_delegate_;
 
-  // Store dependent test name (map is indexed by full test name).
-  typedef std::map<std::string, std::string> DependentTestMap;
-  DependentTestMap dependent_test_map_;
-  DependentTestMap reverse_dependent_test_map_;
-
   // Store unique data directory prefix for test names (without PRE_ prefixes).
   // PRE_ tests and tests that depend on them must share the same
   // data directory. Using test name as directory name leads to too long
@@ -239,9 +226,6 @@
   typedef std::map<std::string, base::FilePath> UserDataDirMap;
   UserDataDirMap user_data_dir_map_;
 
-  // Store names of all seen tests to properly handle PRE_ tests.
-  std::set<std::string> all_test_names_;
-
   // Temporary directory for user data directories.
   base::ScopedTempDir temp_dir_;
 
@@ -255,14 +239,11 @@
 }
 
 bool IsPreTestName(const std::string& test_name) {
-  return base::StartsWith(test_name, kPreTestPrefix,
-                          base::CompareCase::SENSITIVE);
+  return test_name.find(kPreTestPrefix) != std::string::npos;
 }
 
 bool WrapperTestLauncherDelegate::WillRunTest(const std::string& test_case_name,
                                               const std::string& test_name) {
-  all_test_names_.insert(test_case_name + "." + test_name);
-
   if (base::StartsWith(test_name, kManualTestPrefix,
                        base::CompareCase::SENSITIVE) &&
       !base::CommandLine::ForCurrentProcess()->HasSwitch(kRunManualTestsFlag)) {
@@ -272,101 +253,32 @@
   return true;
 }
 
-bool WrapperTestLauncherDelegate::ShouldRunTest(
-    const std::string& test_case_name,
-    const std::string& test_name) {
-  if (!WillRunTest(test_case_name, test_name))
-    return false;
-
-  if (IsPreTestName(test_name)) {
-    // We will actually run PRE_ tests, but to ensure they run on the same shard
-    // as dependent tests, handle all these details internally.
-    return false;
-  }
-
-  return true;
-}
-
-std::string GetPreTestName(const std::string& full_name) {
-  size_t dot_pos = full_name.find('.');
-  CHECK_NE(dot_pos, std::string::npos);
-  std::string test_case_name = full_name.substr(0, dot_pos);
-  std::string test_name = full_name.substr(dot_pos + 1);
-  return test_case_name + "." + kPreTestPrefix + test_name;
-}
-
 size_t WrapperTestLauncherDelegate::RunTests(
     base::TestLauncher* test_launcher,
     const std::vector<std::string>& test_names) {
-  dependent_test_map_.clear();
-  reverse_dependent_test_map_.clear();
   user_data_dir_map_.clear();
 
-  // Number of additional tests to run because of dependencies.
-  size_t additional_tests_to_run_count = 0;
-
-  // Compute dependencies of tests to be run.
+  std::vector<std::string> test_list;
   for (const std::string& test_name : test_names) {
-    std::string full_name(test_name);
-    std::string pre_test_name(GetPreTestName(full_name));
-
-    while (base::ContainsKey(all_test_names_, pre_test_name)) {
-      additional_tests_to_run_count++;
-
-      DCHECK(!base::ContainsKey(dependent_test_map_, pre_test_name));
-      dependent_test_map_[pre_test_name] = full_name;
-
-      DCHECK(!base::ContainsKey(reverse_dependent_test_map_, full_name));
-      reverse_dependent_test_map_[full_name] = pre_test_name;
-
-      full_name = pre_test_name;
-      pre_test_name = GetPreTestName(pre_test_name);
+    // Stack all dependent tests and run them sequentially.
+    test_list.push_back(test_name);
+    if (!IsPreTestName(test_name)) {
+      if (!base::ContainsKey(user_data_dir_map_, test_name)) {
+        base::FilePath temp_dir;
+        CHECK(base::CreateTemporaryDirInDir(temp_dir_.GetPath(),
+                                            FILE_PATH_LITERAL("d"), &temp_dir));
+        user_data_dir_map_[test_name] = temp_dir;
+      }
+      DoRunTests(test_launcher, test_list);
+      test_list.clear();
     }
   }
-
-  for (const std::string& test_name : test_names) {
-    std::string full_name(test_name);
-    // Make sure no PRE_ tests were requested explicitly.
-    DCHECK_EQ(full_name, RemoveAnyPrePrefixes(full_name));
-
-    if (!base::ContainsKey(user_data_dir_map_, full_name)) {
-      base::FilePath temp_dir;
-      CHECK(base::CreateTemporaryDirInDir(temp_dir_.GetPath(),
-                                          FILE_PATH_LITERAL("d"), &temp_dir));
-      user_data_dir_map_[full_name] = temp_dir;
-    }
-
-    // If the test has any dependencies, get to the root and start with that.
-    while (base::ContainsKey(reverse_dependent_test_map_, full_name))
-      full_name = GetPreTestName(full_name);
-
-    std::vector<std::string> test_list;
-    test_list.push_back(full_name);
-    DoRunTests(test_launcher, test_list);
-  }
-
-  return test_names.size() + additional_tests_to_run_count;
+  return test_names.size();
 }
 
 size_t WrapperTestLauncherDelegate::RetryTests(
     base::TestLauncher* test_launcher,
     const std::vector<std::string>& test_names) {
-  // List of tests we can kick off right now, depending on no other tests.
-  std::vector<std::string> tests_to_run_now;
-
-  // We retry at least the tests requested to retry.
-  std::set<std::string> test_names_set(test_names.begin(), test_names.end());
-
-  // In the face of PRE_ tests, we need to retry the entire chain of tests,
-  // from the very first one.
-  for (const std::string& test_name : test_names) {
-    std::string name(test_name);
-    while (base::ContainsKey(reverse_dependent_test_map_, name)) {
-      name = reverse_dependent_test_map_[name];
-      test_names_set.insert(name);
-    }
-  }
-
   // Discard user data directories from any previous runs. Start with
   // fresh state.
   for (const auto& it : user_data_dir_map_) {
@@ -377,7 +289,7 @@
   }
   user_data_dir_map_.clear();
 
-  for (const std::string& full_name : test_names_set) {
+  for (const std::string& full_name : test_names) {
     // Make sure PRE_ tests and tests that depend on them share the same
     // data directory - based it on the test name without prefixes.
     std::string test_name_no_pre(RemoveAnyPrePrefixes(full_name));
@@ -387,15 +299,11 @@
                                           FILE_PATH_LITERAL("d"), &temp_dir));
       user_data_dir_map_[test_name_no_pre] = temp_dir;
     }
-
-    std::string pre_test_name = GetPreTestName(full_name);
-    if (!base::ContainsKey(test_names_set, pre_test_name))
-      tests_to_run_now.push_back(full_name);
   }
 
-  DoRunTests(test_launcher, tests_to_run_now);
+  DoRunTests(test_launcher, test_names);
 
-  return test_names_set.size();
+  return test_names.size();
 }
 
 void WrapperTestLauncherDelegate::DoRunTests(
@@ -455,30 +363,6 @@
       std::move(observer));
 }
 
-void WrapperTestLauncherDelegate::RunDependentTest(
-    base::TestLauncher* test_launcher,
-    const std::string test_name,
-    const base::TestResult& pre_test_result) {
-  if (pre_test_result.status == base::TestResult::TEST_SUCCESS) {
-    // Only run the dependent test if PRE_ test succeeded.
-    std::vector<std::string> test_list;
-    test_list.push_back(test_name);
-    DoRunTests(test_launcher, test_list);
-  } else {
-    // Otherwise mark the test as a failure.
-    base::TestResult test_result;
-    test_result.full_name = test_name;
-    test_result.status = base::TestResult::TEST_FAILURE;
-    test_launcher->OnTestFinished(test_result);
-
-    if (base::ContainsKey(dependent_test_map_, test_name)) {
-      RunDependentTest(test_launcher,
-                       dependent_test_map_[test_name],
-                       test_result);
-    }
-  }
-}
-
 void WrapperTestLauncherDelegate::OnTestTimedOut(
     const base::CommandLine& command_line) {
   launcher_delegate_->OnTestTimedOut(command_line);
@@ -543,19 +427,14 @@
 
   launcher_delegate_->PostRunTest(&result);
 
-  if (base::ContainsKey(dependent_test_map_, test_name)) {
-    RunDependentTest(test_launcher, dependent_test_map_[test_name], result);
-  } else {
     // No other tests depend on this, we can delete the temporary directory now.
     // Do so to avoid too many temporary files using lots of disk space.
-    std::string test_name_no_pre(RemoveAnyPrePrefixes(test_name));
-    if (base::ContainsKey(user_data_dir_map_, test_name_no_pre)) {
-      if (!base::DeleteFile(user_data_dir_map_[test_name_no_pre], true)) {
-        LOG(WARNING) << "Failed to delete "
-                     << user_data_dir_map_[test_name_no_pre].value();
-      }
-      user_data_dir_map_.erase(test_name_no_pre);
+  if (base::ContainsKey(user_data_dir_map_, test_name)) {
+    if (!base::DeleteFile(user_data_dir_map_[test_name], true)) {
+      LOG(WARNING) << "Failed to delete "
+                   << user_data_dir_map_[test_name].value();
     }
+    user_data_dir_map_.erase(test_name);
   }
 
   test_launcher->OnTestFinished(result);
diff --git a/content/public/test/test_utils.cc b/content/public/test/test_utils.cc
index 3d559ff..73a636f 100644
--- a/content/public/test/test_utils.cc
+++ b/content/public/test/test_utils.cc
@@ -189,6 +189,12 @@
   return SiteIsolationPolicy::UseDedicatedProcessesForAllSites();
 }
 
+bool AreDefaultSiteInstancesEnabled() {
+  return !AreAllSitesIsolatedForTesting() &&
+         base::CommandLine::ForCurrentProcess()->HasSwitch(
+             switches::kEnableDefaultSiteInstance);
+}
+
 void IsolateAllSitesForTesting(base::CommandLine* command_line) {
   command_line->AppendSwitch(switches::kSitePerProcess);
 }
@@ -450,6 +456,16 @@
   return last_scale_;
 }
 
+EffectiveURLContentBrowserClient::EffectiveURLContentBrowserClient(
+    const GURL& url_to_modify,
+    const GURL& url_to_return,
+    bool requires_dedicated_process)
+    : url_to_modify_(url_to_modify),
+      url_to_return_(url_to_return),
+      requires_dedicated_process_(requires_dedicated_process) {}
+
+EffectiveURLContentBrowserClient::~EffectiveURLContentBrowserClient() {}
+
 GURL EffectiveURLContentBrowserClient::GetEffectiveURL(
     BrowserContext* browser_context,
     const GURL& url) {
@@ -458,4 +474,14 @@
   return url;
 }
 
+bool EffectiveURLContentBrowserClient::DoesSiteRequireDedicatedProcess(
+    BrowserOrResourceContext browser_or_resource_context,
+    const GURL& effective_site_url) {
+  GURL expected_effective_site_url = SiteInstance::GetSiteForURL(
+      browser_or_resource_context.ToBrowserContext(), url_to_modify_);
+
+  return requires_dedicated_process_ &&
+         expected_effective_site_url == effective_site_url;
+}
+
 }  // namespace content
diff --git a/content/public/test/test_utils.h b/content/public/test/test_utils.h
index 49b4a98..90a6784 100644
--- a/content/public/test/test_utils.h
+++ b/content/public/test/test_utils.h
@@ -94,6 +94,10 @@
 // that is incompatible with --site-per-process.
 bool AreAllSitesIsolatedForTesting();
 
+// Returns true if default SiteInstances are enabled. Typically used in a test
+// to mark expectations specific to default SiteInstances.
+bool AreDefaultSiteInstancesEnabled();
+
 // Appends --site-per-process to the command line, enabling tests to exercise
 // site isolation and cross-process iframes. This must be called early in
 // the test; the flag will be read on the first real navigation.
@@ -371,16 +375,20 @@
 class EffectiveURLContentBrowserClient : public ContentBrowserClient {
  public:
   EffectiveURLContentBrowserClient(const GURL& url_to_modify,
-                                   const GURL& url_to_return)
-      : url_to_modify_(url_to_modify), url_to_return_(url_to_return) {}
-  ~EffectiveURLContentBrowserClient() override {}
+                                   const GURL& url_to_return,
+                                   bool requires_dedicated_process);
+  ~EffectiveURLContentBrowserClient() override;
 
  private:
   GURL GetEffectiveURL(BrowserContext* browser_context,
                        const GURL& url) override;
+  bool DoesSiteRequireDedicatedProcess(
+      BrowserOrResourceContext browser_or_resource_context,
+      const GURL& effective_site_url) override;
 
   GURL url_to_modify_;
   GURL url_to_return_;
+  bool requires_dedicated_process_;
 
   DISALLOW_COPY_AND_ASSIGN(EffectiveURLContentBrowserClient);
 };
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index d31e8c9..b281d1b 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -331,14 +331,6 @@
     "media/webrtc_local_audio_source_provider.h",
     "media/webrtc_logging.cc",
     "media/webrtc_logging.h",
-    "media_recorder/audio_track_encoder.cc",
-    "media_recorder/audio_track_encoder.h",
-    "media_recorder/audio_track_opus_encoder.cc",
-    "media_recorder/audio_track_opus_encoder.h",
-    "media_recorder/audio_track_pcm_encoder.cc",
-    "media_recorder/audio_track_pcm_encoder.h",
-    "media_recorder/audio_track_recorder.cc",
-    "media_recorder/audio_track_recorder.h",
     "media_recorder/media_recorder_handler.cc",
     "media_recorder/media_recorder_handler.h",
     "media_recorder/vea_encoder.cc",
@@ -591,7 +583,6 @@
     "//third_party/icu",
     "//third_party/libvpx",
     "//third_party/libyuv",
-    "//third_party/opus",
     "//third_party/sqlite",
     "//third_party/webrtc/api:callfactory_api",
     "//third_party/webrtc/api:libjingle_logging_api",
diff --git a/content/renderer/accessibility/ax_image_annotator.cc b/content/renderer/accessibility/ax_image_annotator.cc
index be98889d..8021718 100644
--- a/content/renderer/accessibility/ax_image_annotator.cc
+++ b/content/renderer/accessibility/ax_image_annotator.cc
@@ -178,7 +178,7 @@
   blink::WebAXObject parent = image.ParentObject();
   for (int ancestor_count = 0; !parent.IsDetached() && ancestor_count < 2;
        parent = parent.ParentObject()) {
-    if (!parent.AccessibilityIsIgnored()) {
+    if (parent.AccessibilityIsIncludedInTree()) {
       ++ancestor_count;
       if (parent.Role() == ax::mojom::Role::kLink ||
           parent.Role() == ax::mojom::Role::kRootWebArea) {
diff --git a/content/renderer/accessibility/blink_ax_enum_conversion.cc b/content/renderer/accessibility/blink_ax_enum_conversion.cc
index 023b603..114d1c9 100644
--- a/content/renderer/accessibility/blink_ax_enum_conversion.cc
+++ b/content/renderer/accessibility/blink_ax_enum_conversion.cc
@@ -75,9 +75,6 @@
     dst->AddState(ax::mojom::State::kVertical);
   else if (o.Orientation() == blink::kWebAXOrientationHorizontal)
     dst->AddState(ax::mojom::State::kHorizontal);
-
-  if (o.IsVisited())
-    dst->AddState(ax::mojom::State::kVisited);
 }
 
 }  // namespace content.
diff --git a/content/renderer/accessibility/blink_ax_tree_source.cc b/content/renderer/accessibility/blink_ax_tree_source.cc
index 37fc22b..8f43940 100644
--- a/content/renderer/accessibility/blink_ax_tree_source.cc
+++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -161,7 +161,7 @@
 
 WebAXObject ParentObjectUnignored(WebAXObject child) {
   WebAXObject parent = child.ParentObject();
-  while (!parent.IsDetached() && parent.AccessibilityIsIgnored())
+  while (!parent.IsDetached() && !parent.AccessibilityIsIncludedInTree())
     parent = parent.ParentObject();
   return parent;
 }
@@ -483,7 +483,7 @@
     if (node.Equals(root()))
       return WebAXObject();
     node = node.ParentObject();
-  } while (!node.IsDetached() && node.AccessibilityIsIgnored());
+  } while (!node.IsDetached() && !node.AccessibilityIsIncludedInTree());
 
   return node;
 }
diff --git a/content/renderer/loader/web_url_request_util.cc b/content/renderer/loader/web_url_request_util.cc
index 0f7d31d..19037f2b 100644
--- a/content/renderer/loader/web_url_request_util.cc
+++ b/content/renderer/loader/web_url_request_util.cc
@@ -225,17 +225,10 @@
 
 WebHTTPBody GetWebHTTPBodyForRequestBody(
     const network::ResourceRequestBody& input) {
-  return GetWebHTTPBodyForRequestBodyWithBlobPtrs(input, {});
-}
-
-WebHTTPBody GetWebHTTPBodyForRequestBodyWithBlobPtrs(
-    const network::ResourceRequestBody& input,
-    std::vector<blink::mojom::BlobPtrInfo> blob_ptrs) {
   WebHTTPBody http_body;
   http_body.Initialize();
   http_body.SetIdentifier(input.identifier());
   http_body.SetContainsPasswordData(input.contains_sensitive_info());
-  auto blob_ptr_iter = blob_ptrs.begin();
   for (auto& element : *input.elements()) {
     switch (element.type()) {
       case network::mojom::DataElementType::kBytes:
@@ -250,14 +243,7 @@
             element.expected_modification_time().ToDoubleT());
         break;
       case network::mojom::DataElementType::kBlob:
-        if (blob_ptrs.empty()) {
           http_body.AppendBlob(WebString::FromASCII(element.blob_uuid()));
-        } else {
-          DCHECK(blob_ptr_iter != blob_ptrs.end());
-          blink::mojom::BlobPtrInfo& blob = *blob_ptr_iter++;
-          http_body.AppendBlob(WebString::FromASCII(element.blob_uuid()),
-                               element.length(), blob.PassHandle());
-        }
         break;
       case network::mojom::DataElementType::kDataPipe: {
         http_body.AppendDataPipe(
@@ -274,25 +260,6 @@
   return http_body;
 }
 
-std::vector<blink::mojom::BlobPtrInfo> GetBlobPtrsForRequestBody(
-    const network::ResourceRequestBody& input) {
-  std::vector<blink::mojom::BlobPtrInfo> blob_ptrs;
-  blink::mojom::BlobRegistryPtr blob_registry;
-  for (auto& element : *input.elements()) {
-    if (element.type() == network::mojom::DataElementType::kBlob) {
-      blink::mojom::BlobPtrInfo blob_ptr;
-      if (!blob_registry) {
-        blink::Platform::Current()->GetInterfaceProvider()->GetInterface(
-            mojo::MakeRequest(&blob_registry));
-      }
-      blob_registry->GetBlobFromUUID(mojo::MakeRequest(&blob_ptr),
-                                     element.blob_uuid());
-      blob_ptrs.push_back(std::move(blob_ptr));
-    }
-  }
-  return blob_ptrs;
-}
-
 scoped_refptr<network::ResourceRequestBody> GetRequestBodyForWebURLRequest(
     const WebURLRequest& request) {
   scoped_refptr<network::ResourceRequestBody> request_body;
diff --git a/content/renderer/loader/web_url_request_util.h b/content/renderer/loader/web_url_request_util.h
index 57ede1af..ed5f727 100644
--- a/content/renderer/loader/web_url_request_util.h
+++ b/content/renderer/loader/web_url_request_util.h
@@ -38,19 +38,6 @@
 blink::WebHTTPBody GetWebHTTPBodyForRequestBody(
     const network::ResourceRequestBody& input);
 
-// Takes a ResourceRequestBody with additional |blob_ptrs| which corresponds to
-// each Blob entries, and converts into WebHTTPBody.
-// TODO(kinuko): Remove this once Network Service is shipped.
-blink::WebHTTPBody GetWebHTTPBodyForRequestBodyWithBlobPtrs(
-    const network::ResourceRequestBody& input,
-    std::vector<blink::mojom::BlobPtrInfo> blob_ptrs);
-
-// Takes a ResourceRequestBody and gets blob pointers for Blob entries.
-// Used only in non-NetworkService case.
-// TODO(kinuko): Remove this once Network Service is shipped.
-std::vector<blink::mojom::BlobPtrInfo> GetBlobPtrsForRequestBody(
-    const network::ResourceRequestBody& input);
-
 // Takes a WebHTTPBody and converts into a ResourceRequestBody.
 scoped_refptr<network::ResourceRequestBody> GetRequestBodyForWebHTTPBody(
     const blink::WebHTTPBody& httpBody);
diff --git a/content/renderer/media/stream/apply_constraints_processor.cc b/content/renderer/media/stream/apply_constraints_processor.cc
index d3b2e67..0a2ddbc7 100644
--- a/content/renderer/media/stream/apply_constraints_processor.cc
+++ b/content/renderer/media/stream/apply_constraints_processor.cc
@@ -106,7 +106,7 @@
   }
 
   const blink::MediaStreamDevice& device_info = video_source_->device();
-  if (device_info.type == blink::MEDIA_DEVICE_VIDEO_CAPTURE) {
+  if (device_info.type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
     ProcessVideoDeviceRequest();
   } else {
     FinalizeVideoRequest();
diff --git a/content/renderer/media/stream/media_stream_constraints_util_audio.cc b/content/renderer/media/stream/media_stream_constraints_util_audio.cc
index 7906f9c..9b6072fec 100644
--- a/content/renderer/media/stream/media_stream_constraints_util_audio.cc
+++ b/content/renderer/media/stream/media_stream_constraints_util_audio.cc
@@ -1429,26 +1429,32 @@
     blink::MediaStreamAudioSource* source,
     const blink::WebMediaConstraints& constraints) {
   DCHECK(source);
-  if (source->device().type != blink::MEDIA_DEVICE_AUDIO_CAPTURE &&
-      source->device().type != blink::MEDIA_GUM_TAB_AUDIO_CAPTURE &&
-      source->device().type != blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE) {
+  if (source->device().type !=
+          blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE &&
+      source->device().type !=
+          blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE &&
+      source->device().type !=
+          blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE) {
     return AudioCaptureSettings();
   }
 
   std::string media_stream_source = GetMediaStreamSource(constraints);
-  if (source->device().type == blink::MEDIA_DEVICE_AUDIO_CAPTURE &&
+  if (source->device().type ==
+          blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE &&
       !media_stream_source.empty()) {
     return AudioCaptureSettings(
         constraints.Basic().media_stream_source.GetName());
   }
 
-  if (source->device().type == blink::MEDIA_GUM_TAB_AUDIO_CAPTURE &&
+  if (source->device().type ==
+          blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE &&
       !media_stream_source.empty() &&
       media_stream_source != blink::kMediaStreamSourceTab) {
     return AudioCaptureSettings(
         constraints.Basic().media_stream_source.GetName());
   }
-  if (source->device().type == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE &&
+  if (source->device().type ==
+          blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE &&
       !media_stream_source.empty() &&
       media_stream_source != blink::kMediaStreamSourceSystem &&
       media_stream_source != blink::kMediaStreamSourceDesktop) {
diff --git a/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc b/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
index d2ac608..fb82b60 100644
--- a/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
+++ b/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
@@ -94,13 +94,13 @@
   virtual std::string GetMediaStreamSource() { return std::string(); }
   bool IsDeviceCapture() { return GetMediaStreamSource().empty(); }
 
-  blink::MediaStreamType GetMediaStreamType() {
+  blink::mojom::MediaStreamType GetMediaStreamType() {
     std::string media_source = GetMediaStreamSource();
     if (media_source.empty())
-      return blink::MEDIA_DEVICE_AUDIO_CAPTURE;
+      return blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE;
     else if (media_source == blink::kMediaStreamSourceTab)
-      return blink::MEDIA_GUM_TAB_AUDIO_CAPTURE;
-    return blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE;
+      return blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE;
+    return blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE;
   }
 
   std::unique_ptr<ProcessedLocalAudioSource> GetProcessedLocalAudioSource(
diff --git a/content/renderer/media/stream/media_stream_constraints_util_video_content.cc b/content/renderer/media/stream/media_stream_constraints_util_video_content.cc
index 16cb6e4..d7440a4 100644
--- a/content/renderer/media/stream/media_stream_constraints_util_video_content.cc
+++ b/content/renderer/media/stream/media_stream_constraints_util_video_content.cc
@@ -291,7 +291,7 @@
 VideoCaptureSettings SelectResultFromCandidates(
     const VideoContentCaptureCandidates& candidates,
     const blink::WebMediaTrackConstraintSet& basic_constraint_set,
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     int screen_width,
     int screen_height) {
   std::string device_id = SelectDeviceIDFromCandidates(
@@ -331,7 +331,7 @@
 
   // This default comes from the old algorithm.
   media::ResolutionChangePolicy default_resolution_policy =
-      stream_type == blink::MEDIA_GUM_TAB_VIDEO_CAPTURE
+      stream_type == blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE
           ? media::ResolutionChangePolicy::FIXED_RESOLUTION
           : media::ResolutionChangePolicy::ANY_WITHIN_LIMIT;
 
@@ -383,7 +383,7 @@
 
 VideoCaptureSettings SelectSettingsVideoContentCapture(
     const blink::WebMediaConstraints& constraints,
-    blink::MediaStreamType stream_type,
+    blink::mojom::MediaStreamType stream_type,
     int screen_width,
     int screen_height) {
   VideoContentCaptureCandidates candidates;
diff --git a/content/renderer/media/stream/media_stream_constraints_util_video_content.h b/content/renderer/media/stream/media_stream_constraints_util_video_content.h
index 7c8b309..daaecb53 100644
--- a/content/renderer/media/stream/media_stream_constraints_util_video_content.h
+++ b/content/renderer/media/stream/media_stream_constraints_util_video_content.h
@@ -29,7 +29,7 @@
 // for content video capture based on the given |constraints|.
 blink::VideoCaptureSettings CONTENT_EXPORT
 SelectSettingsVideoContentCapture(const blink::WebMediaConstraints& constraints,
-                                  blink::MediaStreamType stream_type,
+                                  blink::mojom::MediaStreamType stream_type,
                                   int screen_width,
                                   int screen_height);
 
diff --git a/content/renderer/media/stream/media_stream_constraints_util_video_content_unittest.cc b/content/renderer/media/stream/media_stream_constraints_util_video_content_unittest.cc
index 8184dbb..0b7e4b8b 100644
--- a/content/renderer/media/stream/media_stream_constraints_util_video_content_unittest.cc
+++ b/content/renderer/media/stream/media_stream_constraints_util_video_content_unittest.cc
@@ -63,8 +63,8 @@
 class MediaStreamConstraintsUtilVideoContentTest : public testing::Test {
  protected:
   blink::VideoCaptureSettings SelectSettings(
-      blink::MediaStreamType stream_type =
-          blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE) {
+      blink::mojom::MediaStreamType stream_type =
+          blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE) {
     blink::WebMediaConstraints constraints =
         constraint_factory_.CreateWebMediaConstraints();
     return SelectSettingsVideoContentCapture(constraints, stream_type,
@@ -2133,7 +2133,8 @@
   }
   {
     constraint_factory_.Reset();
-    auto result = SelectSettings(blink::MEDIA_GUM_TAB_VIDEO_CAPTURE);
+    auto result =
+        SelectSettings(blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE);
     EXPECT_EQ(kDefaultScreenCastWidth, result.Width());
     EXPECT_EQ(kDefaultScreenCastHeight, result.Height());
     // Default policy for tab capture is fixed resolution.
diff --git a/content/renderer/media/stream/media_stream_device_observer.cc b/content/renderer/media/stream/media_stream_device_observer.cc
index 8509de6..d32589d 100644
--- a/content/renderer/media/stream/media_stream_device_observer.cc
+++ b/content/renderer/media/stream/media_stream_device_observer.cc
@@ -57,7 +57,7 @@
   blink::MediaStreamDevices video_devices;
   for (const auto& stream_it : label_stream_map_) {
     for (const auto& video_device : stream_it.second.video_devices) {
-      if (!IsScreenCaptureMediaType(video_device.type))
+      if (!blink::IsScreenCaptureMediaType(video_device.type))
         video_devices.push_back(video_device);
     }
   }
@@ -87,7 +87,7 @@
     return;
   }
   Stream* stream = &it->second;
-  if (IsAudioInputMediaType(device.type))
+  if (blink::IsAudioInputMediaType(device.type))
     RemoveStreamDeviceFromArray(device, &stream->audio_devices);
   else
     RemoveStreamDeviceFromArray(device, &stream->video_devices);
@@ -129,11 +129,11 @@
 
   // Update device list only for device changing. Removing device will be
   // handled in its own callback.
-  if (old_device.type != blink::MEDIA_NO_SERVICE &&
-      new_device.type != blink::MEDIA_NO_SERVICE) {
+  if (old_device.type != blink::mojom::MediaStreamType::NO_SERVICE &&
+      new_device.type != blink::mojom::MediaStreamType::NO_SERVICE) {
     if (RemoveStreamDeviceFromArray(old_device, &stream->audio_devices) ||
         RemoveStreamDeviceFromArray(old_device, &stream->video_devices)) {
-      if (IsAudioInputMediaType(new_device.type))
+      if (blink::IsAudioInputMediaType(new_device.type))
         stream->audio_devices.push_back(new_device);
       else
         stream->video_devices.push_back(new_device);
@@ -167,9 +167,9 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   Stream stream;
-  if (IsAudioInputMediaType(device.type))
+  if (blink::IsAudioInputMediaType(device.type))
     stream.audio_devices.push_back(device);
-  else if (IsVideoInputMediaType(device.type))
+  else if (blink::IsVideoInputMediaType(device.type))
     stream.video_devices.push_back(device);
   else
     NOTREACHED();
diff --git a/content/renderer/media/stream/media_stream_device_observer_unittest.cc b/content/renderer/media/stream/media_stream_device_observer_unittest.cc
index 29788ba..c8052b6 100644
--- a/content/renderer/media/stream/media_stream_device_observer_unittest.cc
+++ b/content/renderer/media/stream/media_stream_device_observer_unittest.cc
@@ -54,7 +54,8 @@
   // OpenDevice request 1
   base::RunLoop run_loop1;
   mock_dispatcher_host_.OpenDevice(
-      kRequestId1, "device_path", blink::MEDIA_DEVICE_VIDEO_CAPTURE,
+      kRequestId1, "device_path",
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
       base::BindOnce(&MediaStreamDeviceObserverTest::OnDeviceOpened,
                      base::Unretained(this), run_loop1.QuitClosure()));
   run_loop1.Run();
@@ -63,7 +64,8 @@
   // OpenDevice request 2
   base::RunLoop run_loop2;
   mock_dispatcher_host_.OpenDevice(
-      kRequestId2, "screen_capture", blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
+      kRequestId2, "screen_capture",
+      blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
       base::BindOnce(&MediaStreamDeviceObserverTest::OnDeviceOpened,
                      base::Unretained(this), run_loop2.QuitClosure()));
   run_loop2.Run();
@@ -71,12 +73,13 @@
 
   EXPECT_EQ(observer_->label_stream_map_.size(), 2u);
 
-  // Only the device with type blink::MEDIA_DEVICE_VIDEO_CAPTURE will be
-  // returned.
+  // Only the device with type
+  // blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE will be returned.
   blink::MediaStreamDevices video_devices =
       observer_->GetNonScreenCaptureDevices();
   EXPECT_EQ(video_devices.size(), 1u);
-  EXPECT_EQ(video_devices[0].type, blink::MEDIA_DEVICE_VIDEO_CAPTURE);
+  EXPECT_EQ(video_devices[0].type,
+            blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
 
   // Close the device from request 2.
   observer_->RemoveStream(stream_label2);
@@ -100,7 +103,8 @@
   // OpenDevice request.
   base::RunLoop run_loop1;
   mock_dispatcher_host_.OpenDevice(
-      kRequestId, "device_path", blink::MEDIA_DEVICE_VIDEO_CAPTURE,
+      kRequestId, "device_path",
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
       base::BindOnce(&MediaStreamDeviceObserverTest::OnDeviceOpened,
                      base::Unretained(this), run_loop1.QuitClosure()));
   run_loop1.Run();
@@ -124,7 +128,8 @@
   // OpenDevice request.
   base::RunLoop run_loop1;
   mock_dispatcher_host_.OpenDevice(
-      kRequestId1, example_video_id1, blink::MEDIA_DEVICE_VIDEO_CAPTURE,
+      kRequestId1, example_video_id1,
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
       base::BindOnce(&MediaStreamDeviceObserverTest::OnDeviceOpened,
                      base::Unretained(this), run_loop1.QuitClosure()));
   run_loop1.Run();
@@ -136,9 +141,9 @@
   EXPECT_EQ(video_devices[0].id, example_video_id1);
 
   // OnDeviceChange request.
-  blink::MediaStreamDevice fake_video_device(blink::MEDIA_DEVICE_VIDEO_CAPTURE,
-                                             example_video_id2,
-                                             "Fake Video Device");
+  blink::MediaStreamDevice fake_video_device(
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, example_video_id2,
+      "Fake Video Device");
   fake_video_device.session_id = kRequestId2;
   observer_->OnDeviceChanged(stream_label_, current_device_, fake_video_device);
 
diff --git a/content/renderer/media/stream/mock_media_stream_registry.cc b/content/renderer/media/stream/mock_media_stream_registry.cc
index 2a694349..4c1ce265 100644
--- a/content/renderer/media/stream/mock_media_stream_registry.cc
+++ b/content/renderer/media/stream/mock_media_stream_registry.cc
@@ -35,8 +35,9 @@
         media::AudioParameters::kAudioCDSampleRate,
         media::AudioParameters::kAudioCDSampleRate / 100));
     SetDevice(blink::MediaStreamDevice(
-        blink::MEDIA_DEVICE_AUDIO_CAPTURE, "mock_audio_device_id",
-        "Mock audio device", media::AudioParameters::kAudioCDSampleRate,
+        blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+        "mock_audio_device_id", "Mock audio device",
+        media::AudioParameters::kAudioCDSampleRate,
         media::CHANNEL_LAYOUT_STEREO,
         media::AudioParameters::kAudioCDSampleRate / 100));
   }
diff --git a/content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.cc b/content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.cc
index 92dea2d7..b385ef8 100644
--- a/content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.cc
+++ b/content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.cc
@@ -88,7 +88,7 @@
 void MockMojoMediaStreamDispatcherHost::OpenDevice(
     int32_t request_id,
     const std::string& device_id,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     OpenDeviceCallback callback) {
   blink::MediaStreamDevice device;
   device.id = device_id;
diff --git a/content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.h b/content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.h
index 11cd538..bee9502 100644
--- a/content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.h
+++ b/content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.h
@@ -33,12 +33,12 @@
                         int32_t session_id) override;
   void OpenDevice(int32_t request_id,
                   const std::string& device_id,
-                  blink::MediaStreamType type,
+                  blink::mojom::MediaStreamType type,
                   OpenDeviceCallback callback) override;
 
   MOCK_METHOD1(CloseDevice, void(const std::string&));
   MOCK_METHOD3(SetCapturingLinkSecured,
-               void(int32_t, blink::MediaStreamType, bool));
+               void(int32_t, blink::mojom::MediaStreamType, bool));
   MOCK_METHOD1(OnStreamStarted, void(const std::string&));
 
   void IncrementSessionId() { ++session_id_; }
diff --git a/content/renderer/media/stream/processed_local_audio_source_unittest.cc b/content/renderer/media/stream/processed_local_audio_source_unittest.cc
index ae54fb4..66c52b6 100644
--- a/content/renderer/media/stream/processed_local_audio_source_unittest.cc
+++ b/content/renderer/media/stream/processed_local_audio_source_unittest.cc
@@ -102,10 +102,10 @@
     std::unique_ptr<ProcessedLocalAudioSource> source =
         std::make_unique<ProcessedLocalAudioSource>(
             -1 /* consumer_render_frame_id is N/A for non-browser tests */,
-            blink::MediaStreamDevice(blink::MEDIA_DEVICE_AUDIO_CAPTURE,
-                                     "mock_audio_device_id",
-                                     "Mock audio device", kSampleRate,
-                                     kChannelLayout, kRequestedBufferSize),
+            blink::MediaStreamDevice(
+                blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                "mock_audio_device_id", "Mock audio device", kSampleRate,
+                kChannelLayout, kRequestedBufferSize),
             false /* disable_local_echo */, properties, base::DoNothing(),
             &mock_dependency_factory_,
             blink::scheduler::GetSingleThreadTaskRunnerForTesting());
diff --git a/content/renderer/media/stream/user_media_client_impl_unittest.cc b/content/renderer/media/stream/user_media_client_impl_unittest.cc
index c2a41bb..5c34abe5 100644
--- a/content/renderer/media/stream/user_media_client_impl_unittest.cc
+++ b/content/renderer/media/stream/user_media_client_impl_unittest.cc
@@ -377,7 +377,7 @@
         bool EnsureSourceIsStarted() override { return false; }
       };
       source = std::make_unique<FailedAtLifeAudioSource>();
-    } else if (IsDesktopCaptureMediaType(device.type)) {
+    } else if (blink::IsDesktopCaptureMediaType(device.type)) {
       local_audio_source_ = new MockLocalMediaStreamAudioSource();
       source = base::WrapUnique(local_audio_source_);
     } else {
@@ -1408,8 +1408,8 @@
   MockMediaStreamVideoCapturerSource* video_source =
       user_media_processor_->last_created_video_source();
   blink::MediaStreamDevice fake_video_device(
-      blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, kFakeVideoInputDeviceId1,
-      "Fake Video Device");
+      blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+      kFakeVideoInputDeviceId1, "Fake Video Device");
   EXPECT_CALL(*video_source, ChangeSourceImpl(_));
   user_media_processor_->OnDeviceChanged(video_source->device(),
                                          fake_video_device);
@@ -1419,8 +1419,8 @@
       user_media_processor_->last_created_local_audio_source();
   EXPECT_NE(audio_source, nullptr);
   blink::MediaStreamDevice fake_audio_device(
-      blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE, kFakeVideoInputDeviceId1,
-      "Fake Audio Device");
+      blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE,
+      kFakeVideoInputDeviceId1, "Fake Audio Device");
   EXPECT_CALL(*audio_source, EnsureSourceIsStopped()).Times(2);
   user_media_processor_->OnDeviceChanged(audio_source->device(),
                                          fake_audio_device);
@@ -1451,7 +1451,8 @@
       user_media_processor_->last_created_local_audio_source();
   EXPECT_NE(audio_source, nullptr);
   EXPECT_CALL(*audio_source, EnsureSourceIsStopped()).Times(1);
-  blink::MediaStreamDevice fake_audio_device(blink::MEDIA_NO_SERVICE, "", "");
+  blink::MediaStreamDevice fake_audio_device(
+      blink::mojom::MediaStreamType::NO_SERVICE, "", "");
   user_media_processor_->OnDeviceChanged(audio_source->device(),
                                          fake_audio_device);
   base::RunLoop().RunUntilIdle();
diff --git a/content/renderer/media/stream/user_media_processor.cc b/content/renderer/media/stream/user_media_processor.cc
index feea0e1..ff20048 100644
--- a/content/renderer/media/stream/user_media_processor.cc
+++ b/content/renderer/media/stream/user_media_processor.cc
@@ -54,11 +54,11 @@
 
 using blink::MediaStreamDevice;
 using blink::MediaStreamDevices;
-using blink::MediaStreamType;
 using blink::StreamControls;
 using blink::TrackControls;
 using blink::WebMediaStreamSource;
 using blink::mojom::MediaStreamRequestResult;
+using blink::mojom::MediaStreamType;
 using EchoCancellationType =
     blink::AudioProcessingProperties::EchoCancellationType;
 
@@ -69,7 +69,7 @@
   if (web_request.MediaRequestType() ==
       blink::WebUserMediaRequest::MediaType::kDisplayMedia) {
     track_controls->requested = true;
-    track_controls->stream_type = blink::MEDIA_DISPLAY_AUDIO_CAPTURE;
+    track_controls->stream_type = MediaStreamType::DISPLAY_AUDIO_CAPTURE;
     return;
   }
 
@@ -81,7 +81,7 @@
   track_controls->requested = true;
 
   MediaStreamType* stream_type = &track_controls->stream_type;
-  *stream_type = blink::MEDIA_NO_SERVICE;
+  *stream_type = MediaStreamType::NO_SERVICE;
 
   std::string source_constraint =
       constraints.Basic().media_stream_source.Exact().empty()
@@ -89,13 +89,13 @@
           : constraints.Basic().media_stream_source.Exact()[0].Utf8();
   if (!source_constraint.empty()) {
     if (source_constraint == blink::kMediaStreamSourceTab) {
-      *stream_type = blink::MEDIA_GUM_TAB_AUDIO_CAPTURE;
+      *stream_type = MediaStreamType::GUM_TAB_AUDIO_CAPTURE;
     } else if (source_constraint == blink::kMediaStreamSourceDesktop ||
                source_constraint == blink::kMediaStreamSourceSystem) {
-      *stream_type = blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE;
+      *stream_type = MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE;
     }
   } else {
-    *stream_type = blink::MEDIA_DEVICE_AUDIO_CAPTURE;
+    *stream_type = MediaStreamType::DEVICE_AUDIO_CAPTURE;
   }
 }
 
@@ -104,7 +104,7 @@
   if (web_request.MediaRequestType() ==
       blink::WebUserMediaRequest::MediaType::kDisplayMedia) {
     track_controls->requested = true;
-    track_controls->stream_type = blink::MEDIA_DISPLAY_VIDEO_CAPTURE;
+    track_controls->stream_type = MediaStreamType::DISPLAY_VIDEO_CAPTURE;
     return;
   }
 
@@ -116,7 +116,7 @@
   track_controls->requested = true;
 
   MediaStreamType* stream_type = &track_controls->stream_type;
-  *stream_type = blink::MEDIA_NO_SERVICE;
+  *stream_type = MediaStreamType::NO_SERVICE;
 
   std::string source_constraint =
       constraints.Basic().media_stream_source.Exact().empty()
@@ -124,13 +124,13 @@
           : constraints.Basic().media_stream_source.Exact()[0].Utf8();
   if (!source_constraint.empty()) {
     if (source_constraint == blink::kMediaStreamSourceTab) {
-      *stream_type = blink::MEDIA_GUM_TAB_VIDEO_CAPTURE;
+      *stream_type = MediaStreamType::GUM_TAB_VIDEO_CAPTURE;
     } else if (source_constraint == blink::kMediaStreamSourceDesktop ||
                source_constraint == blink::kMediaStreamSourceScreen) {
-      *stream_type = blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE;
+      *stream_type = MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE;
     }
   } else {
-    *stream_type = blink::MEDIA_DEVICE_VIDEO_CAPTURE;
+    *stream_type = MediaStreamType::DEVICE_VIDEO_CAPTURE;
   }
 }
 
@@ -496,18 +496,18 @@
   InitializeAudioTrackControls(current_request_info_->web_request(),
                                &audio_controls);
 
-  if (audio_controls.stream_type == blink::MEDIA_DISPLAY_AUDIO_CAPTURE) {
+  if (audio_controls.stream_type == MediaStreamType::DISPLAY_AUDIO_CAPTURE) {
     SelectAudioSettings(current_request_info_->web_request(),
                         {AudioDeviceCaptureCapability()});
     return;
   }
 
-  if (IsDeviceMediaType(audio_controls.stream_type)) {
+  if (blink::IsDeviceMediaType(audio_controls.stream_type)) {
     GetMediaDevicesDispatcher()->GetAudioInputCapabilities(base::BindOnce(
         &UserMediaProcessor::SelectAudioDeviceSettings,
         weak_factory_.GetWeakPtr(), current_request_info_->web_request()));
   } else {
-    if (!IsAudioInputMediaType(audio_controls.stream_type)) {
+    if (!blink::IsAudioInputMediaType(audio_controls.stream_type)) {
       blink::WebString failed_constraint_name =
           blink::WebString::FromASCII(current_request_info_->web_request()
                                           .AudioConstraints()
@@ -539,7 +539,7 @@
     if (it != local_sources_.end()) {
       blink::WebPlatformMediaStreamSource* const source =
           it->GetPlatformSource();
-      if (source->device().type == blink::MEDIA_DEVICE_AUDIO_CAPTURE)
+      if (source->device().type == MediaStreamType::DEVICE_AUDIO_CAPTURE)
         audio_source = static_cast<blink::MediaStreamAudioSource*>(source);
     }
     if (audio_source) {
@@ -578,7 +578,7 @@
     return;
   }
   if (current_request_info_->stream_controls()->audio.stream_type !=
-      blink::MEDIA_DISPLAY_AUDIO_CAPTURE) {
+      MediaStreamType::DISPLAY_AUDIO_CAPTURE) {
     current_request_info_->stream_controls()->audio.device_id =
         settings.device_id();
     current_request_info_->stream_controls()->disable_local_echo =
@@ -586,7 +586,7 @@
   }
   current_request_info_->SetAudioCaptureSettings(
       settings,
-      !IsDeviceMediaType(
+      !blink::IsDeviceMediaType(
           current_request_info_->stream_controls()->audio.stream_type));
 
   // No further audio setup required. Continue with video.
@@ -605,12 +605,12 @@
   auto& video_controls = current_request_info_->stream_controls()->video;
   InitializeVideoTrackControls(current_request_info_->web_request(),
                                &video_controls);
-  if (IsDeviceMediaType(video_controls.stream_type)) {
+  if (blink::IsDeviceMediaType(video_controls.stream_type)) {
     GetMediaDevicesDispatcher()->GetVideoInputCapabilities(base::BindOnce(
         &UserMediaProcessor::SelectVideoDeviceSettings,
         weak_factory_.GetWeakPtr(), current_request_info_->web_request()));
   } else {
-    if (!IsVideoInputMediaType(video_controls.stream_type)) {
+    if (!blink::IsVideoInputMediaType(video_controls.stream_type)) {
       blink::WebString failed_constraint_name =
           blink::WebString::FromASCII(current_request_info_->web_request()
                                           .VideoConstraints()
@@ -637,7 +637,7 @@
     return;
 
   DCHECK(current_request_info_->stream_controls()->video.requested);
-  DCHECK(IsDeviceMediaType(
+  DCHECK(blink::IsDeviceMediaType(
       current_request_info_->stream_controls()->video.stream_type));
 
   blink::VideoDeviceCaptureCapabilities capabilities;
@@ -686,7 +686,7 @@
     return;
   }
   if (current_request_info_->stream_controls()->video.stream_type !=
-      blink::MEDIA_DISPLAY_VIDEO_CAPTURE) {
+      MediaStreamType::DISPLAY_VIDEO_CAPTURE) {
     current_request_info_->stream_controls()->video.device_id =
         settings.device_id();
   }
@@ -918,12 +918,12 @@
     return;
   }
 
-  if (old_device.type != blink::MEDIA_NO_SERVICE &&
-      new_device.type == blink::MEDIA_NO_SERVICE) {
+  if (old_device.type != MediaStreamType::NO_SERVICE &&
+      new_device.type == MediaStreamType::NO_SERVICE) {
     // At present, this will only happen to the case that a new desktop capture
     // source without audio share is selected, then the previous audio capture
     // device should be stopped if existing.
-    DCHECK(IsAudioInputMediaType(old_device.type));
+    DCHECK(blink::IsAudioInputMediaType(old_device.type));
     OnDeviceStopped(old_device);
     return;
   }
@@ -1047,7 +1047,7 @@
   blink::AudioProcessingProperties audio_processing_properties =
       current_request_info_->audio_capture_settings()
           .audio_processing_properties();
-  if (IsScreenCaptureMediaType(device.type) ||
+  if (blink::IsScreenCaptureMediaType(device.type) ||
       !MediaStreamAudioProcessor::WouldModifyAudio(
           audio_processing_properties)) {
     return std::make_unique<LocalMediaStreamAudioSource>(
@@ -1339,7 +1339,7 @@
   }
 
   blink::WebMediaStreamSource::Type type =
-      IsAudioInputMediaType(device.type)
+      blink::IsAudioInputMediaType(device.type)
           ? blink::WebMediaStreamSource::kTypeAudio
           : blink::WebMediaStreamSource::kTypeVideo;
 
diff --git a/content/renderer/media/stream/user_media_processor.h b/content/renderer/media/stream/user_media_processor.h
index 44ba2f4..51ac1b66 100644
--- a/content/renderer/media/stream/user_media_processor.h
+++ b/content/renderer/media/stream/user_media_processor.h
@@ -18,7 +18,6 @@
 #include "content/common/content_export.h"
 #include "content/renderer/media/stream/media_stream_dispatcher_eventhandler.h"
 #include "third_party/blink/public/mojom/mediastream/media_devices.mojom.h"
-#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
 #include "third_party/blink/public/platform/modules/mediastream/web_platform_media_stream_source.h"
 #include "third_party/blink/public/platform/web_vector.h"
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
index 2dead86..899f900 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
@@ -311,8 +311,9 @@
         new ProcessedLocalAudioSource(
             -1 /* consumer_render_frame_id is N/A for non-browser tests */,
             blink::MediaStreamDevice(
-                blink::MEDIA_DEVICE_AUDIO_CAPTURE, "mock_device_id",
-                "Mock device", media::AudioParameters::kAudioCDSampleRate,
+                blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                "mock_device_id", "Mock device",
+                media::AudioParameters::kAudioCDSampleRate,
                 media::CHANNEL_LAYOUT_STEREO,
                 media::AudioParameters::kAudioCDSampleRate / 100),
             false /* disable_local_echo */, blink::AudioProcessingProperties(),
diff --git a/content/renderer/media/webrtc/rtc_video_decoder_adapter_unittest.cc b/content/renderer/media/webrtc/rtc_video_decoder_adapter_unittest.cc
index 799f340..d84e092 100644
--- a/content/renderer/media/webrtc/rtc_video_decoder_adapter_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_video_decoder_adapter_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread.h"
@@ -21,7 +22,6 @@
 #include "content/renderer/media/webrtc/rtc_video_decoder_adapter.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "media/base/decode_status.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/media_util.h"
 #include "media/base/video_decoder.h"
 #include "media/base/video_decoder_config.h"
@@ -162,7 +162,7 @@
   bool CreateAndInitialize(bool init_cb_result = true) {
     EXPECT_CALL(*video_decoder_, Initialize_(_, _, _, _, _, _))
         .WillOnce(DoAll(SaveArg<0>(&vda_config_), SaveArg<4>(&output_cb_),
-                        media::RunOnceCallback<3>(init_cb_result)));
+                        base::test::RunOnceCallback<3>(init_cb_result)));
     rtc_video_decoder_adapter_ =
         RTCVideoDecoderAdapter::Create(&gpu_factories_, sdp_format_);
     return !!rtc_video_decoder_adapter_;
@@ -285,7 +285,7 @@
   ASSERT_TRUE(BasicSetup());
 
   EXPECT_CALL(*video_decoder_, Decode_(_, _))
-      .WillOnce(media::RunOnceCallback<1>(media::DecodeStatus::OK));
+      .WillOnce(base::test::RunOnceCallback<1>(media::DecodeStatus::OK));
 
   ASSERT_EQ(Decode(0), WEBRTC_VIDEO_CODEC_OK);
 
@@ -298,7 +298,8 @@
   ASSERT_TRUE(BasicSetup());
 
   EXPECT_CALL(*video_decoder_, Decode_(_, _))
-      .WillOnce(media::RunOnceCallback<1>(media::DecodeStatus::DECODE_ERROR));
+      .WillOnce(
+          base::test::RunOnceCallback<1>(media::DecodeStatus::DECODE_ERROR));
 
   ASSERT_EQ(Decode(0), WEBRTC_VIDEO_CODEC_OK);
   media_thread_.FlushForTesting();
@@ -353,13 +354,13 @@
   // Decode() is expected to be called for EOS flush as well.
   EXPECT_CALL(*video_decoder_, Decode_(_, _))
       .Times(3)
-      .WillRepeatedly(media::RunOnceCallback<1>(media::DecodeStatus::OK));
+      .WillRepeatedly(base::test::RunOnceCallback<1>(media::DecodeStatus::OK));
   EXPECT_CALL(decoded_cb_, Run(_)).Times(2);
 
   // First Decode() should cause a reinitialize as new color space is given.
   EXPECT_CALL(*video_decoder_, Initialize_(_, _, _, _, _, _))
-      .WillOnce(
-          DoAll(SaveArg<0>(&vda_config_), media::RunOnceCallback<3>(true)));
+      .WillOnce(DoAll(SaveArg<0>(&vda_config_),
+                      base::test::RunOnceCallback<3>(true)));
   webrtc::EncodedImage first_input_image = GetEncodedImageWithColorSpace(0);
   ASSERT_EQ(rtc_video_decoder_adapter_->Decode(first_input_image, false, 0),
             WEBRTC_VIDEO_CODEC_OK);
@@ -387,11 +388,11 @@
 
   // Decode() is expected to be called for EOS flush as well.
   EXPECT_CALL(*video_decoder_, Decode_(_, _))
-      .WillOnce(media::RunOnceCallback<1>(media::DecodeStatus::OK));
+      .WillOnce(base::test::RunOnceCallback<1>(media::DecodeStatus::OK));
 
   // Set Initialize() to fail.
   EXPECT_CALL(*video_decoder_, Initialize_(_, _, _, _, _, _))
-      .WillOnce(media::RunOnceCallback<3>(false));
+      .WillOnce(base::test::RunOnceCallback<3>(false));
   ASSERT_EQ(rtc_video_decoder_adapter_->Decode(input_image, false, 0),
             WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE);
 }
@@ -407,7 +408,7 @@
 
   // Decode() is expected to be called for EOS flush, set to fail.
   EXPECT_CALL(*video_decoder_, Decode_(_, _))
-      .WillOnce(media::RunOnceCallback<1>(media::DecodeStatus::ABORTED));
+      .WillOnce(base::test::RunOnceCallback<1>(media::DecodeStatus::ABORTED));
   ASSERT_EQ(rtc_video_decoder_adapter_->Decode(input_image, false, 0),
             WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE);
 }
diff --git a/content/renderer/media_recorder/DEPS b/content/renderer/media_recorder/DEPS
index 59fe857..6a5e82e 100644
--- a/content/renderer/media_recorder/DEPS
+++ b/content/renderer/media_recorder/DEPS
@@ -2,5 +2,4 @@
   "+third_party/libvpx",
   "+third_party/libyuv",
   '+third_party/openh264/src/codec/api/svc',
-  '+third_party/opus',
 ]
diff --git a/content/renderer/media_recorder/audio_track_recorder.cc b/content/renderer/media_recorder/audio_track_recorder.cc
deleted file mode 100644
index 100d425..0000000
--- a/content/renderer/media_recorder/audio_track_recorder.cc
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/renderer/media_recorder/audio_track_recorder.h"
-
-#include <stdint.h>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "base/stl_util.h"
-#include "content/renderer/media_recorder/audio_track_opus_encoder.h"
-#include "content/renderer/media_recorder/audio_track_pcm_encoder.h"
-#include "media/base/audio_bus.h"
-#include "media/base/audio_parameters.h"
-#include "media/base/bind_to_current_loop.h"
-#include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_track.h"
-
-// Note that this code follows the Chrome media convention of defining a "frame"
-// as "one multi-channel sample" as opposed to another common definition meaning
-// "a chunk of samples". Here this second definition of "frame" is called a
-// "buffer"; so what might be called "frame duration" is instead "buffer
-// duration", and so on.
-
-namespace content {
-
-AudioTrackRecorder::CodecId AudioTrackRecorder::GetPreferredCodecId() {
-  return CodecId::OPUS;
-}
-
-AudioTrackRecorder::AudioTrackRecorder(CodecId codec,
-                                       const blink::WebMediaStreamTrack& track,
-                                       OnEncodedAudioCB on_encoded_audio_cb,
-                                       int32_t bits_per_second)
-    : track_(track),
-      encoder_(CreateAudioEncoder(codec,
-                                  std::move(on_encoded_audio_cb),
-                                  bits_per_second)),
-      encoder_thread_("AudioEncoderThread") {
-  DCHECK(main_render_thread_checker_.CalledOnValidThread());
-  DCHECK(!track_.IsNull());
-  DCHECK(blink::MediaStreamAudioTrack::From(track_));
-
-  // Start the |encoder_thread_|. From this point on, |encoder_| should work
-  // only on |encoder_thread_|, as enforced by DCHECKs.
-  encoder_thread_.Start();
-
-  // Connect the source provider to the track as a sink.
-  blink::WebMediaStreamAudioSink::AddToAudioTrack(this, track_);
-}
-
-AudioTrackRecorder::~AudioTrackRecorder() {
-  DCHECK(main_render_thread_checker_.CalledOnValidThread());
-  blink::WebMediaStreamAudioSink::RemoveFromAudioTrack(this, track_);
-}
-
-// Creates an audio encoder from the codec. Returns nullptr if the codec is
-// invalid.
-AudioTrackEncoder* AudioTrackRecorder::CreateAudioEncoder(
-    CodecId codec,
-    OnEncodedAudioCB on_encoded_audio_cb,
-    int32_t bits_per_second) {
-  if (codec == CodecId::PCM) {
-    return new AudioTrackPcmEncoder(
-        media::BindToCurrentLoop(std::move(on_encoded_audio_cb)));
-  }
-
-  // All other paths will use the AudioTrackOpusEncoder.
-  return new AudioTrackOpusEncoder(
-      media::BindToCurrentLoop(std::move(on_encoded_audio_cb)),
-      bits_per_second);
-}
-
-void AudioTrackRecorder::OnSetFormat(const media::AudioParameters& params) {
-  // If the source is restarted, might have changed to another capture thread.
-  capture_thread_checker_.DetachFromThread();
-  DCHECK(capture_thread_checker_.CalledOnValidThread());
-
-  encoder_thread_.task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&AudioTrackEncoder::OnSetFormat, encoder_, params));
-}
-
-void AudioTrackRecorder::OnData(const media::AudioBus& audio_bus,
-                                base::TimeTicks capture_time) {
-  DCHECK(capture_thread_checker_.CalledOnValidThread());
-  DCHECK(!capture_time.is_null());
-
-  std::unique_ptr<media::AudioBus> audio_data =
-      media::AudioBus::Create(audio_bus.channels(), audio_bus.frames());
-  audio_bus.CopyTo(audio_data.get());
-
-  encoder_thread_.task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(&AudioTrackEncoder::EncodeAudio, encoder_,
-                                std::move(audio_data), capture_time));
-}
-
-void AudioTrackRecorder::Pause() {
-  DCHECK(main_render_thread_checker_.CalledOnValidThread());
-  DCHECK(encoder_);
-  encoder_thread_.task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&AudioTrackEncoder::set_paused, encoder_, true));
-}
-
-void AudioTrackRecorder::Resume() {
-  DCHECK(main_render_thread_checker_.CalledOnValidThread());
-  DCHECK(encoder_);
-  encoder_thread_.task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&AudioTrackEncoder::set_paused, encoder_, false));
-}
-
-}  // namespace content
diff --git a/content/renderer/media_recorder/media_recorder_handler.cc b/content/renderer/media_recorder/media_recorder_handler.cc
index 31d9b8a..97715db 100644
--- a/content/renderer/media_recorder/media_recorder_handler.cc
+++ b/content/renderer/media_recorder/media_recorder_handler.cc
@@ -13,7 +13,6 @@
 #include "base/strings/string_tokenizer.h"
 #include "base/strings/string_util.h"
 #include "base/system/sys_info.h"
-#include "content/renderer/media_recorder/audio_track_recorder.h"
 #include "media/base/audio_bus.h"
 #include "media/base/audio_codecs.h"
 #include "media/base/audio_parameters.h"
@@ -30,6 +29,7 @@
 #include "third_party/blink/public/platform/web_media_recorder_handler_client.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
 #include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/web/modules/mediarecorder/audio_track_recorder.h"
 
 using base::TimeDelta;
 using base::TimeTicks;
@@ -67,13 +67,14 @@
   return media::kUnknownVideoCodec;
 }
 
-media::AudioCodec CodecIdToMediaAudioCodec(AudioTrackRecorder::CodecId id) {
+media::AudioCodec CodecIdToMediaAudioCodec(
+    blink::AudioTrackRecorder::CodecId id) {
   switch (id) {
-    case AudioTrackRecorder::CodecId::PCM:
+    case blink::AudioTrackRecorder::CodecId::PCM:
       return media::kCodecPCM;
-    case AudioTrackRecorder::CodecId::OPUS:
+    case blink::AudioTrackRecorder::CodecId::OPUS:
       return media::kCodecOpus;
-    case AudioTrackRecorder::CodecId::LAST:
+    case blink::AudioTrackRecorder::CodecId::LAST:
       return media::kUnknownAudioCodec;
   }
   NOTREACHED() << "Unsupported audio codec";
@@ -98,16 +99,16 @@
   return VideoTrackRecorder::CodecId::LAST;
 }
 
-AudioTrackRecorder::CodecId AudioStringToCodecId(
+blink::AudioTrackRecorder::CodecId AudioStringToCodecId(
     const blink::WebString& codecs) {
   const std::string& codecs_str = ToLowerASCII(codecs.Utf8());
 
   if (codecs_str.find("opus") != std::string::npos)
-    return AudioTrackRecorder::CodecId::OPUS;
+    return blink::AudioTrackRecorder::CodecId::OPUS;
   if (codecs_str.find("pcm") != std::string::npos)
-    return AudioTrackRecorder::CodecId::PCM;
+    return blink::AudioTrackRecorder::CodecId::PCM;
 
-  return AudioTrackRecorder::CodecId::LAST;
+  return blink::AudioTrackRecorder::CodecId::LAST;
 }
 
 }  // anonymous namespace
@@ -117,7 +118,7 @@
     : video_bits_per_second_(0),
       audio_bits_per_second_(0),
       video_codec_id_(VideoTrackRecorder::CodecId::LAST),
-      audio_codec_id_(AudioTrackRecorder::CodecId::LAST),
+      audio_codec_id_(blink::AudioTrackRecorder::CodecId::LAST),
       recording_(false),
       client_(nullptr),
       task_runner_(std::move(task_runner)),
@@ -211,12 +212,12 @@
       << static_cast<int>(video_codec_id_);
 
   // Do the same for the audio codec(s).
-  const AudioTrackRecorder::CodecId audio_codec_id =
+  const blink::AudioTrackRecorder::CodecId audio_codec_id =
       AudioStringToCodecId(codecs);
-  audio_codec_id_ = (audio_codec_id != AudioTrackRecorder::CodecId::LAST)
+  audio_codec_id_ = (audio_codec_id != blink::AudioTrackRecorder::CodecId::LAST)
                         ? audio_codec_id
-                        : AudioTrackRecorder::GetPreferredCodecId();
-  DVLOG_IF(1, audio_codec_id == AudioTrackRecorder::CodecId::LAST)
+                        : blink::AudioTrackRecorder::GetPreferredCodecId();
+  DVLOG_IF(1, audio_codec_id == blink::AudioTrackRecorder::CodecId::LAST)
       << "Falling back to preferred audio codec id "
       << static_cast<int>(audio_codec_id_);
 
@@ -298,11 +299,11 @@
     if (audio_track.IsNull())
       return false;
 
-    const AudioTrackRecorder::OnEncodedAudioCB on_encoded_audio_cb =
+    const blink::AudioTrackRecorder::OnEncodedAudioCB on_encoded_audio_cb =
         media::BindToCurrentLoop(base::Bind(
             &MediaRecorderHandler::OnEncodedAudio, weak_factory_.GetWeakPtr()));
 
-    audio_recorders_.emplace_back(new AudioTrackRecorder(
+    audio_recorders_.emplace_back(new blink::AudioTrackRecorder(
         audio_codec_id_, audio_track, std::move(on_encoded_audio_cb),
         audio_bits_per_second_));
   }
@@ -443,24 +444,24 @@
         break;
 #endif
       case VideoTrackRecorder::CodecId::LAST:
-        DCHECK_NE(audio_codec_id_, AudioTrackRecorder::CodecId::LAST);
+        DCHECK_NE(audio_codec_id_, blink::AudioTrackRecorder::CodecId::LAST);
     }
   }
   if (has_video_tracks && has_audio_tracks) {
     if (video_codec_id_ != VideoTrackRecorder::CodecId::LAST &&
-        audio_codec_id_ != AudioTrackRecorder::CodecId::LAST) {
+        audio_codec_id_ != blink::AudioTrackRecorder::CodecId::LAST) {
       mime_type.append(",");
     }
   }
   if (has_audio_tracks) {
     switch (audio_codec_id_) {
-      case AudioTrackRecorder::CodecId::OPUS:
+      case blink::AudioTrackRecorder::CodecId::OPUS:
         mime_type.append("opus");
         break;
-      case AudioTrackRecorder::CodecId::PCM:
+      case blink::AudioTrackRecorder::CodecId::PCM:
         mime_type.append("pcm");
         break;
-      case AudioTrackRecorder::CodecId::LAST:
+      case blink::AudioTrackRecorder::CodecId::LAST:
         DCHECK_NE(video_codec_id_, VideoTrackRecorder::CodecId::LAST);
     }
   }
diff --git a/content/renderer/media_recorder/media_recorder_handler.h b/content/renderer/media_recorder/media_recorder_handler.h
index c820c0f..c3428abe 100644
--- a/content/renderer/media_recorder/media_recorder_handler.h
+++ b/content/renderer/media_recorder/media_recorder_handler.h
@@ -14,10 +14,10 @@
 #include "base/strings/string_piece.h"
 #include "base/threading/thread_checker.h"
 #include "content/common/content_export.h"
-#include "content/renderer/media_recorder/audio_track_recorder.h"
 #include "content/renderer/media_recorder/video_track_recorder.h"
 #include "third_party/blink/public/platform/web_media_recorder_handler.h"
 #include "third_party/blink/public/platform/web_media_stream.h"
+#include "third_party/blink/public/web/modules/mediarecorder/audio_track_recorder.h"
 
 namespace blink {
 class WebMediaRecorderHandlerClient;
@@ -33,8 +33,6 @@
 
 namespace content {
 
-class AudioTrackRecorder;
-
 // MediaRecorderHandler orchestrates the creation, lifetime management and
 // mapping between:
 // - MediaStreamTrack(s) providing data,
@@ -104,7 +102,7 @@
   VideoTrackRecorder::CodecId video_codec_id_;
 
   // Audio Codec, OPUS is used by default.
-  AudioTrackRecorder::CodecId audio_codec_id_;
+  blink::AudioTrackRecorder::CodecId audio_codec_id_;
 
   // |client_| has no notion of time, thus may configure us via start(timeslice)
   // to notify it after a certain |timeslice_| has passed. We use a moving
@@ -121,7 +119,7 @@
   blink::WebMediaRecorderHandlerClient* client_;
 
   std::vector<std::unique_ptr<VideoTrackRecorder>> video_recorders_;
-  std::vector<std::unique_ptr<AudioTrackRecorder>> audio_recorders_;
+  std::vector<std::unique_ptr<blink::AudioTrackRecorder>> audio_recorders_;
 
   // Worker class doing the actual Webm Muxing work.
   std::unique_ptr<media::WebmMuxer> webm_muxer_;
diff --git a/content/renderer/pepper/pepper_media_device_manager.cc b/content/renderer/pepper/pepper_media_device_manager.cc
index f882acaa..ec02430 100644
--- a/content/renderer/pepper/pepper_media_device_manager.cc
+++ b/content/renderer/pepper/pepper_media_device_manager.cc
@@ -206,18 +206,18 @@
 }
 
 // static
-blink::MediaStreamType PepperMediaDeviceManager::FromPepperDeviceType(
+blink::mojom::MediaStreamType PepperMediaDeviceManager::FromPepperDeviceType(
     PP_DeviceType_Dev type) {
   switch (type) {
     case PP_DEVICETYPE_DEV_INVALID:
-      return blink::MEDIA_NO_SERVICE;
+      return blink::mojom::MediaStreamType::NO_SERVICE;
     case PP_DEVICETYPE_DEV_AUDIOCAPTURE:
-      return blink::MEDIA_DEVICE_AUDIO_CAPTURE;
+      return blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE;
     case PP_DEVICETYPE_DEV_VIDEOCAPTURE:
-      return blink::MEDIA_DEVICE_VIDEO_CAPTURE;
+      return blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE;
     default:
       NOTREACHED();
-      return blink::MEDIA_NO_SERVICE;
+      return blink::mojom::MediaStreamType::NO_SERVICE;
   }
 }
 
diff --git a/content/renderer/pepper/pepper_media_device_manager.h b/content/renderer/pepper/pepper_media_device_manager.h
index 9449668..8153e7f 100644
--- a/content/renderer/pepper/pepper_media_device_manager.h
+++ b/content/renderer/pepper/pepper_media_device_manager.h
@@ -70,7 +70,8 @@
   int GetSessionID(PP_DeviceType_Dev type, const std::string& label);
 
   // Stream type conversion.
-  static blink::MediaStreamType FromPepperDeviceType(PP_DeviceType_Dev type);
+  static blink::mojom::MediaStreamType FromPepperDeviceType(
+      PP_DeviceType_Dev type);
 
  private:
   explicit PepperMediaDeviceManager(RenderFrame* render_frame);
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index 03ae763..b01ab89 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -286,6 +286,10 @@
 STATIC_ASSERT_MATCHING_ENUM(kTypeZoomOut, PP_MOUSECURSOR_TYPE_ZOOMOUT);
 STATIC_ASSERT_MATCHING_ENUM(kTypeGrab, PP_MOUSECURSOR_TYPE_GRAB);
 STATIC_ASSERT_MATCHING_ENUM(kTypeGrabbing, PP_MOUSECURSOR_TYPE_GRABBING);
+STATIC_ASSERT_MATCHING_ENUM(kTypeMiddlePanningVertical,
+                            PP_MOUSECURSOR_TYPE_MIDDLEPANNINGVERTICAL);
+STATIC_ASSERT_MATCHING_ENUM(kTypeMiddlePanningHorizontal,
+                            PP_MOUSECURSOR_TYPE_MIDDLEPANNINGHORIZONTAL);
 // Do not assert WebCursorInfo::TypeCustom == PP_CURSORTYPE_CUSTOM;
 // PP_CURSORTYPE_CUSTOM is pinned to allow new cursor types.
 
diff --git a/content/shell/test_runner/web_ax_object_proxy.cc b/content/shell/test_runner/web_ax_object_proxy.cc
index 0e03fabc..e9ea1df 100644
--- a/content/shell/test_runner/web_ax_object_proxy.cc
+++ b/content/shell/test_runner/web_ax_object_proxy.cc
@@ -1305,10 +1305,6 @@
       return "false";
     case ax::mojom::InvalidState::kTrue:
       return "true";
-    case ax::mojom::InvalidState::kSpelling:
-      return "spelling";
-    case ax::mojom::InvalidState::kGrammar:
-      return "grammar";
     case ax::mojom::InvalidState::kOther:
       return "other";
     default:
@@ -1657,7 +1653,8 @@
 v8::Local<v8::Object> WebAXObjectProxy::ParentElement() {
   accessibility_object_.UpdateLayoutAndCheckValidity();
   blink::WebAXObject parent_object = accessibility_object_.ParentObject();
-  while (parent_object.AccessibilityIsIgnored())
+  while (!parent_object.IsNull() &&
+         !parent_object.AccessibilityIsIncludedInTree())
     parent_object = parent_object.ParentObject();
   return factory_->GetOrCreate(parent_object);
 }
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 318394f4..a29bcad3 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1609,6 +1609,7 @@
     "../browser/memory/test_memory_monitor.h",
     "../browser/native_file_system/file_system_chooser_unittest.cc",
     "../browser/native_file_system/native_file_system_file_handle_impl_unittest.cc",
+    "../browser/native_file_system/native_file_system_handle_base_unittest.cc",
     "../browser/net/network_quality_observer_impl_unittest.cc",
     "../browser/net/quota_policy_cookie_store_unittest.cc",
     "../browser/network_service_client_unittest.cc",
@@ -1884,7 +1885,6 @@
     "../renderer/media/webrtc/webrtc_set_description_observer_unittest.cc",
     "../renderer/media/webrtc/webrtc_video_track_source_unittest.cc",
     "../renderer/media/webrtc_local_audio_source_provider_unittest.cc",
-    "../renderer/media_recorder/audio_track_recorder_unittest.cc",
     "../renderer/media_recorder/media_recorder_handler_unittest.cc",
     "../renderer/media_recorder/video_track_recorder_unittest.cc",
     "../renderer/p2p/filtering_network_manager_unittest.cc",
@@ -2038,7 +2038,6 @@
     "//third_party/leveldatabase",
     "//third_party/libyuv",
     "//third_party/metrics_proto",
-    "//third_party/opus",
     "//third_party/re2",
     "//third_party/sqlite",
     "//third_party/webrtc/api:libjingle_peerconnection_api",
@@ -2125,7 +2124,6 @@
 
   if (is_chromecast) {
     sources -= [
-      "../renderer/media_recorder/audio_track_recorder_unittest.cc",
       "../renderer/media_recorder/media_recorder_handler_unittest.cc",
       "../renderer/media_recorder/video_track_recorder_unittest.cc",
     ]
@@ -2162,6 +2160,7 @@
       "//third_party/blink/public/common",
       "//third_party/blink/public/common:font_unique_name_table_proto",
       "//third_party/iaccessible2",
+      "//ui/base/idle:test_support",
       "//ui/base/win/system_media_controls:test_support",
     ]
     libs = [
diff --git a/content/test/data/accessibility/aria/aria-invalid-expected-blink.txt b/content/test/data/accessibility/aria/aria-invalid-expected-blink.txt
index bc921ab..7d06cbe 100644
--- a/content/test/data/accessibility/aria/aria-invalid-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-invalid-expected-blink.txt
@@ -5,16 +5,16 @@
 ++textField value='An eror via invalid=spelling'
 ++++staticText name='An '
 ++++++inlineTextBox name='An '
-++++genericContainer invalidState=spelling
-++++++staticText name='eror'
+++++genericContainer ariaInvalidValue='spelling' invalidState=other
+++++++staticText name='eror' markerTypes=1 markerStarts=0 markerEnds=4
 ++++++++inlineTextBox name='eror'
 ++++staticText name=' via invalid=spelling'
 ++++++inlineTextBox name=' via invalid=spelling'
 ++textField value='An errors via invalid=grammar'
 ++++staticText name='An '
 ++++++inlineTextBox name='An '
-++++genericContainer invalidState=grammar
-++++++staticText name='errors'
+++++genericContainer ariaInvalidValue='grammar' invalidState=other
+++++++staticText name='errors' markerTypes=2 markerStarts=0 markerEnds=6
 ++++++++inlineTextBox name='errors'
 ++++staticText name=' via invalid=grammar'
 ++++++inlineTextBox name=' via invalid=grammar'
@@ -34,4 +34,4 @@
 ++++textField value='1234' invalidState=true
 ++++++genericContainer
 ++++++++staticText name='1234'
-++++++++++inlineTextBox name='1234'
+++++++++++inlineTextBox name='1234'
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-invalid-expected-win.txt b/content/test/data/accessibility/aria/aria-invalid-expected-win.txt
index bd6f15bf..ea0b608 100644
--- a/content/test/data/accessibility/aria/aria-invalid-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-invalid-expected-win.txt
@@ -20,4 +20,4 @@
 ++IA2_ROLE_SECTION IA2_STATE_INVALID_ENTRY offset:0 invalid:unknown ia2_hypertext='invalid=unknown'
 ++++ROLE_SYSTEM_STATICTEXT name='invalid=unknown' offset:0 invalid:unknown ia2_hypertext='invalid=unknown'
 ++IA2_ROLE_FORM offset:0 invalid:true ia2_hypertext='<obj0>'
-++++ROLE_SYSTEM_TEXT value='1234' FOCUSABLE IA2_STATE_INVALID_ENTRY offset:0 invalid:true ia2_hypertext='1234'
+++++ROLE_SYSTEM_TEXT value='1234' FOCUSABLE IA2_STATE_INVALID_ENTRY offset:0 invalid:true ia2_hypertext='1234'
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-invalid.html b/content/test/data/accessibility/aria/aria-invalid.html
index dfaf72d..9b438f9 100644
--- a/content/test/data/accessibility/aria/aria-invalid.html
+++ b/content/test/data/accessibility/aria/aria-invalid.html
@@ -2,6 +2,7 @@
 <!--
 @BLINK-ALLOW:ariaInvalidValue*
 @BLINK-ALLOW:invalidState*
+@BLINK-ALLOW:marker*
 @MAC-ALLOW:AXInvalid=*
 @WIN-ALLOW:ia2_hypertext*
 @WIN-ALLOW:offset:*
diff --git a/content/test/data/accessibility/event/menu-opened-closed-expected-uia-win.txt b/content/test/data/accessibility/event/menu-opened-closed-expected-uia-win.txt
index d0a082b..17f4da3 100644
--- a/content/test/data/accessibility/event/menu-opened-closed-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/menu-opened-closed-expected-uia-win.txt
@@ -1,10 +1,9 @@
 MenuOpened on role=menu, name=menu
 === Start Continuation ===
-MenuClosed on role=menu, name=menu
-MenuOpened on role=menu, name=submenu1
+MenuOpened on role=menu
 === Start Continuation ===
-MenuClosed on role=menu, name=submenu1
+MenuClosed 
+MenuClosed on role=menu
 === Start Continuation ===
-MenuOpened on role=menu, name=submenu2
-=== Start Continuation ===
-MenuClosed on role=menu, name=submenu2
+MenuClosed 
+MenuClosed on role=menu, name=menu
\ No newline at end of file
diff --git a/content/test/data/accessibility/event/menu-opened-closed-expected-win.txt b/content/test/data/accessibility/event/menu-opened-closed-expected-win.txt
new file mode 100644
index 0000000..edd46247
--- /dev/null
+++ b/content/test/data/accessibility/event/menu-opened-closed-expected-win.txt
@@ -0,0 +1,7 @@
+EVENT_SYSTEM_MENUPOPUPSTART on <div#menu> role=ROLE_SYSTEM_MENUPOPUP name="menu" IA2_STATE_VERTICAL SetSize=2
+=== Start Continuation ===
+EVENT_SYSTEM_MENUPOPUPSTART on <div#submenu> role=ROLE_SYSTEM_MENUPOPUP IA2_STATE_VERTICAL SetSize=1
+=== Start Continuation ===
+EVENT_SYSTEM_MENUPOPUPEND on <div#submenu> role=ROLE_SYSTEM_MENUPOPUP IA2_STATE_VERTICAL
+=== Start Continuation ===
+EVENT_SYSTEM_MENUPOPUPEND on <div#menu> role=ROLE_SYSTEM_MENUPOPUP name="menu" IA2_STATE_VERTICAL
diff --git a/content/test/data/accessibility/event/menu-opened-closed.html b/content/test/data/accessibility/event/menu-opened-closed.html
index 580128f..8cbb43a 100644
--- a/content/test/data/accessibility/event/menu-opened-closed.html
+++ b/content/test/data/accessibility/event/menu-opened-closed.html
@@ -1,21 +1,27 @@
 <!--
-@WIN-DENY:EVENT_OBJECT_FOCUS*
-@UIA-WIN-DENY:AutomationFocusChanged*
+@WIN-DENY:EVENT_OBJECT_REORDER*
+@WIN-DENY:EVENT_OBJECT_SHOW*
+@WIN-DENY:EVENT_OBJECT_HIDE*
+@WIN-DENY:IA2_EVENT_TEXT_INSERTED*
+@WIN-DENY:IA2_EVENT_TEXT_REMOVED*
+@UIA-WIN-DENY:*StructureChanged*
+@UIA-WIN_DENY:AutomationFocusChanged*
 -->
 <!DOCTYPE html>
-<div tabindex="1" id="menu" aria-label="menu" role="menu">
-  <div tabindex="2" id="submenu1" aria-label="submenu1" role="menu"></div>
-  <div tabindex="3" id="submenu2" aria-label="submenu2" role="menu">
-    <div tabindex="4" id="option" aria-label="option" role="menuitem"></div>
+<div id="menu" aria-label="menu" role="menu" style="display: none">
+  <div tabindex="2" id="menuitem1" aria-label="submenu1" role="menuitem"></div>
+  <div tabindex="3" id="menuitem2" aria-label="submenu2" role="menuitem">
+    <div id="submenu" role="menu" style="display:none">
+      <div tabindex="4" id="menuitem2.1" aria-label="option" role="menuitem"></div>
+    </div>
   </div>
 </div>
 <script>
   const go_passes = [
-    () => document.getElementById('menu').focus(),
-    () => document.getElementById('submenu1').focus(),
-    () => document.getElementById('submenu1').blur(),
-    () => document.getElementById('submenu2').focus(),
-    () => document.getElementById('option').focus(),
+    () => document.getElementById('menu').style.display = 'block',
+    () => document.getElementById('submenu').style.display = 'block',
+    () => document.getElementById('submenu').style.display = 'none',
+    () => document.getElementById('menu').style.display = 'none',
   ];
 
   var current_pass = 0;
diff --git a/content/test/data/overlapping_cross_site_iframe.html b/content/test/data/overlapping_cross_site_iframe.html
new file mode 100644
index 0000000..d5cc19d
--- /dev/null
+++ b/content/test/data/overlapping_cross_site_iframe.html
@@ -0,0 +1,18 @@
+<html>
+<title>Overlapping cross site iframe</title>
+
+This is the main body content.
+<iframe id="iframe" style="width:300px; height:300px; position:absolute; left:100px; top:100px"></iframe>
+<div id="target" style="background-color: yellow; position: absolute; top: 120px; left: 120px; width: 100px; height: 100px"></div>
+
+<script>
+
+  var url = window.location.protocol + '//b.com';
+  if (window.location.port)
+    url += ':' + window.location.port;
+  url += "/cross_site_iframe_factory.html?b()";
+
+  document.getElementById('iframe').src = url;
+</script>
+
+</html>
diff --git a/content/test/fake_network_url_loader_factory.cc b/content/test/fake_network_url_loader_factory.cc
index 9ad8959..d554b7cd 100644
--- a/content/test/fake_network_url_loader_factory.cc
+++ b/content/test/fake_network_url_loader_factory.cc
@@ -10,10 +10,80 @@
 
 namespace content {
 
+namespace {
+
+const char kDefaultHeader[] = "HTTP/1.1 200 OK\n\n";
+const char kDefaultBody[] = "this body came from the network";
+const char kDefaultHeaderForJS[] =
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: application/javascript\n\n";
+const char kDefaultBodyForJS[] = "/*this body came from the network*/";
+
+}  // namespace
+
+FakeNetworkURLLoaderFactory::ResponseInfo::ResponseInfo(
+    const std::string& headers,
+    const std::string& body,
+    bool network_accessed,
+    net::Error error_code)
+    : headers(headers),
+      body(body),
+      network_accessed(network_accessed),
+      error_code(error_code) {}
+
+FakeNetworkURLLoaderFactory::ResponseInfo::ResponseInfo() = default;
+FakeNetworkURLLoaderFactory::ResponseInfo::ResponseInfo(
+    FakeNetworkURLLoaderFactory::ResponseInfo&& info) = default;
+FakeNetworkURLLoaderFactory::ResponseInfo::~ResponseInfo() = default;
+FakeNetworkURLLoaderFactory::ResponseInfo&
+FakeNetworkURLLoaderFactory::ResponseInfo::operator=(
+    FakeNetworkURLLoaderFactory::ResponseInfo&& info) = default;
+
 FakeNetworkURLLoaderFactory::FakeNetworkURLLoaderFactory() = default;
 
+FakeNetworkURLLoaderFactory::FakeNetworkURLLoaderFactory(
+    const std::string& headers,
+    const std::string& body,
+    bool network_accessed,
+    net::Error error_code)
+    : user_defined_default_response_info_(
+          std::make_unique<FakeNetworkURLLoaderFactory::ResponseInfo>(
+              headers,
+              body,
+              network_accessed,
+              error_code)) {}
+
 FakeNetworkURLLoaderFactory::~FakeNetworkURLLoaderFactory() = default;
 
+void FakeNetworkURLLoaderFactory::SetResponse(const GURL& url,
+                                              const std::string& headers,
+                                              const std::string& body,
+                                              bool network_accessed,
+                                              net::Error error_code) {
+  response_info_map_[url] =
+      ResponseInfo(headers, body, network_accessed, error_code);
+}
+
+const FakeNetworkURLLoaderFactory::ResponseInfo&
+FakeNetworkURLLoaderFactory::FindResponseInfo(const GURL& url) const {
+  auto it = response_info_map_.find(url);
+  if (it != response_info_map_.end())
+    return it->second;
+
+  if (user_defined_default_response_info_)
+    return *user_defined_default_response_info_;
+
+  static const base::NoDestructor<ResponseInfo> kDefaultResponseInfo(
+      kDefaultHeader, kDefaultBody, /*network_accessed=*/true, net::OK);
+  static const base::NoDestructor<ResponseInfo> kDefaultJsResponseInfo(
+      kDefaultHeaderForJS, kDefaultBodyForJS, /*network_accessed=*/true,
+      net::OK);
+  bool is_js =
+      base::EndsWith(url.path(), ".js", base::CompareCase::INSENSITIVE_ASCII);
+
+  return is_js ? *kDefaultJsResponseInfo : *kDefaultResponseInfo;
+}
+
 void FakeNetworkURLLoaderFactory::CreateLoaderAndStart(
     network::mojom::URLLoaderRequest request,
     int32_t routing_id,
@@ -22,34 +92,28 @@
     const network::ResourceRequest& url_request,
     network::mojom::URLLoaderClientPtr client,
     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
-  bool is_js = base::EndsWith(url_request.url.path(), ".js",
-                              base::CompareCase::INSENSITIVE_ASCII);
-  std::string headers = "HTTP/1.1 200 OK\n";
-  if (is_js)
-    headers += "Content-Type: application/javascript\n";
-  headers += "\n";
+  const ResponseInfo& response_info = FindResponseInfo(url_request.url);
+
   net::HttpResponseInfo info;
   info.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
-      net::HttpUtil::AssembleRawHeaders(headers));
+      net::HttpUtil::AssembleRawHeaders(response_info.headers));
   network::ResourceResponseHead response;
   response.headers = info.headers;
   response.headers->GetMimeType(&response.mime_type);
+  response.network_accessed = response_info.network_accessed;
   client->OnReceiveResponse(response);
 
-  std::string body = "this body came from the network";
-  if (is_js)
-    body = "/*" + body + "*/";
-  uint32_t bytes_written = body.size();
+  uint32_t bytes_written = response_info.body.size();
   mojo::ScopedDataPipeProducerHandle producer_handle;
   mojo::ScopedDataPipeConsumerHandle consumer_handle;
   CHECK_EQ(MOJO_RESULT_OK,
            mojo::CreateDataPipe(nullptr, &producer_handle, &consumer_handle));
-  producer_handle->WriteData(body.data(), &bytes_written,
+  producer_handle->WriteData(response_info.body.data(), &bytes_written,
                              MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
   client->OnStartLoadingResponseBody(std::move(consumer_handle));
 
   network::URLLoaderCompletionStatus status;
-  status.error_code = net::OK;
+  status.error_code = response_info.error_code;
   client->OnComplete(status);
 }
 
diff --git a/content/test/fake_network_url_loader_factory.h b/content/test/fake_network_url_loader_factory.h
index 0200bafd..dd87bd6f8 100644
--- a/content/test/fake_network_url_loader_factory.h
+++ b/content/test/fake_network_url_loader_factory.h
@@ -10,14 +10,29 @@
 
 namespace content {
 
-// A URLLoaderFactory that returns 200 OK with a simple body to any request.
-// Any request path that ends in ".js" gets a body of
-// "/*this body came from the network*/". Other requests get
-// "this body came from the network".
+// A configurable URLLoaderFactory:
+// 1. By default, it returns 200 OK with a simple body to any request:
+//    If request path ends in ".js", body is:
+//    "/*this body came from the network*/". Otherwise, body is:
+//    "this body came from the network".
+// 2. The default response can be overridden through the non-default
+//    constructor.
+// 3. Call SetResponse() to set specific response for a url.
 class FakeNetworkURLLoaderFactory final
     : public network::mojom::URLLoaderFactory {
  public:
+  // If this constructor is used:
+  // A default response is used for any url request that is not configured
+  // through SetResponse().
   FakeNetworkURLLoaderFactory();
+
+  // If this constructor is used:
+  // The provided response is used for any url request that is not configured
+  // through SetResponse().
+  FakeNetworkURLLoaderFactory(const std::string& headers,
+                              const std::string& body,
+                              bool network_accessed,
+                              net::Error error_code);
   ~FakeNetworkURLLoaderFactory() override;
 
   // network::mojom::URLLoaderFactory implementation.
@@ -32,7 +47,46 @@
 
   void Clone(network::mojom::URLLoaderFactoryRequest request) override;
 
+  // Sets the response for a specific url. CreateLoaderAndStart() uses this
+  // response instead of the default.
+  void SetResponse(const GURL& url,
+                   const std::string& headers,
+                   const std::string& body,
+                   bool network_accessed,
+                   net::Error error_code);
+
  private:
+  class ResponseInfo {
+   public:
+    ResponseInfo();
+    ResponseInfo(ResponseInfo&& info);
+    ResponseInfo(const std::string& headers,
+                 const std::string& body,
+                 bool network_accessed,
+                 net::Error error_code);
+    ~ResponseInfo();
+    ResponseInfo& operator=(ResponseInfo&& info);
+
+    GURL url;
+    std::string headers;
+    std::string body;
+    bool network_accessed = true;
+    net::Error error_code = net::OK;
+  };
+
+  // Returns the ResponseInfo for the |url|, it follows the order:
+  // 1. Returns the matching entry in |response_info_map_| if exists.
+  // 2. Returns |user_defined_default_response_info_| if it's set.
+  // 3. Returns default response info (defined inside this method).
+  const ResponseInfo& FindResponseInfo(const GURL& url) const;
+
+  // This is user-defined default response info, it overrides the default
+  // response info.
+  std::unique_ptr<ResponseInfo> user_defined_default_response_info_;
+
+  // User-defined URL => ResponseInfo map.
+  base::flat_map<GURL, ResponseInfo> response_info_map_;
+
   mojo::BindingSet<network::mojom::URLLoaderFactory> bindings_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeNetworkURLLoaderFactory);
diff --git a/device/fido/pin.cc b/device/fido/pin.cc
index 36834a3..ff212fc 100644
--- a/device/fido/pin.cc
+++ b/device/fido/pin.cc
@@ -351,7 +351,7 @@
 }
 
 std::vector<uint8_t> TokenResponse::PinAuth(
-    const std::array<uint8_t, 32> client_data_hash) {
+    base::span<const uint8_t> client_data_hash) const {
   return MakePinAuth(token_, client_data_hash);
 }
 
diff --git a/device/fido/pin.h b/device/fido/pin.h
index f71cb2c9..e5e849a0 100644
--- a/device/fido/pin.h
+++ b/device/fido/pin.h
@@ -163,7 +163,8 @@
 
   // PinAuth returns a pinAuth parameter for a request that will use the given
   // client-data hash.
-  std::vector<uint8_t> PinAuth(const std::array<uint8_t, 32> client_data_hash);
+  std::vector<uint8_t> PinAuth(
+      base::span<const uint8_t> client_data_hash) const;
 
   const std::vector<uint8_t>& token() { return token_; }
 
diff --git a/device/gamepad/game_controller_data_fetcher_mac.mm b/device/gamepad/game_controller_data_fetcher_mac.mm
index e53f0e0..efa3be6 100644
--- a/device/gamepad/game_controller_data_fetcher_mac.mm
+++ b/device/gamepad/game_controller_data_fetcher_mac.mm
@@ -92,9 +92,7 @@
                                      vendorName ? vendorName : @"Unknown"];
       CopyNSStringAsUTF16LittleEndian(ident, pad.id, sizeof(pad.id));
 
-      CopyNSStringAsUTF16LittleEndian(@"standard", pad.mapping,
-                                      sizeof(pad.mapping));
-
+      pad.mapping = GamepadMapping::kStandard;
       pad.axes_length = AXIS_INDEX_COUNT;
       pad.buttons_length = BUTTON_INDEX_COUNT - 1;
       pad.connected = true;
diff --git a/device/gamepad/gamepad_data_fetcher.cc b/device/gamepad/gamepad_data_fetcher.cc
index 0daa652..b36f4cc 100644
--- a/device/gamepad/gamepad_data_fetcher.cc
+++ b/device/gamepad/gamepad_data_fetcher.cc
@@ -74,18 +74,10 @@
   memset(pad.id, 0, sizeof(pad.id));
   tmp16.copy(pad.id, base::size(pad.id) - 1);
 
-  // Set the mapper string to "standard" if the gamepad has a standard mapping,
-  // or the empty string otherwise.
-  if (has_standard_mapping) {
-    std::string mapping = "standard";
-    base::TruncateUTF8ToByteSize(mapping, Gamepad::kMappingLengthCap - 1,
-                                 &mapping);
-    tmp16 = base::UTF8ToUTF16(mapping);
-    memset(pad.mapping, 0, sizeof(pad.mapping));
-    tmp16.copy(pad.mapping, base::size(pad.mapping) - 1);
-  } else {
-    pad.mapping[0] = 0;
-  }
+  // Set GamepadMapping::kStandard if the gamepad has a standard mapping, or
+  // GamepadMapping::kNone otherwise.
+  pad.mapping =
+      has_standard_mapping ? GamepadMapping::kStandard : GamepadMapping::kNone;
 }
 
 // static
diff --git a/device/gamepad/gamepad_platform_data_fetcher_android.cc b/device/gamepad/gamepad_platform_data_fetcher_android.cc
index 355292a..6865f55f 100644
--- a/device/gamepad/gamepad_platform_data_fetcher_android.cc
+++ b/device/gamepad/gamepad_platform_data_fetcher_android.cc
@@ -102,12 +102,7 @@
            name_to_copy * sizeof(base::string16::value_type));
     pad.id[name_to_copy] = 0;
 
-    base::string16 mapping_name = base::UTF8ToUTF16(mapping ? "standard" : "");
-    const size_t mapping_to_copy =
-        std::min(mapping_name.size(), Gamepad::kMappingLengthCap - 1);
-    memcpy(pad.mapping, mapping_name.data(),
-           mapping_to_copy * sizeof(base::string16::value_type));
-    pad.mapping[mapping_to_copy] = 0;
+    pad.mapping = mapping ? GamepadMapping::kStandard : GamepadMapping::kNone;
   }
 
   pad.connected = true;
diff --git a/device/gamepad/gamepad_platform_data_fetcher_mac.mm b/device/gamepad/gamepad_platform_data_fetcher_mac.mm
index e2332c3..23679309 100644
--- a/device/gamepad/gamepad_platform_data_fetcher_mac.mm
+++ b/device/gamepad/gamepad_platform_data_fetcher_mac.mm
@@ -230,12 +230,8 @@
   CopyNSStringAsUTF16LittleEndian(ident, state->data.id,
                                   sizeof(state->data.id));
 
-  if (state->mapper) {
-    CopyNSStringAsUTF16LittleEndian(@"standard", state->data.mapping,
-                                    sizeof(state->data.mapping));
-  } else {
-    state->data.mapping[0] = 0;
-  }
+  state->data.mapping =
+      state->mapper ? GamepadMapping::kStandard : GamepadMapping::kNone;
 
   devices_[slot] = std::make_unique<GamepadDeviceMac>(location_int, device,
                                                       vendor_int, product_int);
diff --git a/device/gamepad/gamepad_platform_data_fetcher_win.cc b/device/gamepad/gamepad_platform_data_fetcher_win.cc
index b4dca19..4da09e9f 100644
--- a/device/gamepad/gamepad_platform_data_fetcher_win.cc
+++ b/device/gamepad/gamepad_platform_data_fetcher_win.cc
@@ -137,8 +137,7 @@
         swprintf(base::as_writable_wcstr(pad.id), Gamepad::kIdLengthCap,
                  L"Xbox 360 Controller (XInput STANDARD %ls)",
                  GamepadSubTypeName(caps.SubType));
-        swprintf(base::as_writable_wcstr(pad.mapping),
-                 Gamepad::kMappingLengthCap, L"standard");
+        pad.mapping = GamepadMapping::kStandard;
       }
     }
   }
diff --git a/device/gamepad/public/cpp/gamepad.cc b/device/gamepad/public/cpp/gamepad.cc
index 02050b5..f8b22682 100644
--- a/device/gamepad/public/cpp/gamepad.cc
+++ b/device/gamepad/public/cpp/gamepad.cc
@@ -9,7 +9,6 @@
 const float GamepadButton::kDefaultButtonPressedThreshold;
 const double GamepadHapticActuator::kMaxEffectDurationMillis;
 const size_t Gamepad::kIdLengthCap;
-const size_t Gamepad::kMappingLengthCap;
 const size_t Gamepad::kAxesLengthCap;
 const size_t Gamepad::kButtonsLengthCap;
 
@@ -18,9 +17,9 @@
       timestamp(0),
       axes_length(0),
       buttons_length(0),
+      mapping(GamepadMapping::kNone),
       display_id(0) {
   id[0] = 0;
-  mapping[0] = 0;
 }
 
 Gamepad::Gamepad(const Gamepad& other) = default;
diff --git a/device/gamepad/public/cpp/gamepad.h b/device/gamepad/public/cpp/gamepad.h
index 234928c..c63cc5e2 100644
--- a/device/gamepad/public/cpp/gamepad.h
+++ b/device/gamepad/public/cpp/gamepad.h
@@ -91,6 +91,8 @@
   GamepadVector linear_acceleration;
 };
 
+enum class GamepadMapping { kNone = 0, kStandard = 1, kXrStandard = 2 };
+
 enum class GamepadHand { kNone = 0, kLeft = 1, kRight = 2 };
 
 // This structure is intentionally POD and fixed size so that it can be shared
@@ -99,7 +101,6 @@
 class COMPONENT_EXPORT(GAMEPAD_PUBLIC) Gamepad {
  public:
   static constexpr size_t kIdLengthCap = 128;
-  static constexpr size_t kMappingLengthCap = 16;
   static constexpr size_t kAxesLengthCap = 16;
   static constexpr size_t kButtonsLengthCap = 32;
 
@@ -130,8 +131,8 @@
 
   GamepadHapticActuator vibration_actuator;
 
-  // Mapping type (for example "standard")
-  base::char16 mapping[kMappingLengthCap];
+  // Mapping type
+  GamepadMapping mapping;
 
   GamepadPose pose;
 
diff --git a/device/gamepad/public/cpp/gamepad.typemap b/device/gamepad/public/cpp/gamepad.typemap
index c6db1b9b..3587984 100644
--- a/device/gamepad/public/cpp/gamepad.typemap
+++ b/device/gamepad/public/cpp/gamepad.typemap
@@ -24,6 +24,7 @@
   "device.mojom.GamepadHand=device::GamepadHand",
   "device.mojom.GamepadHapticActuator=device::GamepadHapticActuator[nullable_is_same_type]",
   "device.mojom.GamepadHapticActuatorType=device::GamepadHapticActuatorType",
+  "device.mojom.GamepadMapping=device::GamepadMapping",
   "device.mojom.GamepadPose=device::GamepadPose[nullable_is_same_type]",
   "device.mojom.GamepadQuaternion=device::GamepadQuaternion[nullable_is_same_type]",
   "device.mojom.GamepadVector=device::GamepadVector[nullable_is_same_type]",
diff --git a/device/gamepad/public/cpp/gamepad_mojom_traits.cc b/device/gamepad/public/cpp/gamepad_mojom_traits.cc
index 0431aae..c6e01ca 100644
--- a/device/gamepad/public/cpp/gamepad_mojom_traits.cc
+++ b/device/gamepad/public/cpp/gamepad_mojom_traits.cc
@@ -153,6 +153,43 @@
 }
 
 // static
+device::mojom::GamepadMapping
+EnumTraits<device::mojom::GamepadMapping, device::GamepadMapping>::ToMojom(
+    device::GamepadMapping input) {
+  switch (input) {
+    case device::GamepadMapping::kNone:
+      return device::mojom::GamepadMapping::GamepadMappingNone;
+    case device::GamepadMapping::kStandard:
+      return device::mojom::GamepadMapping::GamepadMappingStandard;
+    case device::GamepadMapping::kXrStandard:
+      return device::mojom::GamepadMapping::GamepadMappingXRStandard;
+  }
+
+  NOTREACHED();
+  return device::mojom::GamepadMapping::GamepadMappingNone;
+}
+
+// static
+bool EnumTraits<device::mojom::GamepadMapping, device::GamepadMapping>::
+    FromMojom(device::mojom::GamepadMapping input,
+              device::GamepadMapping* output) {
+  switch (input) {
+    case device::mojom::GamepadMapping::GamepadMappingNone:
+      *output = device::GamepadMapping::kNone;
+      return true;
+    case device::mojom::GamepadMapping::GamepadMappingStandard:
+      *output = device::GamepadMapping::kStandard;
+      return true;
+    case device::mojom::GamepadMapping::GamepadMappingXRStandard:
+      *output = device::GamepadMapping::kXrStandard;
+      return true;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
+// static
 device::mojom::GamepadHand
 EnumTraits<device::mojom::GamepadHand, device::GamepadHand>::ToMojom(
     device::GamepadHand input) {
@@ -201,19 +238,6 @@
 }
 
 // static
-base::span<const uint16_t>
-StructTraits<device::mojom::GamepadDataView, device::Gamepad>::mapping(
-    const device::Gamepad& r) {
-  size_t mapping_length = 0;
-  while (mapping_length < device::Gamepad::kMappingLengthCap &&
-         r.mapping[mapping_length] != 0) {
-    mapping_length++;
-  }
-  return base::make_span(reinterpret_cast<const uint16_t*>(r.mapping),
-                         mapping_length);
-}
-
-// static
 bool StructTraits<device::mojom::GamepadDataView, device::Gamepad>::Read(
     device::mojom::GamepadDataView data,
     device::Gamepad* out) {
@@ -245,10 +269,7 @@
   if (!data.ReadVibrationActuator(&out->vibration_actuator))
     return false;
 
-  memset(out->mapping, 0, sizeof(out->mapping));
-  base::span<uint16_t> mapping(reinterpret_cast<uint16_t*>(out->mapping),
-                               device::Gamepad::kMappingLengthCap);
-  if (!data.ReadMapping(&mapping)) {
+  if (!data.ReadMapping(&out->mapping)) {
     return false;
   }
 
diff --git a/device/gamepad/public/cpp/gamepad_mojom_traits.h b/device/gamepad/public/cpp/gamepad_mojom_traits.h
index 3092b1c..434e24d 100644
--- a/device/gamepad/public/cpp/gamepad_mojom_traits.h
+++ b/device/gamepad/public/cpp/gamepad_mojom_traits.h
@@ -112,6 +112,14 @@
 
 template <>
 struct COMPONENT_EXPORT(GAMEPAD_SHARED_TRAITS)
+    EnumTraits<device::mojom::GamepadMapping, device::GamepadMapping> {
+  static device::mojom::GamepadMapping ToMojom(device::GamepadMapping input);
+  static bool FromMojom(device::mojom::GamepadMapping input,
+                        device::GamepadMapping* output);
+};
+
+template <>
+struct COMPONENT_EXPORT(GAMEPAD_SHARED_TRAITS)
     EnumTraits<device::mojom::GamepadHand, device::GamepadHand> {
   static device::mojom::GamepadHand ToMojom(device::GamepadHand input);
   static bool FromMojom(device::mojom::GamepadHand input,
@@ -143,7 +151,9 @@
   static uint32_t display_id(const device::Gamepad& r) { return r.display_id; }
 
   static base::span<const uint16_t> id(const device::Gamepad& r);
-  static base::span<const uint16_t> mapping(const device::Gamepad& r);
+  static const device::GamepadMapping& mapping(const device::Gamepad& r) {
+    return r.mapping;
+  }
   static bool Read(device::mojom::GamepadDataView data, device::Gamepad* out);
 };
 
diff --git a/device/gamepad/public/cpp/gamepad_mojom_traits_unittest.cc b/device/gamepad/public/cpp/gamepad_mojom_traits_unittest.cc
index c477addb..366b3967 100644
--- a/device/gamepad/public/cpp/gamepad_mojom_traits_unittest.cc
+++ b/device/gamepad/public/cpp/gamepad_mojom_traits_unittest.cc
@@ -54,17 +54,19 @@
     wgp.angular_acceleration = wgv;
   }
 
-  base::char16 wch[Gamepad::kMappingLengthCap] = {
-      1,    8,    9,     127,   128,   1024,  1025,  1949,
-      2047, 2048, 16383, 16384, 20000, 32767, 32768, 65535};
+  constexpr base::char16 kTestIdString[] = {L'M', L'o', L'c', L'k', L'S',
+                                            L't', L'i', L'c', L'k', L' ',
+                                            L'3', L'0', L'0', L'0', L'\0'};
+  constexpr size_t kTestIdStringLength = base::size(kTestIdString);
 
   Gamepad send;
   memset(&send, 0, sizeof(Gamepad));
 
   send.connected = true;
-  for (size_t i = 0; i < Gamepad::kMappingLengthCap; i++) {
-    send.id[i] = send.mapping[i] = wch[i];
+  for (size_t i = 0; i < kTestIdStringLength; i++) {
+    send.id[i] = kTestIdString[i];
   }
+  send.mapping = GamepadMapping::kNone;
   send.timestamp = base::TimeTicks::Now().since_origin().InMicroseconds();
   send.axes_length = 0U;
   for (size_t i = 0; i < Gamepad::kAxesLengthCap; i++) {
@@ -134,7 +136,7 @@
       send.axes_length != echo.axes_length ||
       send.buttons_length != echo.buttons_length ||
       !isWebGamepadPoseEqual(send.pose, echo.pose) || send.hand != echo.hand ||
-      send.display_id != echo.display_id) {
+      send.display_id != echo.display_id || send.mapping != echo.mapping) {
     return false;
   }
   for (size_t i = 0; i < Gamepad::kIdLengthCap; i++) {
@@ -152,11 +154,6 @@
       return false;
     }
   }
-  for (size_t i = 0; i < Gamepad::kMappingLengthCap; i++) {
-    if (send.mapping[i] != echo.mapping[i]) {
-      return false;
-    }
-  }
   return true;
 }
 }  // namespace
diff --git a/device/gamepad/public/mojom/gamepad.mojom b/device/gamepad/public/mojom/gamepad.mojom
index 1d51cdac..70926d26 100644
--- a/device/gamepad/public/mojom/gamepad.mojom
+++ b/device/gamepad/public/mojom/gamepad.mojom
@@ -25,6 +25,12 @@
   double value;
 };
 
+enum GamepadMapping {
+  GamepadMappingNone = 0,
+  GamepadMappingStandard = 1,
+  GamepadMappingXRStandard = 2
+};
+
 struct GamepadPose {
   GamepadQuaternion? orientation;
   GamepadVector? position;
@@ -60,7 +66,7 @@
   array<double> axes;
   array<GamepadButton> buttons;
   GamepadHapticActuator? vibration_actuator;
-  array<uint16> mapping;
+  GamepadMapping mapping;
   GamepadPose? pose;
   GamepadHand hand;
   uint32 display_id;
diff --git a/device/gamepad/raw_input_data_fetcher_win.cc b/device/gamepad/raw_input_data_fetcher_win.cc
index af8b711..2e052a0 100644
--- a/device/gamepad/raw_input_data_fetcher_win.cc
+++ b/device/gamepad/raw_input_data_fetcher_win.cc
@@ -220,11 +220,9 @@
                  state->mapper ? L"STANDARD GAMEPAD " : L"", vendor_int,
                  product_int);
 
-        if (state->mapper)
-          swprintf(base::as_writable_wcstr(pad.mapping),
-                   Gamepad::kMappingLengthCap, L"standard");
-        else
-          pad.mapping[0] = 0;
+        // The mapping is standard if there is a standard mapping function.
+        pad.mapping =
+            state->mapper ? GamepadMapping::kStandard : GamepadMapping::kNone;
       }
 
       enumerated_device_handles.insert(device_handle);
diff --git a/device/gamepad/xbox_data_fetcher_mac.cc b/device/gamepad/xbox_data_fetcher_mac.cc
index 3f2d072d..b8ffc025 100644
--- a/device/gamepad/xbox_data_fetcher_mac.cc
+++ b/device/gamepad/xbox_data_fetcher_mac.cc
@@ -322,9 +322,8 @@
 
   CopyToUString(state->data.id, base::size(state->data.id),
                 base::UTF8ToUTF16(controller->GetIdString()));
-  CopyToUString(state->data.mapping, base::size(state->data.mapping),
-                base::UTF8ToUTF16("standard"));
 
+  state->data.mapping = GamepadMapping::kStandard;
   state->data.connected = true;
   state->data.axes_length = 4;
   state->data.buttons_length = 17;
diff --git a/device/vr/android/gvr/cardboard_gamepad_data_fetcher.cc b/device/vr/android/gvr/cardboard_gamepad_data_fetcher.cc
index e52e936..a2c2231 100644
--- a/device/vr/android/gvr/cardboard_gamepad_data_fetcher.cc
+++ b/device/vr/android/gvr/cardboard_gamepad_data_fetcher.cc
@@ -85,8 +85,7 @@
     pad.is_xr = true;
     CopyToUString(pad.id, Gamepad::kIdLengthCap,
                   base::UTF8ToUTF16("Cardboard Button"));
-    CopyToUString(pad.mapping, Gamepad::kMappingLengthCap,
-                  base::UTF8ToUTF16(""));
+    pad.mapping = GamepadMapping::kNone;
     pad.buttons_length = 1;
     pad.axes_length = 0;
 
diff --git a/device/vr/android/gvr/gvr_gamepad_data_fetcher.cc b/device/vr/android/gvr/gvr_gamepad_data_fetcher.cc
index 93b8b444..928ed82 100644
--- a/device/vr/android/gvr/gvr_gamepad_data_fetcher.cc
+++ b/device/vr/android/gvr/gvr_gamepad_data_fetcher.cc
@@ -85,8 +85,7 @@
     // initialization
     CopyToUString(pad.id, Gamepad::kIdLengthCap,
                   base::UTF8ToUTF16("Daydream Controller"));
-    CopyToUString(pad.mapping, Gamepad::kMappingLengthCap,
-                  base::UTF8ToUTF16(""));
+    pad.mapping = GamepadMapping::kNone;
     pad.buttons_length = 1;
     pad.axes_length = 2;
 
diff --git a/device/vr/isolated_gamepad_data_fetcher.cc b/device/vr/isolated_gamepad_data_fetcher.cc
index 0104435..51ea46a9 100644
--- a/device/vr/isolated_gamepad_data_fetcher.cc
+++ b/device/vr/isolated_gamepad_data_fetcher.cc
@@ -185,6 +185,8 @@
 
     seen_gamepads.insert(source->controller_id);
     dest.timestamp = TimeInMicroseconds(source->timestamp);
+    // WebVR did not support mapping.
+    dest.mapping = GamepadMapping::kNone;
     dest.pose = GamepadPoseFromXRPose(source->pose.get());
     dest.pose.has_position = source->can_provide_position;
     dest.pose.has_orientation = source->can_provide_orientation;
@@ -250,8 +252,6 @@
     TRACE_COUNTER1(
         "input", "XR gamepad sample age (ms)",
         (base::TimeTicks::Now() - source->timestamp).InMilliseconds());
-
-    dest.mapping[0] = 0;
   }
 
   // Remove any gamepads that aren't connected.
diff --git a/device/vr/oculus/oculus_gamepad_helper.cc b/device/vr/oculus/oculus_gamepad_helper.cc
index c9db334..84ec28d2 100644
--- a/device/vr/oculus/oculus_gamepad_helper.cc
+++ b/device/vr/oculus/oculus_gamepad_helper.cc
@@ -202,7 +202,7 @@
   // #550 (https://github.com/immersive-web/webxr/issues/550) is resolved.
   OculusGamepadBuilder(ovrInputState state, ovrHandType hand)
       : GamepadBuilder("oculus-touch",
-                       GamepadMapping::kXRStandard,
+                       GamepadMapping::kXrStandard,
                        OculusToMojomHand(hand)),
         state_(state) {}
 
diff --git a/device/vr/openvr/openvr_gamepad_helper.cc b/device/vr/openvr/openvr_gamepad_helper.cc
index 2e4a4ad8..f30034b 100644
--- a/device/vr/openvr/openvr_gamepad_helper.cc
+++ b/device/vr/openvr/openvr_gamepad_helper.cc
@@ -252,7 +252,7 @@
                        vr::VRControllerState_t controller_state,
                        device::mojom::XRHandedness handedness)
       : GamepadBuilder(GetGamepadId(vr_system, controller_id),
-                       GamepadMapping::kXRStandard,
+                       GamepadMapping::kXrStandard,
                        handedness),
         controller_state_(controller_state) {
     supported_buttons_ = vr_system->GetUint64TrackedDeviceProperty(
diff --git a/device/vr/orientation/orientation_device.cc b/device/vr/orientation/orientation_device.cc
index 30f04c08..71c3f465 100644
--- a/device/vr/orientation/orientation_device.cc
+++ b/device/vr/orientation/orientation_device.cc
@@ -9,6 +9,7 @@
 #include "base/numerics/math_constants.h"
 #include "base/time/time.h"
 #include "device/vr/orientation/orientation_device.h"
+#include "services/device/public/cpp/generic_sensor/sensor_reading.h"
 #include "services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h"
 #include "services/device/public/mojom/sensor_provider.mojom.h"
 #include "ui/display/display.h"
@@ -87,24 +88,15 @@
 
   binding_.Bind(std::move(params->client_request));
 
-  shared_buffer_handle_ = std::move(params->memory);
-  DCHECK(!shared_buffer_);
-  shared_buffer_ = shared_buffer_handle_->MapAtOffset(kReadBufferSize,
-                                                      params->buffer_offset);
-
-  if (!shared_buffer_) {
+  shared_buffer_reader_ = device::SensorReadingSharedBufferReader::Create(
+      std::move(params->memory), params->buffer_offset);
+  if (!shared_buffer_reader_) {
     // If we cannot read data, we cannot supply a device.
     HandleSensorError();
     std::move(ready_callback_).Run();
     return;
   }
 
-  const device::SensorReadingSharedBuffer* buffer =
-      static_cast<const device::SensorReadingSharedBuffer*>(
-          shared_buffer_.get());
-  shared_buffer_reader_.reset(
-      new device::SensorReadingSharedBufferReader(buffer));
-
   default_config.set_frequency(kDefaultPumpFrequencyHz);
   sensor_.set_connection_error_handler(base::BindOnce(
       &VROrientationDevice::HandleSensorError, base::Unretained(this)));
@@ -134,8 +126,7 @@
 
 void VROrientationDevice::HandleSensorError() {
   sensor_.reset();
-  shared_buffer_handle_.reset();
-  shared_buffer_.reset();
+  shared_buffer_reader_.reset();
   binding_.Close();
 }
 
diff --git a/device/vr/orientation/orientation_device.h b/device/vr/orientation/orientation_device.h
index f6e39daa..04459c92 100644
--- a/device/vr/orientation/orientation_device.h
+++ b/device/vr/orientation/orientation_device.h
@@ -76,8 +76,6 @@
   gfx::Quaternion latest_pose_;
 
   mojom::SensorPtr sensor_;
-  mojo::ScopedSharedBufferHandle shared_buffer_handle_;
-  mojo::ScopedSharedBufferMapping shared_buffer_;
   std::unique_ptr<SensorReadingSharedBufferReader> shared_buffer_reader_;
   mojo::Binding<mojom::SensorClient> binding_;
 };
diff --git a/device/vr/util/gamepad_builder.cc b/device/vr/util/gamepad_builder.cc
index 2d504ec..90735b4 100644
--- a/device/vr/util/gamepad_builder.cc
+++ b/device/vr/util/gamepad_builder.cc
@@ -25,48 +25,26 @@
   NOTREACHED();
 }
 
-// TODO(crbug.com/955809): Once Gamepad uses an enum this method can be removed.
-std::string GamepadMappingToString(GamepadBuilder::GamepadMapping mapping) {
-  switch (mapping) {
-    case GamepadBuilder::GamepadMapping::kNone:
-      return "";
-      break;
-    case GamepadBuilder::GamepadMapping::kStandard:
-      return "standard";
-      break;
-    case GamepadBuilder::GamepadMapping::kXRStandard:
-      return "xr-standard";
-      break;
-  }
-
-  NOTREACHED();
-}
-
 }  // anonymous namespace
 
 GamepadBuilder::GamepadBuilder(const std::string& gamepad_id,
                                GamepadMapping mapping,
-                               device::mojom::XRHandedness handedness)
-    : mapping_(mapping) {
+                               device::mojom::XRHandedness handedness) {
   DCHECK_LT(gamepad_id.size(), Gamepad::kIdLengthCap);
 
-  auto mapping_str = GamepadMappingToString(mapping);
-  DCHECK_LT(mapping_str.size(), Gamepad::kMappingLengthCap);
-
   gamepad_.connected = true;
   gamepad_.timestamp = base::TimeTicks::Now().since_origin().InMicroseconds();
+  gamepad_.mapping = mapping;
   gamepad_.hand = MojoToGamepadHandedness(handedness);
   CopyToUString(base::UTF8ToUTF16(gamepad_id), gamepad_.id,
                 Gamepad::kIdLengthCap);
-  CopyToUString(base::UTF8ToUTF16(mapping_str), gamepad_.mapping,
-                Gamepad::kMappingLengthCap);
 }
 
 GamepadBuilder::~GamepadBuilder() = default;
 
 bool GamepadBuilder::IsValid() const {
-  switch (mapping_) {
-    case GamepadMapping::kXRStandard:
+  switch (GetMapping()) {
+    case GamepadMapping::kXrStandard:
       // In order to satisfy the XRStandard mapping at least two buttons and one
       // set of axes need to have been added.
       return gamepad_.axes_length >= 2 && gamepad_.buttons_length >= 2;
diff --git a/device/vr/util/gamepad_builder.h b/device/vr/util/gamepad_builder.h
index 7b4eb55..09ac202 100644
--- a/device/vr/util/gamepad_builder.h
+++ b/device/vr/util/gamepad_builder.h
@@ -14,8 +14,6 @@
 
 class GamepadBuilder {
  public:
-  // TODO(crbug.com/955809): Switch this to device::Gamepad's mapping enum.
-  enum class GamepadMapping { kNone = 0, kStandard = 1, kXRStandard = 2 };
 
   // Helper struct that we don't want to pollute the device namespace
   struct ButtonData {
@@ -48,10 +46,9 @@
   double ApplyAxisDeadzoneToValue(double value) const;
 
   GamepadHand GetHandedness() const { return gamepad_.hand; }
-  GamepadMapping GetMapping() const { return mapping_; }
+  GamepadMapping GetMapping() const { return gamepad_.mapping; }
 
  private:
-  const GamepadMapping mapping_;
   double axis_deadzone_ = 0.0;
   Gamepad gamepad_;
 
diff --git a/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc b/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc
index faeca8d..f8dfbec6 100644
--- a/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc
+++ b/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc
@@ -208,8 +208,7 @@
 
   // We have to use the GamepadBuilder because the mojom serialization complains
   // if some of the values are missing/invalid.
-  GamepadBuilder builder(gamepad_id, GamepadBuilder::GamepadMapping::kNone,
-                         handedness);
+  GamepadBuilder builder(gamepad_id, GamepadMapping::kNone, handedness);
 
   auto input_source_state = mojom::XRInputSourceState::New();
   input_source_state->gamepad = builder.GetGamepad();
@@ -232,8 +231,7 @@
 
   // TODO(https://crbug.com/942201): Get correct ID string once WebXR spec issue
   // #550 (https://github.com/immersive-web/webxr/issues/550) is resolved.
-  GamepadBuilder builder("windows-mixed-reality",
-                         GamepadBuilder::GamepadMapping::kXRStandard,
+  GamepadBuilder builder("windows-mixed-reality", GamepadMapping::kXrStandard,
                          handedness);
 
   builder.SetAxisDeadzone(kDeadzoneMinimum);
diff --git a/docs/infra/cq_builders.md b/docs/infra/cq_builders.md
index 006f6654..a738300 100644
--- a/docs/infra/cq_builders.md
+++ b/docs/infra/cq_builders.md
@@ -1,7 +1,7 @@
 # List of CQ builders
 
 This page is auto generated using the script
-//infra/config/cq_config_presubmit.py. Do not manually edit.
+//infra/config/cq_cfg_presubmit.py. Do not manually edit.
 
 [TOC]
 
@@ -226,13 +226,6 @@
     * [`//third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG)
     * [`//third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/)
 
-* [linux_mojo](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_mojo) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux_mojo)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_mojo))
-
-  Path regular expressions:
-    * [`//services/network/.+`](https://cs.chromium.org/chromium/src/services/network/)
-    * [`//testing/buildbot/filters/mojo\\.fyi\\.network_.*`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:testing/buildbot/filters/mojo\\.fyi\\.network_.*)
-    * [`//third_party/blink/web_tests/FlagExpectations/enable-features=NetworkService`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/web_tests/FlagExpectations/enable-features=NetworkService)
-
 * [linux_optional_gpu_tests_rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_optional_gpu_tests_rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux_optional_gpu_tests_rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_optional_gpu_tests_rel))
 
   Path regular expressions:
diff --git a/docs/use_counter_wiki.md b/docs/use_counter_wiki.md
new file mode 100644
index 0000000..20d8410
--- /dev/null
+++ b/docs/use_counter_wiki.md
@@ -0,0 +1,184 @@
+# UseCounter Wiki
+
+UseCounter measures the usage of HTML and JavaScript features across all
+channels and platforms in the wild. Feature usage is recorded per page load and
+is anonymously aggregated. Note measurements only take place on HTTP/HTTPS
+pages. Usages on new tab page, data URL, or file URL are not. Usages on
+extensions is measured on a separate histogram.
+
+UseCounter data can be biased against scenarios where user metrics analysis is
+not enabled (e.g., enterprises). However, UseCounter data is essential for
+[web compat decision making](https://www.chromium.org/blink/platform-predictability/compat-tools)
+and [the blink process for breaking changes](https://sites.google.com/a/chromium.org/dev/blink/removing-features), as it reflects the real Chrome usage with a wide fraction of coverage.
+The results are publicly available on https://chromestatus.com/ and internally
+(for Google employees) on [UMA dashboard](https://goto.google.com/uma-usecounter)
+and [UKM dashboard](https://goto.google.com/ukm-usecounter) with more detailed
+break-downs.
+
+
+## How to Add a UseCounter Feature
+
+UseCounter measures feature usage via UMA histogram and UKM. To add your
+feature to UseCounter, simply:
++ Add your feature to the blink::WebFeature enum;
++ Usage can be measured via:
+    * MeasureAs=\<enum value\> in the feature's IDL definition; Or
+    * blink::UseCounter::Count() for blink side features; Or
+    * page_load_metrics::MetricsWebContentsObserver::RecordFeatureUsage()
+      for browser side features.
+
+Example:
+```c++
+enum WebFeature {
+  ...
+  kMyFeature = N,
+  ...
+}
+```
+```
+interface MyInterface {
+  ...
+  [MeasureAs=MyFeature] myIdlAttribute;
+  ...
+}
+```
+OR
+```c++
+  MyInterface::MyBlinkSideFunction() {
+    ...
+    UseCounter::Count(context, WebFeature::MyFeature);
+    ...
+  }
+```
+OR
+```c++
+  MyBrowserSideFunction() {
+    ...
+    page_load_metrics::MetricsWebContentObserver::RecordFeatureUsage(
+      render_frame_host, blink::mojom::WebFeature::MyFeature);
+    ...
+  }
+```
+
+Not all features collect URL-keyed metrics. To opt in your feature to UKM,
+simply add your feature to
+[UseCounterPageLoadMetricsObserver::GetAllowedUkmFeatures()](https://cs.chromium.org/chromium/src/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc)
+and get approval from one of the privacy owners.
+
+You can quickly verify that your feature is added to UMA histograms and UKM by
+checking chrome://histograms/Blink.UseCounter.Features and chrome://ukm in your
+local build.
+
+## Analyze UseCounter Histogram Data
+
+### Public Data on https://chromestatus.com
+
+Usage of JavaScript and HTML features is available
+[here](https://chromestatus.com/metrics/feature/popularity).
+Usage of CSS properties is available
+[here](https://chromestatus.com/metrics/css/popularity).
+
+The data reflects features' daily usage (count of feature hits / count of total
+page visits):
++ On all platforms: Android, Windows, Mac, ChromeOS, and Linux.
++ On "official" channels: stable, beta, and dev.
++ For the most dominant milestone.
+
+
+### UMA Timeline with Formula
+
+Internally (sorry, Google employees only) you can query the usage of a feature
+with break-downs on platforms, channels, etc on the
+[UMA Timeline dashboard](https://goto.google.com/uma-usecounter).
+
+To create break-downs, select filters on the top of the dashboard, for example,
+"Platform", and set the `operation` to "split by". Note that you can also see
+data usage within Android Webview by setting a filter on "Platform".
+
+Select Metric:
++ "Blink.UseCounter.Features" for HTML and JavaScript features.
++ "Blink.UseCounter.CSSProperties" for CSS properties.
++ "Blink.UseCounter.AnimatedCSSProperties" for animated CSS properties.
++ "Blink.UseCounter.Extensions.Features" for HTML and JacaScript features on
+  extensions.
+
+In the metric panel, select "Formula" in the left-most drop-down. Then Click
+"ADD NEW FORMULA" with:
+```
+"MyFeature" / "PageVisits" * 100
+```
+
+This provides timeline data for your feature usage (per page load) with
+break-downs, which should more or less reflects the results on chromestatus.com.
+
+
+### UseCounter Feature in HTTP Archive
+
+HTTP Archive crawls the top 10K sites on the web and records everything from
+request and response headers. The data is available on Google BigQuery.
+
+You can find pages that trigger a particular UseCounter using the following
+script:
+
+```sql
+SELECT
+  DATE(yyyymmdd) AS date,
+  client AS platform,
+  num_url AS url_count,
+  pct_urls AS urls_percentile,
+  sample_urls AS url
+FROM [httparchive:blink_features.usage]
+WHERE feature = 'MyFeature'
+ORDER BY url_percentile DESC
+```
+OR
+
+```sql
+SELECT
+  url
+FROM [httparchive:pages.yyyy_mm_dd_mobile]
+WHERE
+  JSON_EXTRACT(payload, '$._blinkFeatureFirstUsed.Features.MyFeature') IS NOT
+  NULL
+LIMIT 500
+```
+
+You can also find pages that trigger a particular CSS property (during parsing):
+
+```sql
+SELECT
+  url
+FROM [httparchive:pages.yyyy_mm_dd_mobile]
+WHERE
+  JSON_EXTRACT(payload, '$._blinkFeatureFirstUsed.CSSFeatures.MyCSSProperty')
+  IS NOT NULL
+LIMIT 500
+```
+
+To find pages that trigger a UseCounter and sort by page rank:
+
+```sql
+SELECT
+  IFNULL(runs.rank, 1000000) AS rank,
+  har.url AS url,
+FROM [httparchive:latest.pages_desktop] AS har
+LEFT JOIN [httparchive:runs.latest_pages] AS runs
+  ON har.url = runs.url
+WHERE
+  JSON_EXTRACT(payload, '$._blinkFeatureFirstUsed.Features.MyFeature') IS NOT
+  NULL
+ORDER BY rank;
+```
+
+
+### UMA Usage on Fraction of Users
+You may also see the fraction of users that trigger your feature at lease once a
+day on [UMA Usage dashboard](https://goto.google.com/uma-usecounter-peruser).
+
+
+## Analyze UseCounter UKM Data
+For privacy concerns, UKM data is available for Google employees only.
+
+### UKM Dashboard
+UKM dashboard is accessible to Google employees only. Please see [this internal
+wiki](https://goto.google.com/usecounter-ukm-wiki) for details.
diff --git a/extensions/browser/api/cast_channel/cast_channel_apitest.cc b/extensions/browser/api/cast_channel/cast_channel_apitest.cc
index 6bf58e1..50ffcdd 100644
--- a/extensions/browser/api/cast_channel/cast_channel_apitest.cc
+++ b/extensions/browser/api/cast_channel/cast_channel_apitest.cc
@@ -9,10 +9,12 @@
 #include "base/files/file_path.h"
 #include "base/memory/ptr_util.h"
 #include "base/task/post_task.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/timer/mock_timer.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
+#include "chrome/browser/media/router/media_router_feature.h"
 #include "chrome/browser/media/router/providers/cast/dual_media_sink_service.h"
 #include "chrome/browser/media/router/test/noop_dual_media_sink_service.h"
 #include "chrome/browser/ui/browser.h"
@@ -46,21 +48,21 @@
 using ::cast_channel::MockCastSocket;
 using ::cast_channel::MockCastTransport;
 using ::cast_channel::ReadyState;
+using extensions::Extension;
 using extensions::api::cast_channel::ErrorInfo;
 using extensions::api::cast_channel::MessageInfo;
-using extensions::Extension;
 
 namespace utils = extension_function_test_utils;
 
 using ::testing::_;
 using ::testing::A;
 using ::testing::DoAll;
-using ::testing::Invoke;
 using ::testing::InSequence;
+using ::testing::Invoke;
 using ::testing::NotNull;
 using ::testing::Return;
-using ::testing::ReturnRef;
 using ::testing::ReturnPointee;
+using ::testing::ReturnRef;
 using ::testing::SaveArg;
 using ::testing::WithArgs;
 
@@ -99,6 +101,7 @@
     // Stub out DualMediaSinkService so it does not interfere with the test.
     media_router::DualMediaSinkService::SetInstanceForTest(
         new media_router::NoopDualMediaSinkService());
+    feature_list_.InitAndDisableFeature(media_router::kDialMediaRouteProvider);
     extensions::ExtensionApiTest::SetUp();
   }
 
@@ -233,7 +236,7 @@
   extensions::CastChannelOpenFunction* CreateOpenFunction(
       scoped_refptr<const Extension> extension) {
     extensions::CastChannelOpenFunction* cast_channel_open_function =
-      new extensions::CastChannelOpenFunction;
+        new extensions::CastChannelOpenFunction;
     cast_channel_open_function->set_extension(extension.get());
     return cast_channel_open_function;
   }
@@ -241,7 +244,7 @@
   extensions::CastChannelSendFunction* CreateSendFunction(
       scoped_refptr<const Extension> extension) {
     extensions::CastChannelSendFunction* cast_channel_send_function =
-      new extensions::CastChannelSendFunction;
+        new extensions::CastChannelSendFunction;
     cast_channel_send_function->set_extension(extension.get());
     return cast_channel_send_function;
   }
@@ -252,6 +255,7 @@
   CastSocket::Observer* message_observer_;
   net::TestNetLog capturing_net_log_;
   int channel_id_;
+  base::test::ScopedFeatureList feature_list_;
 };
 
 ACTION_P2(InvokeObserverOnError, api_test, cast_socket_service) {
@@ -274,8 +278,8 @@
 IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenSendClose) {
   SetUpOpenSendClose();
 
-  EXPECT_TRUE(RunExtensionSubtest("cast_channel/api",
-                                  "test_open_send_close.html"));
+  EXPECT_TRUE(
+      RunExtensionSubtest("cast_channel/api", "test_open_send_close.html"));
 }
 
 // TODO(kmarshall): Win Dbg has a workaround that makes RunExtensionSubtest
@@ -371,8 +375,8 @@
         .WillOnce(Return(ReadyState::CLOSED));
   }
 
-  EXPECT_TRUE(RunExtensionSubtest("cast_channel/api",
-                                  "test_open_receive_close.html"));
+  EXPECT_TRUE(
+      RunExtensionSubtest("cast_channel/api", "test_open_receive_close.html"));
 
   extensions::ResultCatcher catcher;
   CallOnMessage("some-message");
@@ -405,8 +409,7 @@
   EXPECT_CALL(*mock_cast_socket_, Close(_))
       .WillOnce(InvokeCompletionCallback<0>(net::OK));
 
-  EXPECT_TRUE(RunExtensionSubtest("cast_channel/api",
-                                  "test_open_error.html"));
+  EXPECT_TRUE(RunExtensionSubtest("cast_channel/api", "test_open_error.html"));
 }
 
 IN_PROC_BROWSER_TEST_F(CastChannelAPITest, TestOpenInvalidConnectInfo) {
diff --git a/extensions/browser/api/webcam_private/webcam_private_api_chromeos.cc b/extensions/browser/api/webcam_private/webcam_private_api_chromeos.cc
index 39fa2274..cd9d3a6 100644
--- a/extensions/browser/api/webcam_private/webcam_private_api_chromeos.cc
+++ b/extensions/browser/api/webcam_private/webcam_private_api_chromeos.cc
@@ -126,7 +126,7 @@
       extensions::Extension::GetBaseURLFromExtensionId(extension_id));
 
   return content::GetMediaDeviceIDForHMAC(
-      blink::MEDIA_DEVICE_VIDEO_CAPTURE,
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
       browser_context_->GetMediaDeviceIDSalt(), security_origin, webcam_id,
       device_id);
 }
diff --git a/extensions/browser/app_window/app_delegate.h b/extensions/browser/app_window/app_delegate.h
index c4870d6..1218ec59 100644
--- a/extensions/browser/app_window/app_delegate.h
+++ b/extensions/browser/app_window/app_delegate.h
@@ -82,7 +82,7 @@
   virtual bool CheckMediaAccessPermission(
       content::RenderFrameHost* render_frame_host,
       const GURL& security_origin,
-      blink::MediaStreamType type,
+      blink::mojom::MediaStreamType type,
       const Extension* extension) = 0;
   virtual int PreferredIconSize() const = 0;
 
diff --git a/extensions/browser/app_window/app_web_contents_helper.cc b/extensions/browser/app_window/app_web_contents_helper.cc
index 13a7259e..0496598 100644
--- a/extensions/browser/app_window/app_web_contents_helper.cc
+++ b/extensions/browser/app_window/app_web_contents_helper.cc
@@ -107,7 +107,7 @@
 bool AppWebContentsHelper::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type) const {
+    blink::mojom::MediaStreamType type) const {
   const Extension* extension = GetExtension();
   if (!extension)
     return false;
diff --git a/extensions/browser/app_window/app_web_contents_helper.h b/extensions/browser/app_window/app_web_contents_helper.h
index ea628cb..9321cdd 100644
--- a/extensions/browser/app_window/app_web_contents_helper.h
+++ b/extensions/browser/app_window/app_web_contents_helper.h
@@ -54,7 +54,7 @@
   // WebContentsDelegate.
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type) const;
+                                  blink::mojom::MediaStreamType type) const;
 
  private:
   const Extension* GetExtension() const;
diff --git a/extensions/browser/app_window/app_window.cc b/extensions/browser/app_window/app_window.cc
index 3804502b..f104301 100644
--- a/extensions/browser/app_window/app_window.cc
+++ b/extensions/browser/app_window/app_window.cc
@@ -355,7 +355,7 @@
 bool AppWindow::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   DCHECK_EQ(web_contents(),
             content::WebContents::FromRenderFrameHost(render_frame_host)
                 ->GetOutermostWebContents());
diff --git a/extensions/browser/app_window/app_window.h b/extensions/browser/app_window/app_window.h
index bd6c41e..1ace1a10 100644
--- a/extensions/browser/app_window/app_window.h
+++ b/extensions/browser/app_window/app_window.h
@@ -421,7 +421,7 @@
       content::MediaResponseCallback callback) override;
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type) override;
+                                  blink::mojom::MediaStreamType type) override;
   content::WebContents* OpenURLFromTab(
       content::WebContents* source,
       const content::OpenURLParams& params) override;
diff --git a/extensions/browser/extension_host.cc b/extensions/browser/extension_host.cc
index 8cc9503..b126f42 100644
--- a/extensions/browser/extension_host.cc
+++ b/extensions/browser/extension_host.cc
@@ -453,7 +453,7 @@
 bool ExtensionHost::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   return delegate_->CheckMediaAccessPermission(
       render_frame_host, security_origin, type, extension());
 }
diff --git a/extensions/browser/extension_host.h b/extensions/browser/extension_host.h
index 4027505..f4bbe0b1 100644
--- a/extensions/browser/extension_host.h
+++ b/extensions/browser/extension_host.h
@@ -126,7 +126,7 @@
       content::MediaResponseCallback callback) override;
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type) override;
+                                  blink::mojom::MediaStreamType type) override;
   bool IsNeverVisible(content::WebContents* web_contents) override;
   gfx::Size EnterPictureInPicture(content::WebContents* web_contents,
                                   const viz::SurfaceId& surface_id,
diff --git a/extensions/browser/extension_host_delegate.h b/extensions/browser/extension_host_delegate.h
index 446ba131..f075bb44 100644
--- a/extensions/browser/extension_host_delegate.h
+++ b/extensions/browser/extension_host_delegate.h
@@ -71,7 +71,7 @@
   virtual bool CheckMediaAccessPermission(
       content::RenderFrameHost* render_frame_host,
       const GURL& security_origin,
-      blink::MediaStreamType type,
+      blink::mojom::MediaStreamType type,
       const Extension* extension) = 0;
 
   // Returns the ExtensionHostQueue implementation to use for creating
diff --git a/extensions/browser/guest_view/app_view/app_view_guest.cc b/extensions/browser/guest_view/app_view/app_view_guest.cc
index b075617..0421a7d8 100644
--- a/extensions/browser/guest_view/app_view/app_view_guest.cc
+++ b/extensions/browser/guest_view/app_view/app_view_guest.cc
@@ -147,7 +147,7 @@
 bool AppViewGuest::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   if (!app_delegate_) {
     return WebContentsDelegate::CheckMediaAccessPermission(
         render_frame_host, security_origin, type);
diff --git a/extensions/browser/guest_view/app_view/app_view_guest.h b/extensions/browser/guest_view/app_view/app_view_guest.h
index 0fea3d0..bf9cba8 100644
--- a/extensions/browser/guest_view/app_view/app_view_guest.h
+++ b/extensions/browser/guest_view/app_view/app_view_guest.h
@@ -68,7 +68,7 @@
       content::MediaResponseCallback callback) final;
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type) final;
+                                  blink::mojom::MediaStreamType type) final;
 
   void CompleteCreateWebContents(const GURL& url,
                                  const Extension* guest_extension,
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.cc b/extensions/browser/guest_view/web_view/web_view_guest.cc
index ffa0d80..2446794a 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_guest.cc
@@ -1055,7 +1055,7 @@
 bool WebViewGuest::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   return web_view_permission_helper_->CheckMediaAccessPermission(
       render_frame_host, security_origin, type);
 }
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.h b/extensions/browser/guest_view/web_view/web_view_guest.h
index e0fbe827..4321cc0 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.h
+++ b/extensions/browser/guest_view/web_view/web_view_guest.h
@@ -235,7 +235,7 @@
       const base::Callback<void(bool)>& callback) final;
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type) final;
+                                  blink::mojom::MediaStreamType type) final;
   void CanDownload(const GURL& url,
                    const std::string& request_method,
                    base::OnceCallback<void(bool)> callback) final;
diff --git a/extensions/browser/guest_view/web_view/web_view_media_access_apitest.cc b/extensions/browser/guest_view/web_view/web_view_media_access_apitest.cc
index 4a0e076..ff3b40d7 100644
--- a/extensions/browser/guest_view/web_view/web_view_media_access_apitest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_media_access_apitest.cc
@@ -35,7 +35,7 @@
 
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type) override {
+                                  blink::mojom::MediaStreamType type) override {
     checked_ = true;
     if (check_message_loop_runner_.get())
       check_message_loop_runner_->Quit();
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper.cc b/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
index 663a36f..ad7b547 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
@@ -208,7 +208,7 @@
 bool WebViewPermissionHelper::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type) {
+    blink::mojom::MediaStreamType type) {
   if (!web_view_guest()->attached() ||
       !web_view_guest()->embedder_web_contents()->GetDelegate()) {
     return false;
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper.h b/extensions/browser/guest_view/web_view/web_view_permission_helper.h
index c3b9014..c0b1b49 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper.h
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper.h
@@ -65,7 +65,7 @@
                                     content::MediaResponseCallback callback);
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type);
+                                  blink::mojom::MediaStreamType type);
   void CanDownload(const GURL& url,
                    const std::string& request_method,
                    base::OnceCallback<void(bool)> callback);
diff --git a/extensions/browser/media_capture_util.cc b/extensions/browser/media_capture_util.cc
index 4b3ad0d..98b8121 100644
--- a/extensions/browser/media_capture_util.cc
+++ b/extensions/browser/media_capture_util.cc
@@ -52,12 +52,15 @@
                              content::MediaResponseCallback callback,
                              const Extension* extension) {
   // app_shell only supports audio and video capture, not tab or screen capture.
-  DCHECK(request.audio_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE ||
-         request.video_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE);
+  DCHECK(request.audio_type ==
+             blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE ||
+         request.video_type ==
+             blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
 
   MediaStreamDevices devices;
 
-  if (request.audio_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE) {
+  if (request.audio_type ==
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
     VerifyMediaAccessPermission(request.audio_type, extension);
     const MediaStreamDevice* device = GetRequestedDeviceOrDefault(
         MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices(),
@@ -66,7 +69,8 @@
       devices.push_back(*device);
   }
 
-  if (request.video_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE) {
+  if (request.video_type ==
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
     VerifyMediaAccessPermission(request.video_type, extension);
     const MediaStreamDevice* device = GetRequestedDeviceOrDefault(
         MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices(),
@@ -84,28 +88,28 @@
       std::move(ui));
 }
 
-void VerifyMediaAccessPermission(blink::MediaStreamType type,
+void VerifyMediaAccessPermission(blink::mojom::MediaStreamType type,
                                  const Extension* extension) {
   const PermissionsData* permissions_data = extension->permissions_data();
-  if (type == blink::MEDIA_DEVICE_AUDIO_CAPTURE) {
+  if (type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
     // app_shell has no UI surface to show an error, and on an embedded device
     // it's better to crash than to have a feature not work.
     CHECK(permissions_data->HasAPIPermission(APIPermission::kAudioCapture))
         << "Audio capture request but no audioCapture permission in manifest.";
   } else {
-    DCHECK(type == blink::MEDIA_DEVICE_VIDEO_CAPTURE);
+    DCHECK(type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
     CHECK(permissions_data->HasAPIPermission(APIPermission::kVideoCapture))
         << "Video capture request but no videoCapture permission in manifest.";
   }
 }
 
-bool CheckMediaAccessPermission(blink::MediaStreamType type,
+bool CheckMediaAccessPermission(blink::mojom::MediaStreamType type,
                                 const Extension* extension) {
   const PermissionsData* permissions_data = extension->permissions_data();
-  if (type == blink::MEDIA_DEVICE_AUDIO_CAPTURE) {
+  if (type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
     return permissions_data->HasAPIPermission(APIPermission::kAudioCapture);
   }
-  DCHECK(type == blink::MEDIA_DEVICE_VIDEO_CAPTURE);
+  DCHECK(type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
   return permissions_data->HasAPIPermission(APIPermission::kVideoCapture);
 }
 
diff --git a/extensions/browser/media_capture_util.h b/extensions/browser/media_capture_util.h
index e29223d..b152fea 100644
--- a/extensions/browser/media_capture_util.h
+++ b/extensions/browser/media_capture_util.h
@@ -30,11 +30,11 @@
                              const Extension* extension);
 
 // Verifies that the extension has permission for |type|. If not, crash.
-void VerifyMediaAccessPermission(blink::MediaStreamType type,
+void VerifyMediaAccessPermission(blink::mojom::MediaStreamType type,
                                  const Extension* extension);
 
 // Check if the extension has permission for |type|.
-bool CheckMediaAccessPermission(blink::MediaStreamType type,
+bool CheckMediaAccessPermission(blink::mojom::MediaStreamType type,
                                 const Extension* extension);
 
 }  // namespace media_capture_util
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index feb42c8..93439e9a 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -383,6 +383,8 @@
     sources = [
       "api/declarative_net_request/test_utils.cc",
       "api/declarative_net_request/test_utils.h",
+      "file_test_util.cc",
+      "file_test_util.h",
     ]
 
     deps = [
diff --git a/extensions/common/file_test_util.cc b/extensions/common/file_test_util.cc
new file mode 100644
index 0000000..f15c5f4
--- /dev/null
+++ b/extensions/common/file_test_util.cc
@@ -0,0 +1,17 @@
+// 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 "extensions/common/file_test_util.h"
+#include "base/files/file_util.h"
+
+namespace extensions {
+namespace file_test_util {
+
+bool WriteFile(const base::FilePath& path, base::StringPiece content) {
+  return base::WriteFile(path, content.data(), content.size()) ==
+         static_cast<int>(content.size());
+}
+
+}  // namespace file_test_util
+}  // namespace extensions
diff --git a/extensions/common/file_test_util.h b/extensions/common/file_test_util.h
new file mode 100644
index 0000000..ecab05df
--- /dev/null
+++ b/extensions/common/file_test_util.h
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef EXTENSIONS_COMMON_FILE_TEST_UTIL_H_
+#define EXTENSIONS_COMMON_FILE_TEST_UTIL_H_
+
+#include "base/strings/string_piece.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace extensions {
+namespace file_test_util {
+
+// Writes |content| to |path|. Returns true if writing was successful,
+// verifying the number of bytes written equals the size of |content|.
+bool WriteFile(const base::FilePath& path, base::StringPiece content);
+
+}  // namespace file_test_util
+}  // namespace extensions
+
+#endif  // EXTENSIONS_COMMON_FILE_TEST_UTIL_H_
diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc
index 85455a06..4aa72fe0 100644
--- a/extensions/common/manifest_constants.cc
+++ b/extensions/common/manifest_constants.cc
@@ -112,6 +112,7 @@
 const char kNaClModules[] = "nacl_modules";
 const char kNaClModulesMIMEType[] = "mime_type";
 const char kNaClModulesPath[] = "path";
+const char kNativelyConnectable[] = "natively_connectable";
 const char kOAuth2[] = "oauth2";
 const char kOAuth2AutoApprove[] = "oauth2.auto_approve";
 const char kOAuth2ClientId[] = "oauth2.client_id";
@@ -557,6 +558,10 @@
     "Invalid value for 'minimum_chrome_version'.";
 const char kInvalidName[] =
     "Required value 'name' is missing or invalid.";
+const char kInvalidNativelyConnectable[] =
+    "Invalid natively_connectable. Must be a list.";
+const char kInvalidNativelyConnectableValue[] =
+    "Invalid natively_connectable value. Must be a string.";
 const char kInvalidNaClModules[] =
     "Invalid value for 'nacl_modules'.";
 const char kInvalidNaClModulesPath[] =
@@ -747,6 +752,9 @@
     "'content_security_policy.sandbox'.";
 const char kRulesFileIsInvalid[] =
     "Invalid value for key '*.*': The provided path is invalid.";
+const char kTransientBackgroundConflictsWithPersistentBackground[] =
+    "The 'transientBackground' permission cannot be used with a persistent "
+    "background page.";
 const char kTtsGenderIsDeprecated[] =
     "Voice gender is deprecated and values will be ignored starting in Chrome "
     "71";
diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h
index dbb3e25f..6be8e12 100644
--- a/extensions/common/manifest_constants.h
+++ b/extensions/common/manifest_constants.h
@@ -115,6 +115,7 @@
 extern const char kNaClModulesMIMEType[];
 extern const char kNaClModulesPath[];
 extern const char kName[];
+extern const char kNativelyConnectable[];
 extern const char kOAuth2[];
 extern const char kOAuth2AutoApprove[];
 extern const char kOAuth2ClientId[];
@@ -409,6 +410,8 @@
 extern const char kInvalidNaClModulesMIMEType[];
 extern const char kInvalidNaClModulesPath[];
 extern const char kInvalidName[];
+extern const char kInvalidNativelyConnectable[];
+extern const char kInvalidNativelyConnectableValue[];
 extern const char kInvalidOAuth2AutoApprove[];
 extern const char kInvalidOAuth2ClientId[];
 extern const char kInvalidOAuth2Scopes[];
@@ -501,6 +504,7 @@
 extern const char kReservedMessageFound[];
 extern const char kSandboxPagesCSPKeyNotAllowed[];
 extern const char kRulesFileIsInvalid[];
+extern const char kTransientBackgroundConflictsWithPersistentBackground[];
 extern const char kTtsGenderIsDeprecated[];
 extern const char kUnrecognizedManifestKey[];
 extern const char kUnrecognizedManifestProperty[];
diff --git a/extensions/common/manifest_handler.h b/extensions/common/manifest_handler.h
index 5e1dffe..702a4ba 100644
--- a/extensions/common/manifest_handler.h
+++ b/extensions/common/manifest_handler.h
@@ -177,7 +177,7 @@
   // Any new manifest handlers added may cause the small_map to overflow
   // to the backup std::unordered_map, which we don't want, as that would
   // defeat the optimization of using small_map.
-  static constexpr size_t kHandlerMax = 73;
+  static constexpr size_t kHandlerMax = 74;
   using FallbackMap = std::unordered_map<std::string, ManifestHandler*>;
   using ManifestHandlerMap = base::small_map<FallbackMap, kHandlerMax>;
   using FallbackPriorityMap = std::unordered_map<ManifestHandler*, int>;
diff --git a/extensions/common/manifest_handlers/background_info.cc b/extensions/common/manifest_handlers/background_info.cc
index eae4adc2..053f142 100644
--- a/extensions/common/manifest_handlers/background_info.cc
+++ b/extensions/common/manifest_handlers/background_info.cc
@@ -296,6 +296,14 @@
     return false;
   }
 
+  if (!info->has_lazy_background_page() &&
+      PermissionsParser::HasAPIPermission(
+          extension, APIPermission::kTransientBackground)) {
+    *error = ASCIIToUTF16(
+        errors::kTransientBackgroundConflictsWithPersistentBackground);
+    return false;
+  }
+
   extension->SetManifestData(kBackground, std::move(info));
   return true;
 }
diff --git a/extensions/common/permissions/api_permission.h b/extensions/common/permissions/api_permission.h
index ff11ffdfa..e93b37e 100644
--- a/extensions/common/permissions/api_permission.h
+++ b/extensions/common/permissions/api_permission.h
@@ -260,8 +260,9 @@
     kEnterpriseHardwarePlatform = 216,
     kLoginScreenUi = 217,
     kDeclarativeNetRequestFeedback = 218,
+    kTransientBackground = 219,
     // Last entry: Add new entries above and ensure to update the
-    // "ExtensionPermission3" enum in tools/metrics/histograms/histograms.xml
+    // "ExtensionPermission3" enum in tools/metrics/histograms/enums.xml
     // (by running update_extension_permission.py).
     kEnumBoundary,
   };
diff --git a/extensions/shell/browser/shell_app_delegate.cc b/extensions/shell/browser/shell_app_delegate.cc
index c21e677..e4f1f252 100644
--- a/extensions/shell/browser/shell_app_delegate.cc
+++ b/extensions/shell/browser/shell_app_delegate.cc
@@ -78,7 +78,7 @@
 bool ShellAppDelegate::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const Extension* extension) {
   media_capture_util::VerifyMediaAccessPermission(type, extension);
   return true;
diff --git a/extensions/shell/browser/shell_app_delegate.h b/extensions/shell/browser/shell_app_delegate.h
index 8f42250..5cf5d83 100644
--- a/extensions/shell/browser/shell_app_delegate.h
+++ b/extensions/shell/browser/shell_app_delegate.h
@@ -43,7 +43,7 @@
                                     const Extension* extension) override;
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type,
+                                  blink::mojom::MediaStreamType type,
                                   const Extension* extension) override;
   int PreferredIconSize() const override;
   void SetWebContentsBlocked(content::WebContents* web_contents,
diff --git a/extensions/shell/browser/shell_extension_host_delegate.cc b/extensions/shell/browser/shell_extension_host_delegate.cc
index 0da639b..4818ae4 100644
--- a/extensions/shell/browser/shell_extension_host_delegate.cc
+++ b/extensions/shell/browser/shell_extension_host_delegate.cc
@@ -58,7 +58,7 @@
 bool ShellExtensionHostDelegate::CheckMediaAccessPermission(
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
-    blink::MediaStreamType type,
+    blink::mojom::MediaStreamType type,
     const Extension* extension) {
   media_capture_util::VerifyMediaAccessPermission(type, extension);
   return true;
diff --git a/extensions/shell/browser/shell_extension_host_delegate.h b/extensions/shell/browser/shell_extension_host_delegate.h
index 34dde0e5..f8fb8f4 100644
--- a/extensions/shell/browser/shell_extension_host_delegate.h
+++ b/extensions/shell/browser/shell_extension_host_delegate.h
@@ -31,7 +31,7 @@
                                  const Extension* extension) override;
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
-                                  blink::MediaStreamType type,
+                                  blink::mojom::MediaStreamType type,
                                   const Extension* extension) override;
   ExtensionHostQueue* GetExtensionHostQueue() const override;
   gfx::Size EnterPictureInPicture(content::WebContents* web_contents,
diff --git a/fuchsia/engine/BUILD.gn b/fuchsia/engine/BUILD.gn
index ff295ca..a175283 100644
--- a/fuchsia/engine/BUILD.gn
+++ b/fuchsia/engine/BUILD.gn
@@ -119,6 +119,8 @@
     "browser/web_engine_devtools_socket_factory.h",
     "browser/web_engine_net_log.cc",
     "browser/web_engine_net_log.h",
+    "browser/web_engine_permission_manager.cc",
+    "browser/web_engine_permission_manager.h",
     "browser/web_engine_screen.cc",
     "browser/web_engine_screen.h",
     "browser/web_engine_url_request_context_getter.cc",
diff --git a/fuchsia/engine/browser/web_engine_browser_context.cc b/fuchsia/engine/browser/web_engine_browser_context.cc
index 9fea00bb..62cc7f0f 100644
--- a/fuchsia/engine/browser/web_engine_browser_context.cc
+++ b/fuchsia/engine/browser/web_engine_browser_context.cc
@@ -18,6 +18,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/resource_context.h"
 #include "fuchsia/engine/browser/web_engine_net_log.h"
+#include "fuchsia/engine/browser/web_engine_permission_manager.h"
 #include "fuchsia/engine/browser/web_engine_url_request_context_getter.h"
 #include "fuchsia/engine/common.h"
 #include "net/url_request/url_request_context.h"
@@ -119,7 +120,9 @@
 
 content::PermissionControllerDelegate*
 WebEngineBrowserContext::GetPermissionControllerDelegate() {
-  return nullptr;
+  if (!permission_manager_)
+    permission_manager_ = std::make_unique<WebEnginePermissionManager>();
+  return permission_manager_.get();
 }
 
 content::ClientHintsControllerDelegate*
diff --git a/fuchsia/engine/browser/web_engine_browser_context.h b/fuchsia/engine/browser/web_engine_browser_context.h
index 671e3dd..6381e7af 100644
--- a/fuchsia/engine/browser/web_engine_browser_context.h
+++ b/fuchsia/engine/browser/web_engine_browser_context.h
@@ -13,6 +13,7 @@
 #include "content/public/browser/browser_context.h"
 
 class WebEngineNetLog;
+class WebEnginePermissionManager;
 class WebEngineURLRequestContextGetter;
 
 class WebEngineBrowserContext : public content::BrowserContext {
@@ -64,6 +65,7 @@
   scoped_refptr<WebEngineURLRequestContextGetter> url_request_getter_;
   std::unique_ptr<SimpleFactoryKey> simple_factory_key_;
   std::unique_ptr<ResourceContext> resource_context_;
+  std::unique_ptr<WebEnginePermissionManager> permission_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(WebEngineBrowserContext);
 };
diff --git a/fuchsia/engine/browser/web_engine_permission_manager.cc b/fuchsia/engine/browser/web_engine_permission_manager.cc
new file mode 100644
index 0000000..cac44b9
--- /dev/null
+++ b/fuchsia/engine/browser/web_engine_permission_manager.cc
@@ -0,0 +1,86 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fuchsia/engine/browser/web_engine_permission_manager.h"
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "content/public/browser/permission_controller.h"
+#include "content/public/browser/permission_type.h"
+
+namespace {
+blink::mojom::PermissionStatus CheckPermissionStatus(
+    content::PermissionType permission_type) {
+  // TODO(crbug/922833): Temporary grant permission of
+  // PROTECTED_MEDIA_IDENTIFIER to unblock EME development.
+  return permission_type == content::PermissionType::PROTECTED_MEDIA_IDENTIFIER
+             ? blink::mojom::PermissionStatus::GRANTED
+             : blink::mojom::PermissionStatus::DENIED;
+}
+}  // namespace
+
+WebEnginePermissionManager::WebEnginePermissionManager() = default;
+
+WebEnginePermissionManager::~WebEnginePermissionManager() = default;
+
+int WebEnginePermissionManager::RequestPermission(
+    content::PermissionType permission,
+    content::RenderFrameHost* render_frame_host,
+    const GURL& origin,
+    bool user_gesture,
+    base::OnceCallback<void(blink::mojom::PermissionStatus)> callback) {
+  std::move(callback).Run(CheckPermissionStatus(permission));
+  return content::PermissionController::kNoPendingOperation;
+}
+
+int WebEnginePermissionManager::RequestPermissions(
+    const std::vector<content::PermissionType>& permissions,
+    content::RenderFrameHost* render_frame_host,
+    const GURL& requesting_origin,
+    bool user_gesture,
+    base::OnceCallback<void(const std::vector<blink::mojom::PermissionStatus>&)>
+        callback) {
+  std::vector<blink::mojom::PermissionStatus> statuses;
+  for (auto permission : permissions)
+    statuses.push_back(CheckPermissionStatus(permission));
+
+  std::move(callback).Run(statuses);
+  return content::PermissionController::kNoPendingOperation;
+}
+
+void WebEnginePermissionManager::ResetPermission(
+    content::PermissionType permission,
+    const GURL& requesting_origin,
+    const GURL& embedding_origin) {
+  NOTIMPLEMENTED() << ": " << static_cast<int>(permission);
+}
+
+blink::mojom::PermissionStatus WebEnginePermissionManager::GetPermissionStatus(
+    content::PermissionType permission,
+    const GURL& requesting_origin,
+    const GURL& embedding_origin) {
+  return CheckPermissionStatus(permission);
+}
+
+blink::mojom::PermissionStatus
+WebEnginePermissionManager::GetPermissionStatusForFrame(
+    content::PermissionType permission,
+    content::RenderFrameHost* render_frame_host,
+    const GURL& requesting_origin) {
+  return CheckPermissionStatus(permission);
+}
+
+int WebEnginePermissionManager::SubscribePermissionStatusChange(
+    content::PermissionType permission,
+    content::RenderFrameHost* render_frame_host,
+    const GURL& requesting_origin,
+    base::RepeatingCallback<void(blink::mojom::PermissionStatus)> callback) {
+  NOTIMPLEMENTED() << ": " << static_cast<int>(permission);
+  return content::PermissionController::kNoPendingOperation;
+}
+
+void WebEnginePermissionManager::UnsubscribePermissionStatusChange(
+    int subscription_id) {
+  NOTIMPLEMENTED();
+}
diff --git a/fuchsia/engine/browser/web_engine_permission_manager.h b/fuchsia/engine/browser/web_engine_permission_manager.h
new file mode 100644
index 0000000..d0fa135
--- /dev/null
+++ b/fuchsia/engine/browser/web_engine_permission_manager.h
@@ -0,0 +1,55 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_PERMISSION_MANAGER_H_
+#define FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_PERMISSION_MANAGER_H_
+
+#include "base/macros.h"
+#include "content/public/browser/permission_controller_delegate.h"
+
+class WebEnginePermissionManager
+    : public content::PermissionControllerDelegate {
+ public:
+  WebEnginePermissionManager();
+  ~WebEnginePermissionManager() override;
+
+  // content::PermissionControllerDelegate implementation:
+  int RequestPermission(content::PermissionType permission,
+                        content::RenderFrameHost* render_frame_host,
+                        const GURL& requesting_origin,
+                        bool user_gesture,
+                        base::OnceCallback<void(blink::mojom::PermissionStatus)>
+                            callback) override;
+  int RequestPermissions(
+      const std::vector<content::PermissionType>& permission,
+      content::RenderFrameHost* render_frame_host,
+      const GURL& requesting_origin,
+      bool user_gesture,
+      base::OnceCallback<
+          void(const std::vector<blink::mojom::PermissionStatus>&)> callback)
+      override;
+  void ResetPermission(content::PermissionType permission,
+                       const GURL& requesting_origin,
+                       const GURL& embedding_origin) override;
+  blink::mojom::PermissionStatus GetPermissionStatus(
+      content::PermissionType permission,
+      const GURL& requesting_origin,
+      const GURL& embedding_origin) override;
+  blink::mojom::PermissionStatus GetPermissionStatusForFrame(
+      content::PermissionType permission,
+      content::RenderFrameHost* render_frame_host,
+      const GURL& requesting_origin) override;
+  int SubscribePermissionStatusChange(
+      content::PermissionType permission,
+      content::RenderFrameHost* render_frame_host,
+      const GURL& requesting_origin,
+      base::RepeatingCallback<void(blink::mojom::PermissionStatus)> callback)
+      override;
+  void UnsubscribePermissionStatusChange(int subscription_id) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WebEnginePermissionManager);
+};
+
+#endif  // FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_PERMISSION_MANAGER_H_
diff --git a/fuchsia/runners/cast/cast_component.cc b/fuchsia/runners/cast/cast_component.cc
index d0f0aa7b..00eb1e4 100644
--- a/fuchsia/runners/cast/cast_component.cc
+++ b/fuchsia/runners/cast/cast_component.cc
@@ -57,8 +57,6 @@
   base::AutoReset<bool> constructor_active_reset(&constructor_active_, true);
 
   frame()->SetEnableInput(false);
-  InitializeCastPlatformBindings();
-
   frame()->SetNavigationEventListener(
       navigation_listener_binding_.NewBinding());
   api_bindings_client_->AttachToFrame(
@@ -66,6 +64,8 @@
       base::BindOnce(&CastComponent::DestroyComponent, base::Unretained(this),
                      kBindingsFailureExitCode,
                      fuchsia::sys::TerminationReason::INTERNAL_ERROR));
+
+  InitializeCastPlatformBindings();
 }
 
 CastComponent::~CastComponent() = default;
diff --git a/fuchsia/runners/cast/named_message_port_connector.cc b/fuchsia/runners/cast/named_message_port_connector.cc
index d1f04b4..23e1061 100644
--- a/fuchsia/runners/cast/named_message_port_connector.cc
+++ b/fuchsia/runners/cast/named_message_port_connector.cc
@@ -142,7 +142,6 @@
 
     port_connected_handlers_[port_name].Run(
         std::move(transferable.message_port()));
-
-    ReceiveNextConnectRequest();
   }
+  ReceiveNextConnectRequest();
 }
diff --git a/fuchsia/runners/cast/named_message_port_connector_browsertest.cc b/fuchsia/runners/cast/named_message_port_connector_browsertest.cc
index a68abec..30e702d 100644
--- a/fuchsia/runners/cast/named_message_port_connector_browsertest.cc
+++ b/fuchsia/runners/cast/named_message_port_connector_browsertest.cc
@@ -198,3 +198,48 @@
   connector_->Unregister("port1");
   connector_->Unregister("port2");
 }
+
+// Verifies that 'port1' and 'port2' are delivered via the DefaultHandler
+// callback.
+IN_PROC_BROWSER_TEST_F(NamedMessagePortConnectorTest,
+                       MultiplePortsViaDefaultHandler) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL test_url(
+      embedded_test_server()->GetURL("/connector_multiple_ports.html"));
+
+  fuchsia::web::NavigationControllerPtr controller;
+  frame_->GetNavigationController(controller.NewRequest());
+
+  std::set<std::string> received_ports;
+  base::RunLoop receive_ports_run_loop;
+
+  // Register port1 to ensure that registered and "default handled" ports can
+  // coexist happily.
+  connector_->Register(
+      "port1", base::BindRepeating(
+                   [](fidl::InterfaceHandle<fuchsia::web::MessagePort>) {}));
+
+  // Resume test execution once two ports are received. Since ports are
+  // received in order, if those two ports are "port2" and "port3", then that
+  // means "port1" was handled separately and correctly.
+  connector_->RegisterDefaultHandler(base::BindRepeating(
+      [](std::set<std::string>* received_ports,
+         base::RunLoop* receive_ports_run_loop, base::StringPiece name,
+         fidl::InterfaceHandle<fuchsia::web::MessagePort>) {
+        received_ports->insert(name.as_string());
+
+        if (received_ports->size() == 2)
+          receive_ports_run_loop->Quit();
+      },
+      base::Unretained(&received_ports),
+      base::Unretained(&receive_ports_run_loop)));
+
+  EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
+      controller.get(), fuchsia::web::LoadUrlParams(), test_url.spec()));
+  receive_ports_run_loop.Run();
+
+  EXPECT_TRUE(received_ports.find("port2") != received_ports.end());
+  EXPECT_TRUE(received_ports.find("port3") != received_ports.end());
+
+  connector_->Unregister("port1");
+}
diff --git a/fuchsia/runners/cast/testdata/connector_multiple_ports.html b/fuchsia/runners/cast/testdata/connector_multiple_ports.html
index 69e3a46a..75c7a3e6 100644
--- a/fuchsia/runners/cast/testdata/connector_multiple_ports.html
+++ b/fuchsia/runners/cast/testdata/connector_multiple_ports.html
@@ -4,6 +4,7 @@
     <script>
       var port1 = cast.__platform__.PortConnector.bind('port1');
       var port2 = cast.__platform__.PortConnector.bind('port2');
+      var port3 = cast.__platform__.PortConnector.bind('port3');
 
       function ackMsg(msg) {
         this.postMessage('ack ' + msg.data);
@@ -12,6 +13,7 @@
 
       port1.onmessage = ackMsg.bind(port1);
       port2.onmessage = ackMsg.bind(port2);
+      port3.onmessage = ackMsg.bind(port3);
     </script>
   </body>
 </html>
diff --git a/google_apis/gaia/fake_oauth2_token_service.cc b/google_apis/gaia/fake_oauth2_token_service.cc
index 413a421..0704ab2c 100644
--- a/google_apis/gaia/fake_oauth2_token_service.cc
+++ b/google_apis/gaia/fake_oauth2_token_service.cc
@@ -23,7 +23,7 @@
 
 void FakeOAuth2TokenService::FetchOAuth2Token(
     RequestImpl* request,
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& client_id,
     const std::string& client_secret,
@@ -37,13 +37,6 @@
   pending_requests_.push_back(pending_request);
 }
 
-void FakeOAuth2TokenService::InvalidateAccessTokenImpl(
-    const std::string& account_id,
-    const std::string& client_id,
-    const ScopeSet& scopes,
-    const std::string& access_token) {
-}
-
 void FakeOAuth2TokenService::AddAccount(const std::string& account_id) {
   GetDelegate()->UpdateCredentials(account_id, "fake_refresh_token");
 }
diff --git a/google_apis/gaia/fake_oauth2_token_service.h b/google_apis/gaia/fake_oauth2_token_service.h
index 032a709..1e8f96aa 100644
--- a/google_apis/gaia/fake_oauth2_token_service.h
+++ b/google_apis/gaia/fake_oauth2_token_service.h
@@ -32,7 +32,7 @@
       const std::string& account_id,
       const GoogleServiceAuthError& auth_error);
 
-  void InvalidateTokenForMultilogin(const std::string& account_id,
+  void InvalidateTokenForMultilogin(const CoreAccountId& account_id,
                                     const std::string& token) override {}
 
   FakeOAuth2TokenServiceDelegate* GetFakeOAuth2TokenServiceDelegate();
@@ -41,16 +41,16 @@
   // OAuth2TokenService overrides.
   void FetchOAuth2Token(
       RequestImpl* request,
-      const std::string& account_id,
+      const CoreAccountId& account_id,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& client_id,
       const std::string& client_secret,
       const ScopeSet& scopes) override;
 
-  void InvalidateAccessTokenImpl(const std::string& account_id,
+  void InvalidateAccessTokenImpl(const CoreAccountId& account_id,
                                  const std::string& client_id,
                                  const ScopeSet& scopes,
-                                 const std::string& access_token) override;
+                                 const std::string& access_token) override {}
 
  private:
   struct PendingRequest {
diff --git a/google_apis/gaia/oauth2_token_service.cc b/google_apis/gaia/oauth2_token_service.cc
index 1f449aa..5fdc3b9 100644
--- a/google_apis/gaia/oauth2_token_service.cc
+++ b/google_apis/gaia/oauth2_token_service.cc
@@ -427,7 +427,7 @@
 
 std::unique_ptr<OAuth2TokenService::Request>
 OAuth2TokenService::StartRequestForMultilogin(
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     OAuth2TokenService::Consumer* consumer) {
   const std::string refresh_token =
       delegate_->GetTokenForMultilogin(account_id);
@@ -453,7 +453,7 @@
 }
 
 std::unique_ptr<OAuth2TokenService::Request> OAuth2TokenService::StartRequest(
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     const OAuth2TokenService::ScopeSet& scopes,
     OAuth2TokenService::Consumer* consumer) {
   return StartRequestForClientWithContext(
@@ -464,7 +464,7 @@
 
 std::unique_ptr<OAuth2TokenService::Request>
 OAuth2TokenService::StartRequestForClient(
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     const std::string& client_id,
     const std::string& client_secret,
     const OAuth2TokenService::ScopeSet& scopes,
@@ -481,7 +481,7 @@
 
 std::unique_ptr<OAuth2TokenService::Request>
 OAuth2TokenService::StartRequestWithContext(
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const ScopeSet& scopes,
     Consumer* consumer) {
@@ -493,7 +493,7 @@
 
 std::unique_ptr<OAuth2TokenService::Request>
 OAuth2TokenService::StartRequestForClientWithContext(
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& client_id,
     const std::string& client_secret,
@@ -537,7 +537,7 @@
 
 void OAuth2TokenService::FetchOAuth2Token(
     RequestImpl* request,
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& client_id,
     const std::string& client_secret,
@@ -560,7 +560,7 @@
 }
 
 OAuth2AccessTokenFetcher* OAuth2TokenService::CreateAccessTokenFetcher(
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     OAuth2AccessTokenConsumer* consumer) {
   return delegate_->CreateAccessTokenFetcher(account_id, url_loader_factory,
@@ -590,24 +590,24 @@
 }
 
 bool OAuth2TokenService::RefreshTokenIsAvailable(
-    const std::string& account_id) const {
+    const CoreAccountId& account_id) const {
   return delegate_->RefreshTokenIsAvailable(account_id);
 }
 
 bool OAuth2TokenService::RefreshTokenHasError(
-    const std::string& account_id) const {
+    const CoreAccountId& account_id) const {
   return GetAuthError(account_id) != GoogleServiceAuthError::AuthErrorNone();
 }
 
 GoogleServiceAuthError OAuth2TokenService::GetAuthError(
-    const std::string& account_id) const {
+    const CoreAccountId& account_id) const {
   GoogleServiceAuthError error = delegate_->GetAuthError(account_id);
   DCHECK(!error.IsTransientError());
   return error;
 }
 
 void OAuth2TokenService::InvalidateAccessToken(
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     const ScopeSet& scopes,
     const std::string& access_token) {
   InvalidateAccessTokenImpl(account_id,
@@ -616,7 +616,7 @@
 }
 
 void OAuth2TokenService::InvalidateTokenForMultilogin(
-    const std::string& failed_account,
+    const CoreAccountId& failed_account,
     const std::string& token) {
   OAuth2TokenService::ScopeSet scopes;
   scopes.insert(GaiaConstants::kOAuth1LoginScope);
@@ -629,7 +629,7 @@
 }
 
 void OAuth2TokenService::InvalidateAccessTokenForClient(
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     const std::string& client_id,
     const ScopeSet& scopes,
     const std::string& access_token) {
@@ -637,7 +637,7 @@
 }
 
 void OAuth2TokenService::InvalidateAccessTokenImpl(
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     const std::string& client_id,
     const ScopeSet& scopes,
     const std::string& access_token) {
@@ -735,14 +735,14 @@
   }
   return false;
 }
-void OAuth2TokenService::UpdateAuthError(const std::string& account_id,
+void OAuth2TokenService::UpdateAuthError(const CoreAccountId& account_id,
                                          const GoogleServiceAuthError& error) {
   delegate_->UpdateAuthError(account_id, error);
 }
 
 void OAuth2TokenService::RegisterTokenResponse(
     const std::string& client_id,
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     const ScopeSet& scopes,
     const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -761,7 +761,7 @@
   token_cache_.clear();
 }
 
-void OAuth2TokenService::ClearCacheForAccount(const std::string& account_id) {
+void OAuth2TokenService::ClearCacheForAccount(const CoreAccountId& account_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   for (TokenCache::iterator iter = token_cache_.begin();
        iter != token_cache_.end();
@@ -784,7 +784,7 @@
 }
 
 void OAuth2TokenService::CancelRequestsForAccount(
-    const std::string& account_id) {
+    const CoreAccountId& account_id) {
   std::vector<Fetcher*> fetchers_to_cancel;
   for (const auto& pending_fetcher : pending_fetchers_) {
     if (pending_fetcher.first.account_id == account_id)
@@ -807,7 +807,7 @@
 
 size_t OAuth2TokenService::GetNumPendingRequestsForTesting(
     const std::string& client_id,
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     const ScopeSet& scopes) const {
   auto iter = pending_fetchers_.find(
       OAuth2TokenService::RequestParameters(client_id, account_id, scopes));
diff --git a/google_apis/gaia/oauth2_token_service.h b/google_apis/gaia/oauth2_token_service.h
index 90157f73..56239fbb 100644
--- a/google_apis/gaia/oauth2_token_service.h
+++ b/google_apis/gaia/oauth2_token_service.h
@@ -179,7 +179,7 @@
   // |scopes| is the set of scopes to get an access token for, |consumer| is
   // the object that will be called back with results if the returned request
   // is not deleted. Virtual for mocking.
-  virtual std::unique_ptr<Request> StartRequest(const std::string& account_id,
+  virtual std::unique_ptr<Request> StartRequest(const CoreAccountId& account_id,
                                                 const ScopeSet& scopes,
                                                 Consumer* consumer);
 
@@ -187,14 +187,14 @@
   // empty), return it directly, otherwise start request to get access token.
   // Used for getting tokens to send to Gaia Multilogin endpoint.
   std::unique_ptr<OAuth2TokenService::Request> StartRequestForMultilogin(
-      const std::string& account_id,
+      const CoreAccountId& account_id,
       OAuth2TokenService::Consumer* consumer);
 
   // This method does the same as |StartRequest| except it uses |client_id| and
   // |client_secret| to identify OAuth client app instead of using
   // Chrome's default values.
   std::unique_ptr<Request> StartRequestForClient(
-      const std::string& account_id,
+      const CoreAccountId& account_id,
       const std::string& client_id,
       const std::string& client_secret,
       const ScopeSet& scopes,
@@ -204,7 +204,7 @@
   // URLLoaderfactory given by |url_loader_factory| instead of using the one
   // returned by |GetURLLoaderFactory| implemented by derived classes.
   std::unique_ptr<Request> StartRequestWithContext(
-      const std::string& account_id,
+      const CoreAccountId& account_id,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const ScopeSet& scopes,
       Consumer* consumer);
@@ -220,28 +220,28 @@
   // |StartRequest| will result in a Consumer::OnGetTokenFailure callback.
   // Note: This will return |true| if and only if |account_id| is contained in
   // the list returned by |GetAccounts|.
-  bool RefreshTokenIsAvailable(const std::string& account_id) const;
+  bool RefreshTokenIsAvailable(const CoreAccountId& account_id) const;
 
   // Returns true if a refresh token exists for |account_id| and it is in a
   // persistent error state.
-  bool RefreshTokenHasError(const std::string& account_id) const;
+  bool RefreshTokenHasError(const CoreAccountId& account_id) const;
 
   // Returns the auth error associated with |account_id|. Only persistent errors
   // will be returned.
-  GoogleServiceAuthError GetAuthError(const std::string& account_id) const;
+  GoogleServiceAuthError GetAuthError(const CoreAccountId& account_id) const;
 
   // Mark an OAuth2 |access_token| issued for |account_id| and |scopes| as
   // invalid. This should be done if the token was received from this class,
   // but was not accepted by the server (e.g., the server returned
   // 401 Unauthorized). The token will be removed from the cache for the given
   // scopes.
-  void InvalidateAccessToken(const std::string& account_id,
+  void InvalidateAccessToken(const CoreAccountId& account_id,
                              const ScopeSet& scopes,
                              const std::string& access_token);
 
   // Like |InvalidateToken| except is uses |client_id| to identity OAuth2 client
   // app that issued the request instead of Chrome's default values.
-  void InvalidateAccessTokenForClient(const std::string& account_id,
+  void InvalidateAccessTokenForClient(const CoreAccountId& account_id,
                                       const std::string& client_id,
                                       const ScopeSet& scopes,
                                       const std::string& access_token);
@@ -250,15 +250,14 @@
   // InvalidateTokenForMultilogin method of the delegate. This should be done if
   // the token was received from this class, but was not accepted by the server
   // (e.g., the server returned 401 Unauthorized).
-  virtual void InvalidateTokenForMultilogin(const std::string& failed_account,
+  virtual void InvalidateTokenForMultilogin(const CoreAccountId& failed_account,
                                             const std::string& token);
 
   void set_max_authorization_token_fetch_retries_for_testing(int max_retries);
   // Returns the current number of pending fetchers matching given params.
-  size_t GetNumPendingRequestsForTesting(
-      const std::string& client_id,
-      const std::string& account_id,
-      const ScopeSet& scopes) const;
+  size_t GetNumPendingRequestsForTesting(const std::string& client_id,
+                                         const CoreAccountId& account_id,
+                                         const ScopeSet& scopes) const;
 
   OAuth2TokenServiceDelegate* GetDelegate();
   const OAuth2TokenServiceDelegate* GetDelegate() const;
@@ -293,7 +292,7 @@
   };
 
   // Implement it in delegates if they want to report errors to the user.
-  void UpdateAuthError(const std::string& account_id,
+  void UpdateAuthError(const CoreAccountId& account_id,
                        const GoogleServiceAuthError& error);
 
   // Add a new entry to the cache.
@@ -301,7 +300,7 @@
   // that an access token should ever not be cached.
   virtual void RegisterTokenResponse(
       const std::string& client_id,
-      const std::string& account_id,
+      const CoreAccountId& account_id,
       const ScopeSet& scopes,
       const OAuth2AccessTokenConsumer::TokenResponse& token_response);
 
@@ -311,7 +310,7 @@
   // Clears all of the tokens belonging to |account_id| from the internal token
   // cache. It does not matter what other parameters, like |client_id| were
   // used to request the tokens.
-  void ClearCacheForAccount(const std::string& account_id);
+  void ClearCacheForAccount(const CoreAccountId& account_id);
 
   // Cancels all requests that are currently in progress. Virtual so it can be
   // overridden for tests.
@@ -319,13 +318,13 @@
 
   // Cancels all requests related to a given |account_id|. Virtual so it can be
   // overridden for tests.
-  virtual void CancelRequestsForAccount(const std::string& account_id);
+  virtual void CancelRequestsForAccount(const CoreAccountId& account_id);
 
   // Fetches an OAuth token for the specified client/scopes. Virtual so it can
   // be overridden for tests and for platform-specific behavior.
   virtual void FetchOAuth2Token(
       RequestImpl* request,
-      const std::string& account_id,
+      const CoreAccountId& account_id,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& client_id,
       const std::string& client_secret,
@@ -333,14 +332,14 @@
 
   // Create an access token fetcher for the given account id.
   OAuth2AccessTokenFetcher* CreateAccessTokenFetcher(
-      const std::string& account_id,
+      const CoreAccountId& account_id,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       OAuth2AccessTokenConsumer* consumer);
 
   // Invalidates the |access_token| issued for |account_id|, |client_id| and
   // |scopes|. Virtual so it can be overriden for tests and for platform-
   // specifc behavior.
-  virtual void InvalidateAccessTokenImpl(const std::string& account_id,
+  virtual void InvalidateAccessTokenImpl(const CoreAccountId& account_id,
                                          const std::string& client_id,
                                          const ScopeSet& scopes,
                                          const std::string& access_token);
@@ -380,7 +379,7 @@
   // uses |client_id| and |client_secret| to identify OAuth
   // client app instead of using Chrome's default values.
   std::unique_ptr<Request> StartRequestForClientWithContext(
-      const std::string& account_id,
+      const CoreAccountId& account_id,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& client_id,
       const std::string& client_secret,
diff --git a/google_apis/gaia/oauth2_token_service_request_unittest.cc b/google_apis/gaia/oauth2_token_service_request_unittest.cc
index 509f28ac..264873c 100644
--- a/google_apis/gaia/oauth2_token_service_request_unittest.cc
+++ b/google_apis/gaia/oauth2_token_service_request_unittest.cc
@@ -91,13 +91,13 @@
  protected:
   void FetchOAuth2Token(
       RequestImpl* request,
-      const std::string& account_id,
+      const CoreAccountId& account_id,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& client_id,
       const std::string& client_secret,
       const ScopeSet& scopes) override;
 
-  void InvalidateAccessTokenImpl(const std::string& account_id,
+  void InvalidateAccessTokenImpl(const CoreAccountId& account_id,
                                  const std::string& client_id,
                                  const ScopeSet& scopes,
                                  const std::string& access_token) override;
@@ -133,7 +133,7 @@
 
 void MockOAuth2TokenService::FetchOAuth2Token(
     RequestImpl* request,
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& client_id,
     const std::string& client_secret,
@@ -148,7 +148,7 @@
 }
 
 void MockOAuth2TokenService::InvalidateAccessTokenImpl(
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     const std::string& client_id,
     const ScopeSet& scopes,
     const std::string& access_token) {
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index 28ceb943..9f264b9 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -682,8 +682,8 @@
       },
       "vendor_id": "0x8086",
       "driver_version": {
-        "op": "<",
-        "value": "8.16"
+        "op": "<=",
+        "value": "9.18"
       },
       "features": [
         "disable_d3d11"
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index ebd799b1f..616b8d7 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -1650,15 +1650,6 @@
       client_thread_weak_ptr_, params.swap_id, params.flags, feedback));
 }
 
-void InProcessCommandBuffer::AddFilter(IPC::MessageFilter* message_filter) {
-  NOTREACHED();
-}
-
-int32_t InProcessCommandBuffer::GetRouteID() const {
-  NOTREACHED();
-  return 0;
-}
-
 void InProcessCommandBuffer::DidSwapBuffersCompleteOnOriginThread(
     SwapBuffersCompleteParams params) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
diff --git a/gpu/ipc/in_process_command_buffer.h b/gpu/ipc/in_process_command_buffer.h
index fd2b837..1d48e0e 100644
--- a/gpu/ipc/in_process_command_buffer.h
+++ b/gpu/ipc/in_process_command_buffer.h
@@ -172,8 +172,6 @@
   const gles2::FeatureInfo* GetFeatureInfo() const override;
   const GpuPreferences& GetGpuPreferences() const override;
   void BufferPresented(const gfx::PresentationFeedback& feedback) override;
-  void AddFilter(IPC::MessageFilter* message_filter) override;
-  int32_t GetRouteID() const override;
   viz::GpuVSyncCallback GetGpuVSyncCallback() override;
 
   // Upstream this function to GpuControl if needs arise. Can be called on any
diff --git a/gpu/ipc/service/gles2_command_buffer_stub.cc b/gpu/ipc/service/gles2_command_buffer_stub.cc
index 0c999d8..92cf96b 100644
--- a/gpu/ipc/service/gles2_command_buffer_stub.cc
+++ b/gpu/ipc/service/gles2_command_buffer_stub.cc
@@ -420,14 +420,6 @@
                                                feedback));
 }
 
-void GLES2CommandBufferStub::AddFilter(IPC::MessageFilter* message_filter) {
-  return channel_->AddFilter(message_filter);
-}
-
-int32_t GLES2CommandBufferStub::GetRouteID() const {
-  return route_id_;
-}
-
 viz::GpuVSyncCallback GLES2CommandBufferStub::GetGpuVSyncCallback() {
   return viz::GpuVSyncCallback();
 }
diff --git a/gpu/ipc/service/gles2_command_buffer_stub.h b/gpu/ipc/service/gles2_command_buffer_stub.h
index 8ef5016..166ef98 100644
--- a/gpu/ipc/service/gles2_command_buffer_stub.h
+++ b/gpu/ipc/service/gles2_command_buffer_stub.h
@@ -49,9 +49,6 @@
   const gles2::FeatureInfo* GetFeatureInfo() const override;
   const GpuPreferences& GetGpuPreferences() const override;
   void BufferPresented(const gfx::PresentationFeedback& feedback) override;
-
-  void AddFilter(IPC::MessageFilter* message_filter) override;
-  int32_t GetRouteID() const override;
   viz::GpuVSyncCallback GetGpuVSyncCallback() override;
 
  private:
diff --git a/gpu/ipc/service/gpu_watchdog_thread.cc b/gpu/ipc/service/gpu_watchdog_thread.cc
index abec67d..bee2a68 100644
--- a/gpu/ipc/service/gpu_watchdog_thread.cc
+++ b/gpu/ipc/service/gpu_watchdog_thread.cc
@@ -4,31 +4,20 @@
 
 #include "gpu/ipc/service/gpu_watchdog_thread.h"
 
-#include <errno.h>
-#include <stdint.h>
-
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/command_line.h"
-#include "base/compiler_specific.h"
 #include "base/debug/alias.h"
 #include "base/files/file_util.h"
 #include "base/format_macros.h"
-#include "base/location.h"
-#include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop_current.h"
 #include "base/power_monitor/power_monitor.h"
-#include "base/process/process.h"
 #include "base/single_thread_task_runner.h"
-#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/system/sys_info.h"
-#include "base/threading/platform_thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "gpu/config/gpu_crash_keys.h"
-#include "gpu/config/gpu_finch_features.h"
 
 #if defined(OS_WIN)
 #include <windows.h>
diff --git a/gpu/ipc/service/image_transport_surface_delegate.h b/gpu/ipc/service/image_transport_surface_delegate.h
index 52f9d6f..44430c0 100644
--- a/gpu/ipc/service/image_transport_surface_delegate.h
+++ b/gpu/ipc/service/image_transport_surface_delegate.h
@@ -11,10 +11,6 @@
 #include "gpu/ipc/common/surface_handle.h"
 #include "gpu/ipc/service/gpu_ipc_service_export.h"
 
-namespace IPC {
-class MessageFilter;
-}
-
 namespace gfx {
 struct PresentationFeedback;
 }
@@ -47,10 +43,6 @@
 
   // Tells the delegate a buffer has been presented.
   virtual void BufferPresented(const gfx::PresentationFeedback& feedback) = 0;
-  // Add IPC message filter.
-  virtual void AddFilter(IPC::MessageFilter* message_filter) = 0;
-  // Gets route ID for sending / receiving IPC messages.
-  virtual int32_t GetRouteID() const = 0;
 
   // Callback for GPU vsync signal.  May be called on a different thread.
   virtual viz::GpuVSyncCallback GetGpuVSyncCallback() = 0;
diff --git a/infra/config/commit-queue.cfg b/infra/config/commit-queue.cfg
index c507966..3f0a3acd 100644
--- a/infra/config/commit-queue.cfg
+++ b/infra/config/commit-queue.cfg
@@ -281,12 +281,6 @@
         location_regexp: ".+/[+]/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/.+"
       }
       builders {
-        name: "chromium/try/linux_mojo"
-        location_regexp: ".+/[+]/services/network/.+"
-        location_regexp: ".+/[+]/testing/buildbot/filters/mojo\\.fyi\\.network_.*"
-        location_regexp: ".+/[+]/third_party/blink/web_tests/FlagExpectations/enable-features=NetworkService"
-      }
-      builders {
         name: "chromium/try/linux_optional_gpu_tests_rel"
         location_regexp: ".+/[+]/chrome/browser/vr/.+"
         location_regexp: ".+/[+]/content/test/gpu/.+"
diff --git a/infra/config/cq_cfg_presubmit.py b/infra/config/cq_cfg_presubmit.py
index 38c8ad7c..40c14db 100755
--- a/infra/config/cq_cfg_presubmit.py
+++ b/infra/config/cq_cfg_presubmit.py
@@ -19,7 +19,7 @@
 MD_HEADER = """# List of CQ builders
 
 This page is auto generated using the script
-//infra/config/cq_config_presubmit.py. Do not manually edit.
+//infra/config/cq_cfg_presubmit.py. Do not manually edit.
 
 [TOC]
 
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index c032e84..b21b697 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -1277,6 +1277,8 @@
     builders {
       name: "chromeos-amd64-generic-rel"
       mixins: "chromeos-ci"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
 
     builders {
@@ -1321,6 +1323,8 @@
     builders {
       name: "linux-chromeos-dbg"
       mixins: "chromeos-ci"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
 
     builders {
@@ -1518,6 +1522,8 @@
       name: "Linux Builder (dbg)"
       mixins: "linux-ci-goma-rbe-prod"
       dimensions: "cores:32"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
 
     builders {
@@ -1529,6 +1535,8 @@
     builders {
       name: "Linux Tests (dbg)(1)"
       mixins: "linux-ci"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
 
     builders {
@@ -2659,9 +2667,10 @@
     }
     builders {
       name: "Libfuzzer Upload Linux ASan"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fuzz-ci"
       mixins: "libfuzzer"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "Libfuzzer Upload Linux V8-ARM64 ASan"
@@ -3467,6 +3476,7 @@
     builders {
       mixins: "android-try"
       mixins: "goma-j150"
+      mixins: "builderless"
       name: "android-kitkat-arm-rel"
       dimensions: "os:Ubuntu-16.04"
     }
@@ -3627,16 +3637,37 @@
     }
 
     builders { mixins: "chromeos-try" name: "chromeos-amd64-generic-cfi-thin-lto-rel" }
-    builders { mixins: "chromeos-try" name: "chromeos-amd64-generic-rel" }
-    builders { mixins: "chromeos-try" name: "chromeos-arm-generic-rel" }
+    builders {
+      mixins: "chromeos-try"
+      mixins: "linux-xenial"
+      mixins: "builderless"
+      name: "chromeos-amd64-generic-rel"
+    }
+    builders {
+      mixins: "chromeos-try"
+      mixins: "linux-xenial"
+      mixins: "builderless"
+      name: "chromeos-arm-generic-rel"
+    }
     builders { mixins: "chromeos-try" name: "chromeos-kevin-compile-rel" }
     builders { mixins: "chromeos-try" name: "chromeos-kevin-rel" }
-    builders { mixins: "chromeos-try" name: "linux-chromeos-compile-dbg" }
-    builders { mixins: "chromeos-try" name: "linux-chromeos-dbg" }
+    builders {
+      mixins: "chromeos-try"
+      mixins: "linux-xenial"
+      mixins: "builderless"
+      name: "linux-chromeos-compile-dbg"
+    }
+    builders {
+      mixins: "chromeos-try"
+      mixins: "linux-xenial"
+      mixins: "builderless"
+      name: "linux-chromeos-dbg"
+    }
     builders {
       mixins: "chromeos-try"
       mixins: "linux-xenial"
       mixins: "goma-j150"
+      mixins: "builderless"
       name: "linux-chromeos-rel"
     }
 
@@ -3647,6 +3678,7 @@
       mixins: "linux-try"
       mixins: "linux-xenial"
       name: "chromium_presubmit"
+      mixins: "builderless"
       recipe {
         name: "run_presubmit"
         properties: "repo_name:chromium"
@@ -3742,6 +3774,8 @@
     builders {
       name: "linux-libfuzzer-asan-rel"
       mixins: "linux-try"
+      mixins: "linux-xenial"
+      mixins: "builderless"
       recipe {
         name: "chromium_libfuzzer_trybot"
       }
@@ -3796,9 +3830,16 @@
       mixins: "linux-try"
       mixins: "linux-debug-cache"
       mixins: "goma-j150"
+      mixins: "linux-xenial"
+      mixins: "builderless"
       name: "linux_chromium_compile_dbg_ng"
     }
-    builders { mixins: "linux-try" name: "linux_chromium_compile_rel_ng" }
+    builders {
+      mixins: "linux-try"
+      mixins: "linux-xenial"
+      mixins: "builderless"
+      name: "linux_chromium_compile_rel_ng"
+    }
     builders {
       mixins: "linux-try"
       mixins: "goma-rbe-prod"
@@ -3824,12 +3865,14 @@
       mixins: "linux-try"
       mixins: "linux-xenial"
       mixins: "goma-j150"
+      mixins: "builderless"
       name: "linux-rel"
     }
     builders {
       mixins: "linux-try"
       mixins: "linux-xenial"
       mixins: "goma-j150"
+      mixins: "builderless"
       name: "linux_chromium_tsan_rel_ng"
     }
     builders { mixins: "linux-try" name: "linux_chromium_ubsan_rel_ng" }
diff --git a/infra/scripts/update_dimensions.py b/infra/scripts/update_dimensions.py
new file mode 100755
index 0000000..521993417
--- /dev/null
+++ b/infra/scripts/update_dimensions.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+# 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.
+
+# TODO(yyanagisawa): remove this script after the migration. (crbug.com/954450)
+"""
+Script to change bots dimensions from trusty to xenial (crbug.com/954450).
+It also marks the builders to use builderless bots.
+
+How to use:
+  $ ./update_dimensions.py <cr-buildbucket.cfg> <builder name>
+  $ git cl issue 0
+  $ git cl upload
+
+  e.g.
+  $ ./update_dimensions.py cr-buildbucket.cfg android-marshmallow-arm64-rel
+"""
+
+import cStringIO as StringIO
+import os
+import re
+import subprocess
+import sys
+
+
+BUILDERS_PATTERN = re.compile(r'\s*builders\s*{\s*')
+
+
+def InsertMixin(builder_info, mixin_name):
+  """Insert given mixin_name to info.
+
+  Args:
+    builder_info: contents of builder section.
+    mixin_name: mixin name to be inserted.
+  """
+  for line in builder_info.splitlines():
+    if 'mixin' in line:
+      idx = line.find('mixin')
+  builder_info += '%smixins: "%s"\n' % (' ' * idx, mixin_name)
+  return builder_info
+
+
+def UpdateConfig(config_filename, target_builder):
+  """Update cr-buildbucket.cfg to use xenial instead of trusty.
+
+  Args:
+    config_filename: cr-buildbucket.cfg filename.
+    target_builder: target builder name to update.
+  """
+  in_builders = False
+  depth = 0
+  builder_name_line = 'name: "%s"' % target_builder
+  builder_info = ''
+  should_replace = False
+
+  out = StringIO.StringIO()
+
+  with open(config_filename) as f:
+    for line in f:
+      if BUILDERS_PATTERN.match(line) and not '}' in line:
+        in_builders = True
+        depth = 1
+      elif in_builders and '{' in line:
+        depth += 1
+      elif in_builders and '}' in line:
+        depth -= 1
+      if depth == 0:
+        in_builders = False
+        if should_replace:
+          builder_info = InsertMixin(builder_info, 'builderless')
+          out.write(builder_info.replace('Ubuntu-14.04', 'Ubuntu-16.04'))
+        else:
+          out.write(builder_info)
+        builder_info = ''
+        should_replace = False
+      if in_builders:
+        builder_info += line
+      else:
+        out.write(line)
+
+      if in_builders and builder_name_line in line:
+        should_replace = True
+  with open(config_filename, 'w') as f:
+    f.write(out.getvalue())
+
+
+def main(argv):
+  if len(argv) != 3:
+    raise Exception('unexpected args')
+
+  UpdateConfig(argv[1], argv[2])
+  message = """switch trusty to xenial for %s CQ/CI builder
+
+Bug: 954450""" % argv[2]
+  subprocess.check_call(['git', 'add', os.path.basename(argv[1])],
+                        cwd=os.path.dirname(os.path.abspath(argv[1])))
+  subprocess.check_call(
+      ['git', 'commit', '-m', message],
+      cwd=os.path.dirname(os.path.abspath(argv[1])))
+
+if __name__ == '__main__':
+  main(sys.argv)
diff --git a/ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm b/ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm
index dcbfcdb..aac97fe6 100644
--- a/ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm
+++ b/ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm
@@ -83,13 +83,13 @@
 
   void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
                            const bookmarks::BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const bookmarks::BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override {}
 
   void OnWillRemoveBookmarks(bookmarks::BookmarkModel* model,
                              const bookmarks::BookmarkNode* parent,
-                             int old_index,
+                             size_t old_index,
                              const bookmarks::BookmarkNode* node) override {
     [owner_ removeNodeFromIndex:node];
   }
@@ -105,8 +105,8 @@
 
   void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* parent,
-                         int index) override {
-    [owner_ refreshNodeInIndex:parent->GetChild(index) initial:NO];
+                         size_t index) override {
+    [owner_ refreshNodeInIndex:parent->children()[index].get() initial:NO];
   }
 
   void OnWillChangeBookmarkNode(bookmarks::BookmarkModel* model,
@@ -137,10 +137,11 @@
 
   void BookmarkNodeMoved(bookmarks::BookmarkModel* model,
                          const bookmarks::BookmarkNode* old_parent,
-                         int old_index,
+                         size_t old_index,
                          const bookmarks::BookmarkNode* new_parent,
-                         int new_index) override {
-    [owner_ refreshNodeInIndex:new_parent->GetChild(new_index) initial:NO];
+                         size_t new_index) override {
+    [owner_ refreshNodeInIndex:new_parent->children()[new_index].get()
+                       initial:NO];
   }
 
  private:
diff --git a/ios/chrome/app/strings/resources/ios_strings_iw.xtb b/ios/chrome/app/strings/resources/ios_strings_iw.xtb
index 2a8b9af..87d37df 100644
--- a/ios/chrome/app/strings/resources/ios_strings_iw.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_iw.xtb
@@ -522,7 +522,7 @@
 <translation id="8976382372951310360">עזרה</translation>
 <translation id="8981454092730389528">‏בחירת הפעילויות ב-Google</translation>
 <translation id="8985320356172329008">‏מחובר ל-Google בשם</translation>
-<translation id="9016406938567631235">שולח...</translation>
+<translation id="9016406938567631235">הכרטיסייה נשלחת...</translation>
 <translation id="902659348151742535">‏חברת Google עשויה להשתמש בהיסטוריית הגלישה שלך לצורך התאמה אישית של חיפוש Google, מודעות Google ושירותי Google אחרים.</translation>
 <translation id="9034759925968272072">‏לא תבוצע יציאה מחשבון Google שלך. ייתכן שתוכל לגשת לסוגים אחרים של היסטוריית גלישה בחשבון Google בכתובת <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" />.</translation>
 <translation id="9039373489628511875">רוחב פס</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_zh-TW.xtb b/ios/chrome/app/strings/resources/ios_strings_zh-TW.xtb
index f2cf1c41..11c51daf 100644
--- a/ios/chrome/app/strings/resources/ios_strings_zh-TW.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_zh-TW.xtb
@@ -499,7 +499,7 @@
 <translation id="8532105204136943229">到期年份</translation>
 <translation id="8534481786647257214">已順利發佈到 Google+。</translation>
 <translation id="8548878600947630424">在網頁中尋找...</translation>
-<translation id="8583866649835978847">將 <ph name="TITLE" /> 傳送到你的其中一部裝置。</translation>
+<translation id="8583866649835978847">將「<ph name="TITLE" />」傳送到你的其中一部裝置。</translation>
 <translation id="8605219856220328675">關閉分頁。</translation>
 <translation id="8620640915598389714">編輯</translation>
 <translation id="8636825310635137004">如要存取您在其他裝置上開啟的分頁,請開啟同步處理功能。</translation>
diff --git a/ios/chrome/browser/favicon/favicon_loader.mm b/ios/chrome/browser/favicon/favicon_loader.mm
index c14d7c7c..23cdb0cc 100644
--- a/ios/chrome/browser/favicon/favicon_loader.mm
+++ b/ios/chrome/browser/favicon/favicon_loader.mm
@@ -63,7 +63,9 @@
                                      // GetLargeIconOrFallbackStyle() doesn't
                                      // return valid favicon.
     FaviconAttributesCompletionBlock block) {
-  NSString* key = base::SysUTF8ToNSString(page_url.spec());
+  NSString* key =
+      [NSString stringWithFormat:@"%d %@", (int)round(size_in_points),
+                                 base::SysUTF8ToNSString(page_url.spec())];
   FaviconAttributes* value = [favicon_cache_ objectForKey:key];
   if (value) {
     return value;
@@ -87,6 +89,8 @@
           [FaviconAttributes attributesWithImage:favicon];
       [favicon_cache_ setObject:attributes forKey:key];
       if (block) {
+        DCHECK(favicon.size.width <= size_in_points &&
+               favicon.size.height <= size_in_points);
         block(attributes);
       }
       return;
@@ -147,7 +151,9 @@
     float size_in_points,
     float min_size_in_points,
     FaviconAttributesCompletionBlock block) {
-  NSString* key = base::SysUTF8ToNSString(icon_url.spec());
+  NSString* key =
+      [NSString stringWithFormat:@"%d %@", (int)round(size_in_points),
+                                 base::SysUTF8ToNSString(icon_url.spec())];
   FaviconAttributes* value = [favicon_cache_ objectForKey:key];
   if (value) {
     return value;
diff --git a/ios/chrome/browser/history/history_client_impl.cc b/ios/chrome/browser/history/history_client_impl.cc
index 838ff8a..21dfd83e 100644
--- a/ios/chrome/browser/history/history_client_impl.cc
+++ b/ios/chrome/browser/history/history_client_impl.cc
@@ -73,7 +73,7 @@
 void HistoryClientImpl::BookmarkNodeRemoved(
     bookmarks::BookmarkModel* model,
     const bookmarks::BookmarkNode* parent,
-    int old_index,
+    size_t old_index,
     const bookmarks::BookmarkNode* node,
     const std::set<GURL>& no_longer_bookmarked) {
   if (on_bookmarks_removed_)
diff --git a/ios/chrome/browser/history/history_client_impl.h b/ios/chrome/browser/history/history_client_impl.h
index 10f1338..bd1df9a9 100644
--- a/ios/chrome/browser/history/history_client_impl.h
+++ b/ios/chrome/browser/history/history_client_impl.h
@@ -44,7 +44,7 @@
   void BookmarkModelBeingDeleted(bookmarks::BookmarkModel* model) override;
   void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
                            const bookmarks::BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const bookmarks::BookmarkNode* node,
                            const std::set<GURL>& no_longer_bookmarked) override;
   void BookmarkAllUserNodesRemoved(bookmarks::BookmarkModel* model,
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.mm b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
index 5c7d0f9..ac92066 100644
--- a/ios/chrome/browser/sync/ios_chrome_sync_client.mm
+++ b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
@@ -14,7 +14,6 @@
 #include "base/task/post_task.h"
 #include "components/autofill/core/browser/webdata/autocomplete_sync_bridge.h"
 #include "components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h"
-#include "components/autofill/core/browser/webdata/autofill_profile_syncable_service.h"
 #include "components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.h"
 #include "components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h"
 #include "components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.h"
@@ -221,13 +220,6 @@
       return browser_state_->GetSyncablePrefs()
           ->GetSyncableService(syncer::PRIORITY_PREFERENCES)
           ->AsWeakPtr();
-    case syncer::AUTOFILL_PROFILE: {
-      if (!profile_web_data_service_)
-        return base::WeakPtr<syncer::SyncableService>();
-      return autofill::AutofillProfileSyncableService::FromWebDataService(
-                 profile_web_data_service_.get())
-          ->AsWeakPtr();
-    }
     case syncer::AUTOFILL_WALLET_METADATA: {
       if (!profile_web_data_service_)
         return base::WeakPtr<syncer::SyncableService>();
diff --git a/ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.h b/ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.h
index 478cb836..7f975fd4 100644
--- a/ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.h
+++ b/ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.h
@@ -33,6 +33,7 @@
   GURL GetVirtualURLAtIndex(int i) const override;
   GURL GetFaviconURLAtIndex(int i) const override;
   ui::PageTransition GetTransitionAtIndex(int i) const override;
+  std::string GetPageLanguageAtIndex(int i) const override;
   void GetSerializedNavigationAtIndex(
       int i,
       sessions::SerializedNavigationEntry* serialized_entry) const override;
diff --git a/ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.mm b/ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.mm
index bdc6e44..4d91cf1 100644
--- a/ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.mm
+++ b/ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.mm
@@ -90,6 +90,11 @@
   return item->GetTransitionType();
 }
 
+std::string IOSChromeSyncedTabDelegate::GetPageLanguageAtIndex(int i) const {
+  // TODO(crbug.com/957657): Add page language to NavigationItem.
+  return std::string();
+}
+
 void IOSChromeSyncedTabDelegate::GetSerializedNavigationAtIndex(
     int i,
     sessions::SerializedNavigationEntry* serialized_entry) const {
diff --git a/ios/chrome/browser/ui/alert_coordinator/BUILD.gn b/ios/chrome/browser/ui/alert_coordinator/BUILD.gn
index 5ecde50..3d4366b 100644
--- a/ios/chrome/browser/ui/alert_coordinator/BUILD.gn
+++ b/ios/chrome/browser/ui/alert_coordinator/BUILD.gn
@@ -20,6 +20,7 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
+    "//ios/chrome/browser/ui/dialogs:completion_block_util",
     "//ios/web",
     "//ui/base",
     "//ui/strings",
diff --git a/ios/chrome/browser/ui/alert_coordinator/repost_form_coordinator.mm b/ios/chrome/browser/ui/alert_coordinator/repost_form_coordinator.mm
index d94fbeb..1d54693 100644
--- a/ios/chrome/browser/ui/alert_coordinator/repost_form_coordinator.mm
+++ b/ios/chrome/browser/ui/alert_coordinator/repost_form_coordinator.mm
@@ -7,12 +7,16 @@
 #include "base/logging.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/ui/dialogs/completion_block_util.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+using completion_block_util::DecidePolicyCallback;
+using completion_block_util::GetSafeDecidePolicyCompletion;
+
 @interface RepostFormCoordinator () {
   // WebState which requested this dialog.
   web::WebState* _webState;
@@ -47,15 +51,17 @@
   if (self) {
     _webState = webState;
     CGRect sourceRect = CGRectMake(dialogLocation.x, dialogLocation.y, 1, 1);
+    DecidePolicyCallback safeCallback =
+        GetSafeDecidePolicyCompletion(completionHandler);
     _dialogController =
         [[self class] newDialogControllerForSourceView:viewController.view
                                             sourceRect:sourceRect
-                                     completionHandler:completionHandler];
+                                     completionHandler:safeCallback];
     // The dialog may be dimissed when a new navigation starts while the dialog
     // is still presenting. This should be treated as a NO from user.
     // See https://crbug.com/854750 for a case why this matters.
     _dismissCompletionHandler = ^{
-      completionHandler(NO);
+      safeCallback(NO);
     };
   }
   return self;
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.h b/ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.h
index 2766a4d..18c9e3a 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.h
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.h
@@ -49,15 +49,15 @@
   void BookmarkModelBeingDeleted(BookmarkModel* model) override;
   void BookmarkNodeMoved(BookmarkModel* model,
                          const BookmarkNode* old_parent,
-                         int old_index,
+                         size_t old_index,
                          const BookmarkNode* new_parent,
-                         int new_index) override;
+                         size_t new_index) override;
   void BookmarkNodeAdded(BookmarkModel* model,
                          const BookmarkNode* parent,
-                         int index) override;
+                         size_t index) override;
   void BookmarkNodeRemoved(BookmarkModel* model,
                            const BookmarkNode* parent,
-                           int old_index,
+                           size_t old_index,
                            const BookmarkNode* node,
                            const std::set<GURL>& removed_urls) override;
   void BookmarkNodeChanged(BookmarkModel* model,
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.mm b/ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.mm
index b8a40762..3453705 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.mm
@@ -42,23 +42,23 @@
 
 void BookmarkModelBridge::BookmarkNodeMoved(BookmarkModel* model,
                                             const BookmarkNode* old_parent,
-                                            int old_index,
+                                            size_t old_index,
                                             const BookmarkNode* new_parent,
-                                            int new_index) {
-  const BookmarkNode* node = new_parent->GetChild(new_index);
+                                            size_t new_index) {
+  const BookmarkNode* node = new_parent->children()[new_index].get();
   [observer_ bookmarkNode:node movedFromParent:old_parent toParent:new_parent];
 }
 
 void BookmarkModelBridge::BookmarkNodeAdded(BookmarkModel* model,
                                             const BookmarkNode* parent,
-                                            int index) {
+                                            size_t index) {
   [observer_ bookmarkNodeChildrenChanged:parent];
 }
 
 void BookmarkModelBridge::BookmarkNodeRemoved(
     BookmarkModel* model,
     const BookmarkNode* parent,
-    int old_index,
+    size_t old_index,
     const BookmarkNode* node,
     const std::set<GURL>& removed_urls) {
   [observer_ bookmarkNodeDeleted:node fromFolder:parent];
diff --git a/ios/chrome/browser/ui/dialogs/BUILD.gn b/ios/chrome/browser/ui/dialogs/BUILD.gn
index 1a2ef8cd..60589a8 100644
--- a/ios/chrome/browser/ui/dialogs/BUILD.gn
+++ b/ios/chrome/browser/ui/dialogs/BUILD.gn
@@ -50,8 +50,6 @@
 source_set("dialogs_internal") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
-    "completion_block_util.h",
-    "completion_block_util.mm",
     "dialog_presenter.h",
     "dialog_presenter.mm",
     "java_script_dialog_presenter_impl.h",
@@ -60,6 +58,7 @@
     "nsurl_protection_space_util.mm",
   ]
   deps = [
+    ":completion_block_util",
     ":dialogs",
     "//base",
     "//components/strings",
@@ -76,6 +75,17 @@
   libs = [ "UIKit.framework" ]
 }
 
+source_set("completion_block_util") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "completion_block_util.h",
+    "completion_block_util.mm",
+  ]
+  deps = [
+    "//base",
+  ]
+}
+
 source_set("unit_tests_internal") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
@@ -86,6 +96,7 @@
     "nsurl_protection_space_util_unittest.mm",
   ]
   deps = [
+    ":completion_block_util",
     ":dialogs_internal",
     "//base",
     "//components/strings",
diff --git a/ios/chrome/browser/ui/dialogs/completion_block_util.h b/ios/chrome/browser/ui/dialogs/completion_block_util.h
index fbe1926..e8eba36 100644
--- a/ios/chrome/browser/ui/dialogs/completion_block_util.h
+++ b/ios/chrome/browser/ui/dialogs/completion_block_util.h
@@ -5,7 +5,7 @@
 #ifndef IOS_CHROME_BROWSER_UI_DIALOGS_COMPLETION_BLOCK_UTIL_H_
 #define IOS_CHROME_BROWSER_UI_DIALOGS_COMPLETION_BLOCK_UTIL_H_
 
-#import "ios/web/public/java_script_dialog_callback.h"
+#import <Foundation/Foundation.h>
 
 namespace completion_block_util {
 
@@ -14,6 +14,7 @@
 typedef void (^ConfirmCallback)(BOOL isConfirmed);
 typedef void (^PromptCallback)(NSString* input);
 typedef void (^HTTPAuthCallack)(NSString* user, NSString* password);
+typedef void (^DecidePolicyCallback)(BOOL shouldContinue);
 
 // Completion callbacks provided by web// for dialogs have a built-in
 // mechanism that throws an exception if they are deallocated before being
@@ -25,6 +26,8 @@
     ConfirmCallback callback);
 PromptCallback GetSafeJavaScriptPromptCompletion(PromptCallback callback);
 HTTPAuthCallack GetSafeHTTPAuthCompletion(HTTPAuthCallack callback);
+DecidePolicyCallback GetSafeDecidePolicyCompletion(
+    DecidePolicyCallback callback);
 
 }  // completion_block_util
 
diff --git a/ios/chrome/browser/ui/dialogs/completion_block_util.mm b/ios/chrome/browser/ui/dialogs/completion_block_util.mm
index 06546e1..74e5158 100644
--- a/ios/chrome/browser/ui/dialogs/completion_block_util.mm
+++ b/ios/chrome/browser/ui/dialogs/completion_block_util.mm
@@ -14,6 +14,7 @@
 using completion_block_util::ConfirmCallback;
 using completion_block_util::PromptCallback;
 using completion_block_util::HTTPAuthCallack;
+using completion_block_util::DecidePolicyCallback;
 
 #pragma mark - WebCompletionWrapper
 
@@ -201,6 +202,45 @@
 
 @end
 
+#pragma mark - DecidePolicyCompletionWrapper
+
+// Completion wrapper for decide policy completions.
+@interface DecidePolicyCompletionWrapper : WebCompletionWrapper {
+  DecidePolicyCallback _callback;
+}
+
+// Initializer that takes a JavaScript dialog callback.
+- (instancetype)initWithCallback:(DecidePolicyCallback)callback
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_UNAVAILABLE;
+
+// Executes |_callback| with |shouldContinue|.
+- (void)executeCallbackToConinue:(BOOL)shouldContinue;
+
+@end
+
+@implementation DecidePolicyCompletionWrapper
+
+- (instancetype)initWithCallback:(DecidePolicyCallback)callback {
+  if (self = [super init]) {
+    _callback = callback;
+  }
+  return self;
+}
+
+- (void)executeCompletionForCancellation {
+  [self executeCallbackToConinue:NO];
+}
+
+- (void)executeCallbackToConinue:(BOOL)shouldContinue {
+  if (self.executed || !_callback)
+    return;
+  _callback(shouldContinue);
+  self.executed = YES;
+}
+
+@end
+
 namespace completion_block_util {
 
 AlertCallback GetSafeJavaScriptAlertCompletion(AlertCallback callback) {
@@ -236,4 +276,13 @@
   };
 }
 
+DecidePolicyCallback GetSafeDecidePolicyCompletion(
+    DecidePolicyCallback callback) {
+  DecidePolicyCompletionWrapper* wrapper =
+      [[DecidePolicyCompletionWrapper alloc] initWithCallback:callback];
+  return ^(BOOL shouldContinue) {
+    [wrapper executeCallbackToConinue:shouldContinue];
+  };
+}
+
 }  // completion_block_util
diff --git a/ios/chrome/browser/ui/dialogs/completion_block_util_unittest.mm b/ios/chrome/browser/ui/dialogs/completion_block_util_unittest.mm
index 8532ce0..f117b030 100644
--- a/ios/chrome/browser/ui/dialogs/completion_block_util_unittest.mm
+++ b/ios/chrome/browser/ui/dialogs/completion_block_util_unittest.mm
@@ -16,10 +16,12 @@
 using completion_block_util::ConfirmCallback;
 using completion_block_util::PromptCallback;
 using completion_block_util::HTTPAuthCallack;
+using completion_block_util::DecidePolicyCallback;
 using completion_block_util::GetSafeJavaScriptAlertCompletion;
 using completion_block_util::GetSafeJavaScriptConfirmationCompletion;
 using completion_block_util::GetSafeJavaScriptPromptCompletion;
 using completion_block_util::GetSafeHTTPAuthCompletion;
+using completion_block_util::GetSafeDecidePolicyCompletion;
 
 // Tests that a safe JavaScript alert completion block executes the original
 // callback if deallocated.
@@ -87,3 +89,20 @@
   }
   EXPECT_TRUE(callback_executed);
 }
+
+// Tests that a safe decide policy completion block executes the original
+// callback if deallocated.
+TEST_F(SafeWebCompletionTest, DecidePolicy) {
+  __block BOOL callback_executed = NO;
+  @autoreleasepool {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-variable"
+    DecidePolicyCallback callback =
+        GetSafeDecidePolicyCompletion(^(bool shouldContinue) {
+          EXPECT_FALSE(shouldContinue);
+          callback_executed = YES;
+        });
+#pragma clang diagnostic pop
+  }
+  EXPECT_TRUE(callback_executed);
+}
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm b/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm
index ec064094..765cde2 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm
@@ -196,6 +196,7 @@
   __weak __typeof(self) weakSelf = self;
   self.latestDefaultSearchEngine = defaultProvider;
   auto handleFaviconResult = ^void(FaviconAttributes* faviconCacheResult) {
+    DCHECK_LE(faviconCacheResult.faviconImage.size.width, kOmniboxIconSize);
     if (weakSelf.latestDefaultSearchEngine != defaultProvider ||
         !faviconCacheResult.faviconImage ||
         faviconCacheResult.usesDefaultImage) {
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 8ed23ad..ec5a288 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
@@ -28,6 +28,7 @@
 #include "ios/chrome/grit/ios_strings.h"
 #include "ios/chrome/grit/ios_theme_resources.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
+#import "ios/chrome/test/app/static_html_view_test_util.h"
 #import "ios/chrome/test/earl_grey/accessibility_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
@@ -50,6 +51,8 @@
 #error "This file requires ARC support."
 #endif
 
+using base::test::ios::kWaitForUIElementTimeout;
+
 namespace {
 const char kContentToRemove[] = "Text that distillation should remove.";
 const char kContentToKeep[] = "Text that distillation should keep.";
@@ -396,6 +399,38 @@
       tapToolsMenuButton:grey_accessibilityID(kToolsMenuSiteInformation)];
 }
 
+// Waits for a static html view containing |text|. If the condition is not met
+// within a timeout, a GREYAssert is induced.
+void WaitForStaticHtmlViewContainingText(NSString* text) {
+  bool has_static_view =
+      base::test::ios::WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^{
+        return chrome_test_util::StaticHtmlViewContainingText(
+            chrome_test_util::GetCurrentWebState(),
+            base::SysNSStringToUTF8(text));
+      });
+
+  NSString* error_description = [NSString
+      stringWithFormat:@"Failed to find static html view containing %@", text];
+  GREYAssert(has_static_view, error_description);
+}
+
+// Waits for there to be no static html view, or a static html view that does
+// not contain |text|. If the condition is not met within a timeout, a
+// GREYAssert is induced.
+void WaitForStaticHtmlViewNotContainingText(NSString* text) {
+  bool no_static_view =
+      base::test::ios::WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^{
+        return !chrome_test_util::StaticHtmlViewContainingText(
+            chrome_test_util::GetCurrentWebState(),
+            base::SysNSStringToUTF8(text));
+      });
+
+  NSString* error_description = [NSString
+      stringWithFormat:@"Failed, there was a static html view containing %@",
+                       text];
+  GREYAssert(no_static_view, error_description);
+}
+
 void AssertIsShowingDistillablePageNoNativeContent(
     bool online,
     const GURL& distillable_url) {
@@ -445,8 +480,7 @@
                    }),
                @"Waiting for online page.");
   } else {
-    CHROME_EG_ASSERT_NO_ERROR(
-        [ChromeEarlGrey waitForStaticHTMLViewContainingText:contentToKeep]);
+    WaitForStaticHtmlViewContainingText(contentToKeep);
   }
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxText(
@@ -457,13 +491,11 @@
   if (online) {
     CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
         waitForWebStateContainingText:base::SysNSStringToUTF8(contentToKeep)]);
-    CHROME_EG_ASSERT_NO_ERROR(
-        [ChromeEarlGrey waitForStaticHTMLViewNotContainingText:contentToKeep]);
+    WaitForStaticHtmlViewNotContainingText(contentToKeep);
   } else {
     CHROME_EG_ASSERT_NO_ERROR(
         [ChromeEarlGrey waitForWebStateNotContainingText:kContentToKeep]);
-    CHROME_EG_ASSERT_NO_ERROR(
-        [ChromeEarlGrey waitForStaticHTMLViewContainingText:contentToKeep]);
+    WaitForStaticHtmlViewContainingText(contentToKeep);
   }
 
   // Test the presence of the omnibox offline chip.
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
index 1c181d1..1112383c 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
@@ -6,13 +6,11 @@
 
 #include "base/ios/ios_util.h"
 #include "components/strings/grit/components_strings.h"
-#import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
-#import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_error_util.h"
@@ -211,11 +209,9 @@
 
 // Verifies that copying and pasting a URL includes the hidden protocol prefix.
 - (void)testCopyPasteURL {
-  if (IsRefreshLocationBarEnabled()) {
-    // TODO(crbug.com/834345): Enable this test when long press on the steady
-    // location bar is supported.
-    EARL_GREY_TEST_SKIPPED(@"Test not supported yet in UI Refresh.");
-  }
+  // TODO(crbug.com/834345): Enable this test when long press on the steady
+  // location bar is supported.
+  EARL_GREY_TEST_SKIPPED(@"Test not supported yet in UI Refresh.");
 
   // Clear generalPasteboard before and after the test.
   [UIPasteboard generalPasteboard].string = @"";
diff --git a/ios/chrome/browser/ui/util/ui_util.h b/ios/chrome/browser/ui/util/ui_util.h
index 54608e9..6cef11d4 100644
--- a/ios/chrome/browser/ui/util/ui_util.h
+++ b/ios/chrome/browser/ui/util/ui_util.h
@@ -43,10 +43,6 @@
 // in tab switcher when the last incognito tab is closed.
 bool IsClosingLastIncognitoTabEnabled();
 
-// Returns whether the UI Refresh Location Bar will be used.
-// TODO (crbug.com/884723): Remove all use of this flag.
-bool IsRefreshLocationBarEnabled();
-
 // Returns the approximate corner radius of the current device.
 CGFloat DeviceCornerRadius();
 
diff --git a/ios/chrome/browser/ui/util/ui_util.mm b/ios/chrome/browser/ui/util/ui_util.mm
index f48cda6..bd5b6245 100644
--- a/ios/chrome/browser/ui/util/ui_util.mm
+++ b/ios/chrome/browser/ui/util/ui_util.mm
@@ -64,10 +64,6 @@
   return base::FeatureList::IsEnabled(kClosingLastIncognitoTab);
 }
 
-bool IsRefreshLocationBarEnabled() {
-  return true;
-}
-
 CGFloat DeviceCornerRadius() {
   return IsIPhoneX() ? 40.0 : 0.0;
 }
diff --git a/ios/chrome/browser/web/BUILD.gn b/ios/chrome/browser/web/BUILD.gn
index 3677cad..75c262e 100644
--- a/ios/chrome/browser/web/BUILD.gn
+++ b/ios/chrome/browser/web/BUILD.gn
@@ -381,7 +381,9 @@
   ]
   testonly = true
 
-  sources = []
+  sources = [
+    "error_page_egtest.mm",
+  ]
 
   deps = [
     "//components/content_settings/core/common",
diff --git a/ios/chrome/browser/web/forms_egtest.mm b/ios/chrome/browser/web/forms_egtest.mm
index d9aa8b7..7fe0bb0 100644
--- a/ios/chrome/browser/web/forms_egtest.mm
+++ b/ios/chrome/browser/web/forms_egtest.mm
@@ -12,13 +12,13 @@
 #import "base/test/ios/wait_util.h"
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
-#include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_error_util.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
+#import "ios/chrome/test/scoped_eg_synchronization_disabler.h"
 #import "ios/testing/earl_grey/matchers.h"
 #import "ios/web/public/test/earl_grey/web_view_actions.h"
 #import "ios/web/public/test/earl_grey/web_view_matchers.h"
@@ -206,11 +206,6 @@
 
 // Tests that a POST followed by reloading the destination page resends data.
 - (void)testRepostFormAfterReload {
-  if (IsIPadIdiom() && web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
-    // TODO(crbug.com/968296): Re-enable this test on iPad.
-    EARL_GREY_TEST_DISABLED(@"Test disabled with Slim Navigation.");
-  }
-
   [self setUpFormTestSimpleHttpServer];
   const GURL destinationURL = GetDestinationUrl();
 
@@ -231,7 +226,22 @@
     // loading stops.
     CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey reload]);
   }
-  [self confirmResendWarning];
+
+  {
+    // When slim navigation manager is enabled, synchronization must be disabled
+    // until after the repost confirmation is dismissed because it is presented
+    // during the load. It is always disabled, but immediately re-enabled if
+    // slim navigation manger is not enabled. This is necessary in order to keep
+    // the correct scope of ScopedSynchronizationDisabler which ensures
+    // synchronization is not left disabled if the test fails.
+    std::unique_ptr<ScopedSynchronizationDisabler> disabler =
+        std::make_unique<ScopedSynchronizationDisabler>();
+    if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
+      disabler.reset();
+    }
+
+    [self confirmResendWarning];
+  }
 
   CHROME_EG_ASSERT_NO_ERROR(
       [ChromeEarlGrey waitForWebStateContainingText:kDestinationText]);
@@ -242,11 +252,6 @@
 // Tests that a POST followed by navigating to a new page and then tapping back
 // to the form result page resends data.
 - (void)testRepostFormAfterTappingBack {
-  // TODO(crbug.com/968296): Test is failing on iPad for slim nav.
-  if (IsIPadIdiom() && web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Test disabled on iPad.");
-  }
-
   [self setUpFormTestSimpleHttpServer];
   const GURL destinationURL = GetDestinationUrl();
 
@@ -270,7 +275,22 @@
     [chrome_test_util::BrowserCommandDispatcherForMainBVC() reload];
   }
 
-  [self confirmResendWarning];
+  {
+    // When slim navigation manager is enabled, synchronization must be disabled
+    // until after the repost confirmation is dismissed because it is presented
+    // during the load. It is always disabled, but immediately re-enabled if
+    // slim navigation manger is not enabled. This is necessary in order to keep
+    // the correct scope of ScopedSynchronizationDisabler which ensures
+    // synchronization is not left disabled if the test fails.
+    std::unique_ptr<ScopedSynchronizationDisabler> disabler =
+        std::make_unique<ScopedSynchronizationDisabler>();
+    if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
+      disabler.reset();
+    }
+
+    [self confirmResendWarning];
+  }
+
   CHROME_EG_ASSERT_NO_ERROR(
       [ChromeEarlGrey waitForWebStateContainingText:kDestinationText]);
   [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
@@ -280,11 +300,6 @@
 // Tests that a POST followed by tapping back to the form page and then tapping
 // forward to the result page resends data.
 - (void)testRepostFormAfterTappingBackAndForward {
-  // TODO(crbug.com/968296): Test is failing on iPad for slim nav.
-  if (IsIPadIdiom() && web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Test disabled on iPad.");
-  }
-
   [self setUpFormTestSimpleHttpServer];
   const GURL destinationURL = GetDestinationUrl();
 
@@ -307,7 +322,22 @@
     [chrome_test_util::BrowserCommandDispatcherForMainBVC() reload];
   }
 
-  [self confirmResendWarning];
+  {
+    // When slim navigation manager is enabled, synchronization must be disabled
+    // until after the repost confirmation is dismissed because it is presented
+    // during the load. It is always disabled, but immediately re-enabled if
+    // slim navigation manger is not enabled. This is necessary in order to keep
+    // the correct scope of ScopedSynchronizationDisabler which ensures
+    // synchronization is not left disabled if the test fails.
+    std::unique_ptr<ScopedSynchronizationDisabler> disabler =
+        std::make_unique<ScopedSynchronizationDisabler>();
+    if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
+      disabler.reset();
+    }
+
+    [self confirmResendWarning];
+  }
+
   CHROME_EG_ASSERT_NO_ERROR(
       [ChromeEarlGrey waitForWebStateContainingText:kDestinationText]);
   [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
@@ -373,8 +403,23 @@
     [chrome_test_util::BrowserCommandDispatcherForMainBVC() reload];
   }
 
-  [[EarlGrey selectElementWithMatcher:ElementToDismissAlert(@"Cancel")]
-      performAction:grey_tap()];
+  {
+    // When slim navigation manager is enabled, synchronization must be disabled
+    // until after the repost confirmation is dismissed because it is presented
+    // during the load. It is always disabled, but immediately re-enabled if
+    // slim navigation manger is not enabled. This is necessary in order to keep
+    // the correct scope of ScopedSynchronizationDisabler which ensures
+    // synchronization is not left disabled if the test fails.
+    std::unique_ptr<ScopedSynchronizationDisabler> disabler =
+        std::make_unique<ScopedSynchronizationDisabler>();
+    if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
+      disabler.reset();
+    }
+
+    [[EarlGrey selectElementWithMatcher:ElementToDismissAlert(@"Cancel")]
+        performAction:grey_tap()];
+  }
+
   CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForPageToFinishLoading]);
 
   // Expected behavior is different between the two navigation manager
@@ -425,6 +470,18 @@
     CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey reload]);
   }
 
+  // When slim navigation manager is enabled, synchronization must be disabled
+  // until after the repost confirmation is dismissed because it is presented
+  // during the load. It is always disabled, but immediately re-enabled if
+  // slim navigation manger is not enabled. This is necessary in order to keep
+  // the correct scope of ScopedSynchronizationDisabler which ensures
+  // synchronization is not left disabled if the test fails.
+  std::unique_ptr<ScopedSynchronizationDisabler> disabler =
+      std::make_unique<ScopedSynchronizationDisabler>();
+  if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
+    disabler.reset();
+  }
+
   // Repost confirmation box should be visible.
   CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
       waitForSufficientlyVisibleElementWithMatcher:ResendPostButtonMatcher()]);
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h
index 8bc3949..133097d 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -372,27 +372,6 @@
 // them into the main class declaration as they are converted.
 @interface ChromeEarlGreyImpl (EG1)
 
-#pragma mark - Navigation Utilities
-
-// Waits for a static html view containing |text|. If the condition is not met
-// within a timeout, a GREYAssert is induced.
-// TODO(crbug.com/963613): Change return type to void when
-// CHROME_EG_ASSERT_NO_ERROR is removed.
-- (NSError*)waitForStaticHTMLViewContainingText:(NSString*)text;
-
-// Waits for there to be no static html view, or a static html view that does
-// not contain |text|. If the condition is not met within a timeout, a
-// GREYAssert is induced.
-// TODO(crbug.com/963613): Change return type to void when
-// CHROME_EG_ASSERT_NO_ERROR is removed.
-- (NSError*)waitForStaticHTMLViewNotContainingText:(NSString*)text;
-
-#pragma mark - Sync Utilities
-
-// Injects a bookmark into the fake sync server with |URL| and |title|.
-- (void)injectBookmarkOnFakeSyncServerWithURL:(const std::string&)URL
-                                bookmarkTitle:(const std::string&)title;
-
 @end
 
 #endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_EARL_GREY_H_
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index ec6c24c9..60196ed 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -23,7 +23,6 @@
 #import "ios/chrome/test/app/chrome_test_util.h"                   // nogncheck
 #import "ios/chrome/test/app/history_test_util.h"                  // nogncheck
 #include "ios/chrome/test/app/navigation_test_util.h"              // nogncheck
-#import "ios/chrome/test/app/static_html_view_test_util.h"         // nogncheck
 #import "ios/chrome/test/app/sync_test_util.h"                     // nogncheck
 #import "ios/chrome/test/app/tab_test_util.h"                      // nogncheck
 #import "ios/web/public/deprecated/crw_js_injection_receiver.h"    // nogncheck
@@ -598,42 +597,6 @@
 
 @implementation ChromeEarlGreyImpl (EG1)
 
-#pragma mark - Navigation Utilities
-
-- (NSError*)waitForStaticHTMLViewContainingText:(NSString*)text {
-  bool hasStaticView = WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^{
-    return chrome_test_util::StaticHtmlViewContainingText(
-        chrome_test_util::GetCurrentWebState(), base::SysNSStringToUTF8(text));
-  });
-
-  NSString* errorDescription = [NSString
-      stringWithFormat:@"Failed to find static html view containing %@", text];
-  EG_TEST_HELPER_ASSERT_TRUE(hasStaticView, errorDescription);
-
-  return nil;
-}
-
-- (NSError*)waitForStaticHTMLViewNotContainingText:(NSString*)text {
-  bool noStaticView = WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^{
-    return !chrome_test_util::StaticHtmlViewContainingText(
-        chrome_test_util::GetCurrentWebState(), base::SysNSStringToUTF8(text));
-  });
-
-  NSString* errorDescription = [NSString
-      stringWithFormat:@"Failed, there was a static html view containing %@",
-                       text];
-  EG_TEST_HELPER_ASSERT_TRUE(noStaticView, errorDescription);
-
-  return nil;
-}
-
-#pragma mark - Sync Utilities
-
-- (void)injectBookmarkOnFakeSyncServerWithURL:(const std::string&)URL
-                                bookmarkTitle:(const std::string&)title {
-  chrome_test_util::InjectBookmarkOnFakeSyncServer(URL, title);
-}
-
 @end
 
 #endif  // defined(CHROME_EARL_GREY_1)
diff --git a/ios/web/navigation/crw_wk_navigation_handler.h b/ios/web/navigation/crw_wk_navigation_handler.h
index c143a4f..6f815b8 100644
--- a/ios/web/navigation/crw_wk_navigation_handler.h
+++ b/ios/web/navigation/crw_wk_navigation_handler.h
@@ -93,27 +93,14 @@
             rendererInitiated:(BOOL)renderedInitiated
         placeholderNavigation:(BOOL)placeholderNavigation;
 
-// Instructs the delegate to clear the web frames list.
-- (void)navigationHandlerRemoveAllWebFrames:
-    (CRWWKNavigationHandler*)navigationHandler;
-
 // Instructs the delegate to display the webView.
 - (void)navigationHandlerDisplayWebView:
     (CRWWKNavigationHandler*)navigationHandler;
 
-// Resets any state that is associated with a specific document object (e.g.,
-// page interaction tracking).
-- (void)navigationHandlerResetDocumentSpecificState:
-    (CRWWKNavigationHandler*)navigationHandler;
-
 // Notifies the delegate that the page has actually started loading.
 - (void)navigationHandlerDidStartLoading:
     (CRWWKNavigationHandler*)navigationHandler;
 
-// Notifies the delegate that the web page has changed document and/or URL.
-- (void)navigationHandler:(CRWWKNavigationHandler*)navigationHandler
-    didChangePageWithContext:(web::NavigationContextImpl*)context;
-
 // Instructs the delegate to update the SSL status for the current navigation
 // item.
 - (void)navigationHandlerUpdateSSLStatusForCurrentNavigationItem:
@@ -214,6 +201,13 @@
                         forContext:(std::unique_ptr<web::NavigationContextImpl>)
                                        originalContext;
 
+// Called when the web page has changed document and/or URL, and so the page
+// navigation should be reported to the delegate, and internal state updated to
+// reflect the fact that the navigation has occurred. |context| contains
+// information about the navigation that triggered the document/URL change.
+- (void)webPageChangedWithContext:(web::NavigationContextImpl*)context
+                          webView:(WKWebView*)webView;
+
 @end
 
 #endif  // IOS_WEB_NAVIGATION_CRW_WK_NAVIGATION_HANDLER_H_
diff --git a/ios/web/navigation/crw_wk_navigation_handler.mm b/ios/web/navigation/crw_wk_navigation_handler.mm
index 3e8c9a9..a69f5ad 100644
--- a/ios/web/navigation/crw_wk_navigation_handler.mm
+++ b/ios/web/navigation/crw_wk_navigation_handler.mm
@@ -542,6 +542,8 @@
           web::GetItemWithUniqueID(self.navigationManagerImpl, context);
       if (!IsWKInternalUrl(webViewURL)) {
         if (item) {
+          // Item may not exist if navigation was stopped (see
+          // crbug.com/969915).
           item->SetURL(webViewURL);
         }
         context->SetUrl(webViewURL);
@@ -696,7 +698,7 @@
     }
   }
 
-  [self.delegate navigationHandlerRemoveAllWebFrames:self];
+  [self removeAllWebFrames];
   // This must be reset at the end, since code above may need information about
   // the pending load.
   self.pendingNavigationInfo = nil;
@@ -798,6 +800,8 @@
     if (web::features::StorePendingItemInContext() &&
         navigationManager->GetPendingItemIndex() == -1) {
       if (context->GetItem()) {
+        // Item may not exist if navigation was stopped (see
+        // crbug.com/969915).
         pendingURL = context->GetItem()->GetURL();
       }
     } else {
@@ -827,7 +831,7 @@
 
   [self commitPendingNavigationInfoInWebView:webView];
 
-  [self.delegate navigationHandlerRemoveAllWebFrames:self];
+  [self removeAllWebFrames];
 
   // This point should closely approximate the document object change, so reset
   // the list of injected scripts to those that are automatically injected.
@@ -870,7 +874,7 @@
       }
       DCHECK(found_correct_navigation_item);
     }
-    [self.delegate navigationHandlerResetDocumentSpecificState:self];
+    [self resetDocumentSpecificState];
     [self.delegate navigationHandlerDidStartLoading:self];
   } else if (context) {
     // If |navigation| is nil (which happens for windows open by DOM), then it
@@ -881,7 +885,7 @@
     if (isLastNavigation ||
         (web::features::StorePendingItemInContext() &&
          self.navigationManagerImpl->GetPendingItemIndex() == -1)) {
-      [self.delegate navigationHandler:self didChangePageWithContext:context];
+      [self webPageChangedWithContext:context webView:webView];
     } else if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
       // WKWebView has more than one in progress navigation, and committed
       // navigation was not the latest. Change last committed item to one that
@@ -970,6 +974,7 @@
   web::NavigationItemImpl* item =
       context ? web::GetItemWithUniqueID(self.navigationManagerImpl, context)
               : nullptr;
+  // Item may not exist if navigation was stopped (see crbug.com/969915).
 
   // Invariant: every |navigation| should have a |context| and a |item|.
   // TODO(crbug.com/899383) Fix invariant violation when a new pending item is
@@ -984,12 +989,9 @@
     }
   }
   DCHECK(context);
-  DCHECK(item);
   UMA_HISTOGRAM_BOOLEAN("IOS.FinishedNavigationHasContext", context);
   UMA_HISTOGRAM_BOOLEAN("IOS.FinishedNavigationHasItem", item);
 
-  // TODO(crbug.com/864769): Remove this guard after fixing root cause of
-  // invariant violation in production.
   if (context && item) {
     GURL navigationURL = context->IsPlaceholderNavigation()
                              ? CreatePlaceholderUrlForUrl(context->GetUrl())
@@ -1089,7 +1091,7 @@
           forNavigation:navigation
                 webView:webView
         provisionalLoad:NO];
-  [self.delegate navigationHandlerRemoveAllWebFrames:self];
+  [self removeAllWebFrames];
   _certVerificationErrors->Clear();
   [self forgetNullWKNavigation:navigation];
 }
@@ -1142,6 +1144,7 @@
 
   _certVerificationErrors->Clear();
   self.webProcessCrashed = YES;
+  [self removeAllWebFrames];
 
   [self.delegate navigationHandlerWebProcessDidCrash:self];
 }
@@ -1966,6 +1969,23 @@
   self.webStateImpl->OnPageLoaded(failingURL, NO);
 }
 
+// Clears the frames list.
+- (void)removeAllWebFrames {
+  web::WebFramesManagerImpl* framesManager =
+      web::WebFramesManagerImpl::FromWebState(self.webStateImpl);
+  for (auto* frame : framesManager->GetAllWebFrames()) {
+    self.webStateImpl->OnWebFrameUnavailable(frame);
+  }
+  framesManager->RemoveAllWebFrames();
+}
+
+// Resets any state that is associated with a specific document object (e.g.,
+// page interaction tracking).
+- (void)resetDocumentSpecificState {
+  self.userInteractionState->SetLastUserInteraction(nullptr);
+  self.userInteractionState->SetTapInProgress(false);
+}
+
 #pragma mark - Public methods
 
 - (void)stopLoading {
@@ -2194,4 +2214,41 @@
   return [self.navigationStates contextForNavigation:navigation];
 }
 
+- (void)webPageChangedWithContext:(web::NavigationContextImpl*)context
+                          webView:(WKWebView*)webView {
+  web::Referrer referrer = self.currentReferrer;
+  // If no referrer was known in advance, record it now. (If there was one,
+  // keep it since it will have a more accurate URL and policy than what can
+  // be extracted from the landing page.)
+  web::NavigationItem* currentItem = self.currentNavItem;
+
+  // TODO(crbug.com/925304): Pending item (which should be used here) should be
+  // owned by NavigationContext object. Pending item should never be null.
+  if (currentItem && !currentItem->GetReferrer().url.is_valid()) {
+    currentItem->SetReferrer(referrer);
+  }
+
+  // TODO(crbug.com/956511): This shouldn't be called for hash state or
+  // push/replaceState.
+  [self resetDocumentSpecificState];
+
+  [self.delegate navigationHandlerDidStartLoading:self];
+  // Do not commit pending item in the middle of loading a placeholder URL. The
+  // item will be committed when the native content or webUI is displayed.
+  if (!context->IsPlaceholderNavigation()) {
+    self.navigationManagerImpl->CommitPendingItem(context->ReleaseItem());
+    if (web::features::StorePendingItemInContext() &&
+        context->IsLoadingHtmlString()) {
+      self.navigationManagerImpl->GetLastCommittedItem()->SetURL(
+          context->GetUrl());
+    }
+    // If a SafeBrowsing warning is currently displayed, the user has tapped
+    // the button on the warning page to proceed to the site, the site has
+    // started loading, and the warning is about to be removed. In this case,
+    // the transient item for the warning needs to be removed too.
+    if (web::IsSafeBrowsingWarningDisplayedInWebView(webView))
+      self.navigationManagerImpl->DiscardNonCommittedItems();
+  }
+}
+
 @end
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 196e05a..cff9892 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -354,8 +354,6 @@
 - (void)frameBecameAvailableWithMessage:(WKScriptMessage*)message;
 // Handles frame became unavailable message.
 - (void)frameBecameUnavailableWithMessage:(WKScriptMessage*)message;
-// Clears the frames list.
-- (void)removeAllWebFrames;
 
 // Restores the state for this page from session history.
 - (void)restoreStateFromHistory;
@@ -891,9 +889,25 @@
 
 - (void)stopLoading {
   base::RecordAction(base::UserMetricsAction("Stop"));
-  // Discard the pending and transient entried before notifying the tab model
-  // observers of the change.
+  // Discard all pending and transient items before notifying WebState observers
   self.navigationManagerImpl->DiscardNonCommittedItems();
+  if (web::features::StorePendingItemInContext()) {
+    for (__strong id navigation in
+         [self.navigationHandler.navigationStates pendingNavigations]) {
+      if (navigation == [NSNull null]) {
+        // null is a valid navigation object passed to WKNavigationDelegate
+        // callbacks and represents window opening action.
+        navigation = nil;
+      }
+      // This will remove pending item for navigations which may still call
+      // WKNavigationDelegate callbacks see (crbug.com/969915).
+      web::NavigationContextImpl* context =
+          [self.navigationHandler.navigationStates
+              contextForNavigation:navigation];
+      context->ReleaseItem();
+    }
+  }
+
   [self.webView stopLoading];
   [self.navigationHandler stopLoading];
   [self.legacyNativeController stopLoading];
@@ -2227,15 +2241,6 @@
   }
 }
 
-- (void)removeAllWebFrames {
-  web::WebFramesManagerImpl* framesManager =
-      web::WebFramesManagerImpl::FromWebState([self webState]);
-  for (auto* frame : framesManager->GetAllWebFrames()) {
-    self.webStateImpl->OnWebFrameUnavailable(frame);
-  }
-  framesManager->RemoveAllWebFrames();
-}
-
 #pragma mark - JavaScript message handlers
 // Handlers for JavaScript messages. |message| contains a JavaScript command and
 // data relevant to the message, and |context| contains contextual information
@@ -3044,56 +3049,6 @@
 
 #pragma mark - WKNavigationDelegate Helpers
 
-// Called when the web page has changed document and/or URL, and so the page
-// navigation should be reported to the delegate, and internal state updated to
-// reflect the fact that the navigation has occurred. |context| contains
-// information about the navigation that triggered the document/URL change.
-// TODO(stuartmorgan): This method conflates document changes and URL changes;
-// we should be distinguishing better, and be clear about the expected
-// WebDelegate and WCO callbacks in each case.
-- (void)webPageChangedWithContext:(web::NavigationContextImpl*)context {
-  web::Referrer referrer = self.navigationHandler.currentReferrer;
-  // If no referrer was known in advance, record it now. (If there was one,
-  // keep it since it will have a more accurate URL and policy than what can
-  // be extracted from the landing page.)
-  web::NavigationItem* currentItem = self.currentNavItem;
-
-  // TODO(crbug.com/925304): Pending item (which should be used here) should be
-  // owned by NavigationContext object. Pending item should never be null.
-  if (currentItem && !currentItem->GetReferrer().url.is_valid()) {
-    currentItem->SetReferrer(referrer);
-  }
-
-  // TODO(stuartmorgan): This shouldn't be called for hash state or
-  // push/replaceState.
-  [self resetDocumentSpecificState];
-
-  [self didStartLoading];
-  // Do not commit pending item in the middle of loading a placeholder URL. The
-  // item will be committed when the native content or webUI is displayed.
-  if (!context->IsPlaceholderNavigation()) {
-    self.navigationManagerImpl->CommitPendingItem(context->ReleaseItem());
-    if (web::features::StorePendingItemInContext() &&
-        context->IsLoadingHtmlString()) {
-      self.navigationManagerImpl->GetLastCommittedItem()->SetURL(
-          context->GetUrl());
-    }
-    // If a SafeBrowsing warning is currently displayed, the user has tapped
-    // the button on the warning page to proceed to the site, the site has
-    // started loading, and the warning is about to be removed. In this case,
-    // the transient item for the warning needs to be removed too.
-    if (web::IsSafeBrowsingWarningDisplayedInWebView(self.webView))
-      self.navigationManagerImpl->DiscardNonCommittedItems();
-  }
-}
-
-// Resets any state that is associated with a specific document object (e.g.,
-// page interaction tracking).
-- (void)resetDocumentSpecificState {
-  _userInteractionState.SetLastUserInteraction(nullptr);
-  _userInteractionState.SetTapInProgress(false);
-}
-
 // Called when a page (native or web) has actually started loading (i.e., for
 // a web page the document has actually changed), or after the load request has
 // been registered for a non-document-changing URL change. Updates internal
@@ -3264,7 +3219,8 @@
                            hasUserGesture:NO
                         rendererInitiated:YES
                     placeholderNavigation:IsPlaceholderUrl(webViewURL)];
-      [self webPageChangedWithContext:newContext.get()];
+      [self.navigationHandler webPageChangedWithContext:newContext.get()
+                                                webView:self.webView];
       newContext->SetHasCommitted(!isSameDocumentNavigation);
       self.webStateImpl->OnNavigationFinished(newContext.get());
       // TODO(crbug.com/792515): It is OK, but very brittle, to call
@@ -3284,7 +3240,8 @@
       existingContext->SetIsSameDocument(isSameDocumentNavigation);
       existingContext->SetHasCommitted(!isSameDocumentNavigation);
       self.webStateImpl->OnNavigationStarted(existingContext);
-      [self webPageChangedWithContext:existingContext];
+      [self.navigationHandler webPageChangedWithContext:existingContext
+                                                webView:self.webView];
       self.webStateImpl->OnNavigationFinished(existingContext);
     }
   }
@@ -3679,31 +3636,16 @@
                    placeholderNavigation:placeholderNavigation];
 }
 
-- (void)navigationHandlerRemoveAllWebFrames:
-    (CRWWKNavigationHandler*)navigationHandler {
-  [self removeAllWebFrames];
-}
-
 - (void)navigationHandlerDisplayWebView:
     (CRWWKNavigationHandler*)navigationHandler {
   [self displayWebView];
 }
 
-- (void)navigationHandlerResetDocumentSpecificState:
-    (CRWWKNavigationHandler*)navigationHandler {
-  [self resetDocumentSpecificState];
-}
-
 - (void)navigationHandlerDidStartLoading:
     (CRWWKNavigationHandler*)navigationHandler {
   [self didStartLoading];
 }
 
-- (void)navigationHandler:(CRWWKNavigationHandler*)navigationHandler
-    didChangePageWithContext:(web::NavigationContextImpl*)context {
-  [self webPageChangedWithContext:context];
-}
-
 - (void)navigationHandlerUpdateSSLStatusForCurrentNavigationItem:
     (CRWWKNavigationHandler*)navigationHandler {
   [self updateSSLStatusForCurrentNavigationItem];
@@ -3721,7 +3663,6 @@
 
 - (void)navigationHandlerWebProcessDidCrash:
     (CRWWKNavigationHandler*)navigationHandler {
-  [self removeAllWebFrames];
   // On iOS 11 WKWebView does not repaint after crash and reload. Recreating
   // web view fixes the issue. TODO(crbug.com/770914): Remove this workaround
   // once rdar://35063950 is fixed.
diff --git a/ios/web/web_state/web_state_observer_inttest.mm b/ios/web/web_state/web_state_observer_inttest.mm
index 359dbb2ec..e1fad7c 100644
--- a/ios/web/web_state/web_state_observer_inttest.mm
+++ b/ios/web/web_state/web_state_observer_inttest.mm
@@ -214,18 +214,10 @@
   EXPECT_FALSE((*context)->IsPost());
   EXPECT_FALSE((*context)->GetError());
   EXPECT_FALSE((*context)->IsRendererInitiated());
-  ASSERT_FALSE((*context)->GetResponseHeaders());
-  ASSERT_FALSE(web_state->IsLoading());
-  NavigationItem* pendig_item =
-      web_state->GetNavigationManager()->GetPendingItem();
-  if (web::features::StorePendingItemInContext()) {
-    ASSERT_TRUE(pendig_item);
-    EXPECT_EQ(url, pendig_item->GetURL());
-  } else {
-    // TODO(crbug.com/899827): Pending URL should exist and be owned by
-    // NavigationContext.
-    ASSERT_FALSE(pendig_item);
-  }
+  EXPECT_FALSE((*context)->GetResponseHeaders());
+  EXPECT_FALSE(web_state->IsLoading());
+  // Pending Item was removed by Stop call (see crbug.com/969915).
+  EXPECT_FALSE(web_state->GetNavigationManager()->GetPendingItem());
 }
 
 // Verifies correctness of |NavigationContext| (|arg1|) for new page navigation
@@ -2171,6 +2163,7 @@
   test::LoadUrl(web_state(), test_server_->GetURL("/hung"));
   web_state()->Stop();
   ASSERT_TRUE(test::WaitForPageToFinishLoading(web_state()));
+  EXPECT_EQ("", web_state()->GetVisibleURL());
 }
 
 // Tests stopping a navigation after allowing the navigation from
diff --git a/ios/web_view/internal/sync/web_view_sync_client.mm b/ios/web_view/internal/sync/web_view_sync_client.mm
index 100f80f..568be01 100644
--- a/ios/web_view/internal/sync/web_view_sync_client.mm
+++ b/ios/web_view/internal/sync/web_view_sync_client.mm
@@ -11,7 +11,6 @@
 #include "base/logging.h"
 #include "base/task/post_task.h"
 #include "components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h"
-#include "components/autofill/core/browser/webdata/autofill_profile_syncable_service.h"
 #include "components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/autofill/core/common/autofill_features.h"
@@ -181,10 +180,6 @@
     return base::WeakPtr<syncer::SyncableService>();
   }
   switch (type) {
-    case syncer::AUTOFILL_PROFILE:
-      return autofill::AutofillProfileSyncableService::FromWebDataService(
-                 service.get())
-          ->AsWeakPtr();
     case syncer::AUTOFILL_WALLET_METADATA:
       return autofill::AutofillWalletMetadataSyncableService::
           FromWebDataService(service.get())
diff --git a/media/audio/audio_output_controller_unittest.cc b/media/audio/audio_output_controller_unittest.cc
index 55e0b08..5ddcd30 100644
--- a/media/audio/audio_output_controller_unittest.cc
+++ b/media/audio/audio_output_controller_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_piece.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/test_message_loop.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
@@ -29,20 +30,21 @@
 #include "media/audio/test_audio_thread.h"
 #include "media/base/audio_bus.h"
 #include "media/base/audio_parameters.h"
-#include "media/base/gmock_callback_support.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::base::test::RunClosure;
+using ::base::test::RunOnceClosure;
 using ::testing::_;
 using ::testing::AtLeast;
 using ::testing::Bool;
-using ::testing::TestWithParam;
 using ::testing::DoAll;
 using ::testing::Invoke;
+using ::testing::Mock;
 using ::testing::NotNull;
 using ::testing::Return;
 using ::testing::StrictMock;
-using ::testing::Mock;
+using ::testing::TestWithParam;
 
 namespace media {
 
diff --git a/media/audio/mac/audio_low_latency_input_mac.cc b/media/audio/mac/audio_low_latency_input_mac.cc
index b8e4e68..7d55649 100644
--- a/media/audio/mac/audio_low_latency_input_mac.cc
+++ b/media/audio/mac/audio_low_latency_input_mac.cc
@@ -232,7 +232,7 @@
       glitches_detected_(0),
       log_callback_(log_callback) {
   DCHECK(manager_);
-  CHECK(!log_callback_.Equals(AudioManager::LogCallback()));
+  CHECK(log_callback_ != AudioManager::LogCallback());
   if (use_voice_processing_) {
     DCHECK(input_params.channels() == 1 || input_params.channels() == 2);
     const bool got_default_device =
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn
index 2ee16b0d..2ed4f7a 100644
--- a/media/base/BUILD.gn
+++ b/media/base/BUILD.gn
@@ -433,7 +433,6 @@
     "fake_single_thread_task_runner.h",
     "fake_text_track_stream.cc",
     "fake_text_track_stream.h",
-    "gmock_callback_support.h",
     "mock_audio_renderer_sink.cc",
     "mock_audio_renderer_sink.h",
     "mock_demuxer_host.cc",
@@ -508,7 +507,6 @@
     "fake_demuxer_stream_unittest.cc",
     "fallback_video_decoder_unittest.cc",
     "feedback_signal_accumulator_unittest.cc",
-    "gmock_callback_support_unittest.cc",
     "key_systems_unittest.cc",
     "media_log_unittest.cc",
     "media_url_demuxer_unittest.cc",
diff --git a/media/base/fallback_video_decoder_unittest.cc b/media/base/fallback_video_decoder_unittest.cc
index 8f5725d..e168be02 100644
--- a/media/base/fallback_video_decoder_unittest.cc
+++ b/media/base/fallback_video_decoder_unittest.cc
@@ -7,10 +7,10 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/fallback_video_decoder.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/mock_filters.h"
 #include "media/base/test_helpers.h"
 #include "media/base/video_decoder.h"
@@ -19,8 +19,9 @@
 #include "testing/gtest/include/gtest/gtest-param-test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using ::testing::StrictMock;
+using ::base::test::RunOnceCallback;
 using ::testing::_;
+using ::testing::StrictMock;
 
 namespace media {
 
diff --git a/media/base/null_video_sink_unittest.cc b/media/base/null_video_sink_unittest.cc
index 4b81e14..ce91e58 100644
--- a/media/base/null_video_sink_unittest.cc
+++ b/media/base/null_video_sink_unittest.cc
@@ -7,15 +7,16 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/macros.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/null_video_sink.h"
 #include "media/base/test_helpers.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using base::test::RunClosure;
 using testing::_;
 using testing::DoAll;
 using testing::Return;
diff --git a/media/base/pipeline_impl_unittest.cc b/media/base/pipeline_impl_unittest.cc
index 3c48a46..bd28653 100644
--- a/media/base/pipeline_impl_unittest.cc
+++ b/media/base/pipeline_impl_unittest.cc
@@ -15,13 +15,13 @@
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/threading/simple_thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/clock.h"
 #include "media/base/fake_text_track_stream.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/media_util.h"
 #include "media/base/mock_filters.h"
 #include "media/base/test_helpers.h"
@@ -32,6 +32,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/size.h"
 
+using ::base::test::RunCallback;
+using ::base::test::RunClosure;
 using ::testing::_;
 using ::testing::AnyNumber;
 using ::testing::CreateFunctor;
diff --git a/media/base/video_thumbnail_decoder_unittest.cc b/media/base/video_thumbnail_decoder_unittest.cc
index 75a1b9be..9468987 100644
--- a/media/base/video_thumbnail_decoder_unittest.cc
+++ b/media/base/video_thumbnail_decoder_unittest.cc
@@ -8,8 +8,8 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/media_util.h"
 #include "media/base/mock_filters.h"
 #include "media/base/video_decoder_config.h"
@@ -18,6 +18,8 @@
 #include "media/base/video_types.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::base::test::RunCallback;
+using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::DoAll;
 
diff --git a/media/blink/video_frame_compositor_unittest.cc b/media/blink/video_frame_compositor_unittest.cc
index 7eb7631..47332d9 100644
--- a/media/blink/video_frame_compositor_unittest.cc
+++ b/media/blink/video_frame_compositor_unittest.cc
@@ -6,16 +6,17 @@
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/video_frame.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/web_video_frame_submitter.h"
 
+using base::test::RunClosure;
 using testing::_;
 using testing::AnyNumber;
 using testing::DoAll;
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 0e80be5..0e361baf 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -862,10 +862,6 @@
   if (watch_time_reporter_)
     watch_time_reporter_->OnSeeking();
 
-  // Clear any new frame processed callbacks on seek; otherwise we'll end up
-  // logging a time long after the seek completes.
-  frame_time_report_cb_.Cancel();
-
   // TODO(sandersd): Move |seeking_| to PipelineController.
   // TODO(sandersd): Do we want to reset the idle timer here?
   delegate_->SetIdle(delegate_id_, false);
@@ -1787,10 +1783,6 @@
   ended_ = true;
   client_->TimeChanged();
 
-  // Clear any new frame processed callbacks on end; otherwise we'll end up
-  // logging a time long after playback ends.
-  frame_time_report_cb_.Cancel();
-
   // We don't actually want this to run until |client_| calls seek() or pause(),
   // but that should have already happened in timeChanged() and so this is
   // expected to be a no-op.
@@ -2336,21 +2328,6 @@
       base::BindOnce(&VideoFrameCompositor::SetIsPageVisible,
                      base::Unretained(compositor_.get()), !IsHidden()));
 
-  // Only track the time to the first frame if playing or about to play because
-  // of being shown and only for videos we would optimize background playback
-  // for.
-  if ((!paused_ && IsBackgroundOptimizationCandidate()) ||
-      paused_when_hidden_) {
-    frame_time_report_cb_.Reset(base::BindOnce(
-        &WebMediaPlayerImpl::ReportTimeFromForegroundToFirstFrame, weak_this_,
-        base::TimeTicks::Now()));
-    vfc_task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(&VideoFrameCompositor::SetOnNewProcessedFrameCallback,
-                       base::Unretained(compositor_.get()),
-                       BindToCurrentLoop(frame_time_report_cb_.callback())));
-  }
-
   UpdateBackgroundVideoOptimizationState();
 
   if (paused_when_hidden_) {
@@ -3400,20 +3377,6 @@
       pipeline_controller_->GetMediaDuration());
 }
 
-void WebMediaPlayerImpl::ReportTimeFromForegroundToFirstFrame(
-    base::TimeTicks foreground_time,
-    base::TimeTicks new_frame_time) {
-  base::TimeDelta time_to_first_frame = new_frame_time - foreground_time;
-  if (HasAudio()) {
-    UMA_HISTOGRAM_TIMES(
-        "Media.Video.TimeFromForegroundToFirstFrame.DisableTrack",
-        time_to_first_frame);
-  } else {
-    UMA_HISTOGRAM_TIMES("Media.Video.TimeFromForegroundToFirstFrame.Paused",
-                        time_to_first_frame);
-  }
-}
-
 void WebMediaPlayerImpl::SwitchToRemoteRenderer(
     const std::string& remote_device_friendly_name) {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index 0d6811b..f747f7d4 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -522,9 +522,6 @@
   // Return the pipeline media duration or the value overridden by tests.
   base::TimeDelta GetPipelineMediaDuration() const;
 
-  void ReportTimeFromForegroundToFirstFrame(base::TimeTicks foreground_time,
-                                            base::TimeTicks new_frame_time);
-
   // Records |duration| to the appropriate metric based on whether we're
   // handling a src= or MSE based playback.
   void RecordUnderflowDuration(base::TimeDelta duration);
@@ -915,8 +912,6 @@
 
   CreateSurfaceLayerBridgeCB create_bridge_callback_;
 
-  base::CancelableOnceCallback<void(base::TimeTicks)> frame_time_report_cb_;
-
   bool initial_video_height_recorded_ = false;
 
   enum class OverlayMode {
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc
index 1519cb1..cada879a 100644
--- a/media/blink/webmediaplayer_impl_unittest.cc
+++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task_runner_util.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_tick_clock.h"
@@ -26,7 +27,6 @@
 #include "cc/layers/layer.h"
 #include "components/viz/test/test_context_provider.h"
 #include "media/base/decoder_buffer.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/media_log.h"
 #include "media/base/media_switches.h"
 #include "media/base/mock_audio_renderer_sink.h"
@@ -64,6 +64,7 @@
 #include "third_party/blink/public/web/web_widget.h"
 #include "url/gurl.h"
 
+using ::base::test::RunClosure;
 using ::testing::_;
 using ::testing::AnyNumber;
 using ::testing::Eq;
diff --git a/media/capture/video/chromeos/OWNERS b/media/capture/video/chromeos/OWNERS
index 12ac582..41a845c 100644
--- a/media/capture/video/chromeos/OWNERS
+++ b/media/capture/video/chromeos/OWNERS
@@ -1,4 +1,6 @@
+set noparent
+
 jcliang@chromium.org
-posciak@chromium.org
 shik@chromium.org
 hywu@chromium.org
+wtlee@chromium.org
diff --git a/media/capture/video/chromeos/request_manager.cc b/media/capture/video/chromeos/request_manager.cc
index 3f3331b..3595473 100644
--- a/media/capture/video/chromeos/request_manager.cc
+++ b/media/capture/video/chromeos/request_manager.cc
@@ -5,6 +5,7 @@
 #include "media/capture/video/chromeos/request_manager.h"
 
 #include <sync/sync.h>
+
 #include <initializer_list>
 #include <map>
 #include <set>
@@ -647,7 +648,7 @@
           FROM_HERE, "Failed to unwrap release fence fd");
       return;
     }
-    if (!sync_wait(fence.GetFD().get(), kSyncWaitTimeoutMs)) {
+    if (sync_wait(fence.GetFD().get(), kSyncWaitTimeoutMs)) {
       device_context_->SetErrorState(
           media::VideoCaptureError::
               kCrosHalV3BufferManagerSyncWaitOnReleaseFenceTimedOut,
diff --git a/media/filters/audio_decoder_stream_unittest.cc b/media/filters/audio_decoder_stream_unittest.cc
index 303e24d..d64bb1d 100644
--- a/media/filters/audio_decoder_stream_unittest.cc
+++ b/media/filters/audio_decoder_stream_unittest.cc
@@ -8,15 +8,17 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/media_util.h"
 #include "media/base/mock_filters.h"
 #include "media/filters/decoder_stream.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::base::test::RunCallback;
+using ::base::test::RunOnceCallback;
 using testing::_;
 using testing::DoAll;
 using testing::Invoke;
diff --git a/media/filters/decoder_selector_unittest.cc b/media/filters/decoder_selector_unittest.cc
index bd613ac..a0ae269 100644
--- a/media/filters/decoder_selector_unittest.cc
+++ b/media/filters/decoder_selector_unittest.cc
@@ -9,10 +9,10 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
 #include "build/build_config.h"
 #include "media/base/demuxer_stream.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/media_util.h"
 #include "media/base/mock_filters.h"
 #include "media/base/test_helpers.h"
@@ -26,6 +26,7 @@
 #include "media/filters/decrypting_video_decoder.h"
 #endif  // !defined(OS_ANDROID)
 
+using ::base::test::RunCallback;
 using ::testing::_;
 using ::testing::IsNull;
 using ::testing::NiceMock;
diff --git a/media/filters/decrypting_audio_decoder_unittest.cc b/media/filters/decrypting_audio_decoder_unittest.cc
index 62a6fd5..e1d52e6 100644
--- a/media/filters/decrypting_audio_decoder_unittest.cc
+++ b/media/filters/decrypting_audio_decoder_unittest.cc
@@ -11,11 +11,11 @@
 #include "base/callback_helpers.h"
 #include "base/run_loop.h"
 #include "base/stl_util.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
 #include "media/base/audio_buffer.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/decrypt_config.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/media_util.h"
 #include "media/base/mock_filters.h"
 #include "media/base/test_helpers.h"
@@ -23,6 +23,7 @@
 #include "media/filters/decrypting_audio_decoder.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
+using ::base::test::RunCallback;
 using ::testing::_;
 using ::testing::AtMost;
 using ::testing::Return;
diff --git a/media/filters/decrypting_demuxer_stream_unittest.cc b/media/filters/decrypting_demuxer_stream_unittest.cc
index 3b83b5c..38395c9 100644
--- a/media/filters/decrypting_demuxer_stream_unittest.cc
+++ b/media/filters/decrypting_demuxer_stream_unittest.cc
@@ -11,10 +11,10 @@
 #include "base/callback_helpers.h"
 #include "base/run_loop.h"
 #include "base/stl_util.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/decrypt_config.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/media_util.h"
 #include "media/base/mock_filters.h"
 #include "media/base/mock_media_log.h"
@@ -22,6 +22,7 @@
 #include "media/filters/decrypting_demuxer_stream.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
+using ::base::test::RunCallback;
 using ::testing::_;
 using ::testing::HasSubstr;
 using ::testing::InSequence;
diff --git a/media/filters/decrypting_media_resource_unittest.cc b/media/filters/decrypting_media_resource_unittest.cc
index 567de63..7823115 100644
--- a/media/filters/decrypting_media_resource_unittest.cc
+++ b/media/filters/decrypting_media_resource_unittest.cc
@@ -8,13 +8,13 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/decrypt_config.h"
 #include "media/base/decryptor.h"
 #include "media/base/demuxer_stream.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/media_util.h"
 #include "media/base/mock_filters.h"
 #include "media/base/pipeline_status.h"
@@ -23,6 +23,7 @@
 #include "media/filters/decrypting_media_resource.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
+using ::base::test::RunCallback;
 using ::testing::_;
 using ::testing::AnyNumber;
 using ::testing::Invoke;
diff --git a/media/filters/decrypting_video_decoder_unittest.cc b/media/filters/decrypting_video_decoder_unittest.cc
index adc3b931..bebba2a 100644
--- a/media/filters/decrypting_video_decoder_unittest.cc
+++ b/media/filters/decrypting_video_decoder_unittest.cc
@@ -11,10 +11,10 @@
 #include "base/callback_helpers.h"
 #include "base/run_loop.h"
 #include "base/stl_util.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/decrypt_config.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/media_util.h"
 #include "media/base/mock_filters.h"
 #include "media/base/test_helpers.h"
@@ -22,6 +22,7 @@
 #include "media/filters/decrypting_video_decoder.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
+using ::base::test::RunCallback;
 using ::testing::_;
 using ::testing::Invoke;
 using ::testing::Return;
diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc
index d152322..523d381 100644
--- a/media/filters/ffmpeg_video_decoder_unittest.cc
+++ b/media/filters/ffmpeg_video_decoder_unittest.cc
@@ -16,9 +16,9 @@
 #include "base/memory/singleton.h"
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
 #include "media/base/decoder_buffer.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/limits.h"
 #include "media/base/media_log.h"
 #include "media/base/media_util.h"
diff --git a/media/filters/offloading_video_decoder_unittest.cc b/media/filters/offloading_video_decoder_unittest.cc
index f672a05..86e0fe0 100644
--- a/media/filters/offloading_video_decoder_unittest.cc
+++ b/media/filters/offloading_video_decoder_unittest.cc
@@ -7,15 +7,18 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
 #include "media/base/decoder_buffer.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/mock_filters.h"
 #include "media/base/test_data_util.h"
 #include "media/base/test_helpers.h"
 #include "media/base/video_frame.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
+using base::test::RunClosure;
+using base::test::RunOnceCallback;
+using base::test::RunOnceClosure;
 using testing::_;
 using testing::DoAll;
 using testing::SaveArg;
diff --git a/media/filters/pipeline_controller_unittest.cc b/media/filters/pipeline_controller_unittest.cc
index 54569dc3..080fe78 100644
--- a/media/filters/pipeline_controller_unittest.cc
+++ b/media/filters/pipeline_controller_unittest.cc
@@ -12,14 +12,15 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/time/time.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/mock_filters.h"
 #include "media/base/pipeline.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::base::test::RunOnceClosure;
 using ::testing::_;
 using ::testing::AnyNumber;
 using ::testing::DoAll;
diff --git a/media/filters/source_buffer_state_unittest.cc b/media/filters/source_buffer_state_unittest.cc
index a29789a..e45afe2b 100644
--- a/media/filters/source_buffer_state_unittest.cc
+++ b/media/filters/source_buffer_state_unittest.cc
@@ -9,7 +9,7 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
-#include "media/base/gmock_callback_support.h"
+#include "base/test/gmock_callback_support.h"
 #include "media/base/media_util.h"
 #include "media/base/mock_filters.h"
 #include "media/base/mock_media_log.h"
@@ -20,6 +20,7 @@
 
 namespace media {
 
+using base::test::RunClosure;
 using testing::_;
 using testing::SaveArg;
 
diff --git a/media/filters/video_decoder_stream_unittest.cc b/media/filters/video_decoder_stream_unittest.cc
index f2e38c7..1c4e99c 100644
--- a/media/filters/video_decoder_stream_unittest.cc
+++ b/media/filters/video_decoder_stream_unittest.cc
@@ -10,10 +10,10 @@
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
 #include "build/build_config.h"
 #include "media/base/fake_demuxer_stream.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/mock_filters.h"
 #include "media/base/mock_media_log.h"
 #include "media/base/test_helpers.h"
@@ -26,6 +26,7 @@
 #include "media/filters/decrypting_video_decoder.h"
 #endif
 
+using ::base::test::RunCallback;
 using ::testing::_;
 using ::testing::AnyNumber;
 using ::testing::Assign;
diff --git a/media/gpu/android/media_codec_video_decoder_unittest.cc b/media/gpu/android/media_codec_video_decoder_unittest.cc
index 2c347d9..2da147dc 100644
--- a/media/gpu/android/media_codec_video_decoder_unittest.cc
+++ b/media/gpu/android/media_codec_video_decoder_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -16,7 +17,6 @@
 #include "media/base/android/mock_android_overlay.h"
 #include "media/base/android/mock_media_crypto_context.h"
 #include "media/base/decoder_buffer.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/test_helpers.h"
 #include "media/base/video_frame.h"
 #include "media/gpu/android/android_video_surface_chooser_impl.h"
@@ -27,12 +27,13 @@
 #include "media/gpu/android/video_frame_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using base::test::RunCallback;
+using testing::_;
 using testing::InvokeWithoutArgs;
 using testing::NiceMock;
 using testing::NotNull;
 using testing::Return;
 using testing::SaveArg;
-using testing::_;
 
 namespace media {
 namespace {
diff --git a/media/gpu/v4l2/BUILD.gn b/media/gpu/v4l2/BUILD.gn
index 906be540..7758cd6 100644
--- a/media/gpu/v4l2/BUILD.gn
+++ b/media/gpu/v4l2/BUILD.gn
@@ -39,6 +39,8 @@
     "v4l2_image_processor.h",
     "v4l2_slice_video_decode_accelerator.cc",
     "v4l2_slice_video_decode_accelerator.h",
+    "v4l2_slice_video_decoder.cc",
+    "v4l2_slice_video_decoder.h",
     "v4l2_stateful_workaround.cc",
     "v4l2_stateful_workaround.h",
     "v4l2_video_decode_accelerator.cc",
diff --git a/media/gpu/v4l2/v4l2_device.cc b/media/gpu/v4l2/v4l2_device.cc
index 1945fb1..fb01f392 100644
--- a/media/gpu/v4l2/v4l2_device.cc
+++ b/media/gpu/v4l2/v4l2_device.cc
@@ -550,6 +550,11 @@
   return buffer_data_->v4l2_buffer_.m.planes[plane].bytesused;
 }
 
+void V4L2WritableBufferRef::PrepareQueueBuffer(
+    scoped_refptr<V4L2DecodeSurface> surface) {
+  surface->PrepareQueueBuffer(&(buffer_data_->v4l2_buffer_));
+}
+
 size_t V4L2WritableBufferRef::BufferId() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(IsValid());
@@ -1022,6 +1027,30 @@
 }
 
 // static
+size_t V4L2Device::V4L2PixFmtToNumPlanes(uint32_t pix_fmt) {
+  switch (pix_fmt) {
+    case V4L2_PIX_FMT_NV12:
+    case V4L2_PIX_FMT_YUV420:
+    case V4L2_PIX_FMT_YVU420:
+    case V4L2_PIX_FMT_RGB32:
+      return 1;
+
+    case V4L2_PIX_FMT_NV12M:
+    case V4L2_PIX_FMT_MT21C:
+      return 2;
+
+    case V4L2_PIX_FMT_YUV420M:
+    case V4L2_PIX_FMT_YVU420M:
+    case V4L2_PIX_FMT_YUV422M:
+      return 3;
+
+    default:
+      LOG(FATAL) << "Add more cases as needed";
+      return 0;
+  }
+}
+
+// static
 uint32_t V4L2Device::VideoPixelFormatToV4L2PixFmt(const VideoPixelFormat format,
                                                   bool single_planar) {
   switch (format) {
diff --git a/media/gpu/v4l2/v4l2_device.h b/media/gpu/v4l2/v4l2_device.h
index bc209f23..ce2fa38 100644
--- a/media/gpu/v4l2/v4l2_device.h
+++ b/media/gpu/v4l2/v4l2_device.h
@@ -24,6 +24,7 @@
 #include "media/base/video_frame.h"
 #include "media/base/video_frame_layout.h"
 #include "media/gpu/media_gpu_export.h"
+#include "media/gpu/v4l2/v4l2_decode_surface.h"
 #include "media/video/video_decode_accelerator.h"
 #include "media/video/video_encode_accelerator.h"
 #include "ui/gfx/geometry/size.h"
@@ -114,6 +115,11 @@
   // return nullptr for any other buffer type.
   scoped_refptr<VideoFrame> GetVideoFrame() WARN_UNUSED_RESULT;
 
+  // Add the request or config store information to |surface|.
+  // TODO(acourbot): This method is a temporary hack. Implement proper config
+  // store/request API support.
+  void PrepareQueueBuffer(scoped_refptr<V4L2DecodeSurface> surface);
+
   // Return the V4L2 buffer ID of the underlying buffer.
   // TODO(acourbot) This is used for legacy clients but should be ultimately
   // removed. See crbug/879971
@@ -321,6 +327,7 @@
  public:
   // Utility format conversion functions
   static VideoPixelFormat V4L2PixFmtToVideoPixelFormat(uint32_t format);
+  static size_t V4L2PixFmtToNumPlanes(uint32_t pix_fmt);
   static uint32_t VideoPixelFormatToV4L2PixFmt(VideoPixelFormat format,
                                                bool single_planar);
   // Returns v4l2 pixel format from |layout|. If there is no corresponding
@@ -469,6 +476,8 @@
                               gfx::Size* min_resolution,
                               gfx::Size* max_resolution);
 
+  std::vector<uint32_t> EnumerateSupportedPixelformats(v4l2_buf_type buf_type);
+
   // Return V4L2 pixelformats supported by the available image processor
   // devices for |buf_type|.
   virtual std::vector<uint32_t> GetSupportedImageProcessorPixelformats(
@@ -502,8 +511,6 @@
 
   VideoEncodeAccelerator::SupportedProfiles EnumerateSupportedEncodeProfiles();
 
-  std::vector<uint32_t> EnumerateSupportedPixelformats(v4l2_buf_type buf_type);
-
  private:
   // Perform platform-specific initialization of the device instance.
   // Return true on success, false on error or if the particular implementation
diff --git a/media/gpu/v4l2/v4l2_slice_video_decoder.cc b/media/gpu/v4l2/v4l2_slice_video_decoder.cc
new file mode 100644
index 0000000..7f5dc938
--- /dev/null
+++ b/media/gpu/v4l2/v4l2_slice_video_decoder.cc
@@ -0,0 +1,1119 @@
+// 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 "media/gpu/v4l2/v4l2_slice_video_decoder.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/task/post_task.h"
+#include "media/base/scopedfd_helper.h"
+#include "media/gpu/accelerated_video_decoder.h"
+#include "media/gpu/linux/dmabuf_video_frame_pool.h"
+#include "media/gpu/macros.h"
+#include "media/gpu/v4l2/v4l2_h264_accelerator.h"
+#include "media/gpu/v4l2/v4l2_vp8_accelerator.h"
+#include "media/gpu/v4l2/v4l2_vp9_accelerator.h"
+#include "media/gpu/video_frame_converter.h"
+
+namespace media {
+
+namespace {
+
+// See http://crbug.com/255116.
+constexpr int k1080pArea = 1920 * 1088;
+// Input bitstream buffer size for up to 1080p streams.
+constexpr size_t kInputBufferMaxSizeFor1080p = 1024 * 1024;
+// Input bitstream buffer size for up to 4k streams.
+constexpr size_t kInputBufferMaxSizeFor4k = 4 * kInputBufferMaxSizeFor1080p;
+constexpr size_t kNumInputBuffers = 16;
+constexpr size_t kNumInputPlanes = 1;
+
+// If the driver does not accept as many fds as we received from the client,
+// we have to check if the additional fds are actually duplicated fds pointing
+// to previous planes; if so, close the duplicates and return only the original
+// fd(s). If not, return an empty list.
+std::vector<base::ScopedFD> ExtractAdditionalDmabuf(
+    scoped_refptr<VideoFrame> frame,
+    size_t target_num_fds) {
+  DCHECK(frame);
+  if (frame->DmabufFds().size() < target_num_fds) {
+    VLOGF(1) << "The count of dmabuf fds (" << frame->DmabufFds().size()
+             << ") are not enough, needs " << target_num_fds << " fds.";
+    return std::vector<base::ScopedFD>();
+  }
+
+  const std::vector<VideoFrameLayout::Plane>& planes = frame->layout().planes();
+  for (size_t i = frame->DmabufFds().size() - 1; i >= target_num_fds; --i) {
+    // Assume that an fd is a duplicate of a previous plane's fd if offset != 0.
+    // Otherwise, if offset == 0, return error as surface_it may be pointing to
+    // a new plane.
+    if (planes[i].offset == 0) {
+      VLOGF(1) << "Additional dmabuf fds point to a new buffer.";
+      return std::vector<base::ScopedFD>();
+    }
+  }
+
+  std::vector<base::ScopedFD> dmabuf_fds = DuplicateFDs(frame->DmabufFds());
+  if (dmabuf_fds.size() > target_num_fds)
+    dmabuf_fds.erase(dmabuf_fds.begin() + target_num_fds, dmabuf_fds.end());
+  return dmabuf_fds;
+}
+
+}  // namespace
+
+struct V4L2SliceVideoDecoder::InputRecord {
+  // The writable buffer got from V4L2 input queue. The value is valid from the
+  // time the surface is created, until the input buffer is enqueued into V4L2
+  // device.
+  V4L2WritableBufferRef input_buf;
+
+  explicit InputRecord(V4L2WritableBufferRef buf) : input_buf(std::move(buf)) {}
+};
+
+struct V4L2SliceVideoDecoder::OutputRecord {
+  // The DMA-buf VideoFrame. The DMA-buf will be enqueued into V4L2 device.
+  // After dequeued from V4L2 device, the frame will be sent to the client
+  // of the VideoDecoder.
+  scoped_refptr<VideoFrame> frame;
+  // The writable buffer got from V4L2 output queue. The value is valid from
+  // the time the surface is created, until the buffer is enqueued into V4L2
+  // device.
+  V4L2WritableBufferRef output_buf;
+  // The buffer dequeued from V4L2 output queue. The value is valid from the
+  // time the output buffer is dequeued from V4L2 device, until the surface is
+  // released.
+  V4L2ReadableBufferRef decoded_output_buf;
+
+  OutputRecord(scoped_refptr<VideoFrame> f, V4L2WritableBufferRef buf)
+      : frame(std::move(f)), output_buf(std::move(buf)) {}
+};
+
+struct V4L2SliceVideoDecoder::DecodeRequest {
+  // The decode buffer passed from Decode().
+  scoped_refptr<DecoderBuffer> buffer;
+  // The callback function passed from Decode().
+  DecodeCB decode_cb;
+  // The identifier for the decoder buffer.
+  int32_t bitstream_id;
+
+  DecodeRequest(scoped_refptr<DecoderBuffer> buf, DecodeCB cb, int32_t id)
+      : buffer(std::move(buf)), decode_cb(std::move(cb)), bitstream_id(id) {}
+};
+
+struct V4L2SliceVideoDecoder::OutputRequest {
+  enum OutputRequestType {
+    // The surface to be outputted.
+    kSurface,
+    // The fence to indicate the flush request.
+    kFlushFence,
+    // The fence to indicate resolution change request.
+    kChangeResolutionFence,
+  };
+
+  // The type of the request.
+  const OutputRequestType type;
+  // The surface to be outputted.
+  scoped_refptr<V4L2DecodeSurface> surface;
+
+  explicit OutputRequest(scoped_refptr<V4L2DecodeSurface> s)
+      : type(kSurface), surface(std::move(s)) {}
+  explicit OutputRequest(OutputRequestType t) : type(t) {
+    DCHECK_NE(t, kSurface);
+  }
+
+  bool IsReady() const {
+    return (type != OutputRequestType::kSurface) || surface->decoded();
+  }
+};
+
+// static
+std::unique_ptr<VideoDecoder> V4L2SliceVideoDecoder::Create(
+    scoped_refptr<base::SequencedTaskRunner> client_task_runner,
+    std::unique_ptr<DmabufVideoFramePool> frame_pool,
+    std::unique_ptr<VideoFrameConverter> frame_converter) {
+  DCHECK(client_task_runner->RunsTasksInCurrentSequence());
+  DCHECK(frame_pool);
+  DCHECK(frame_converter);
+
+  scoped_refptr<V4L2Device> device = V4L2Device::Create();
+  if (!device) {
+    VLOGF(1) << "Failed to create V4L2 device.";
+    return nullptr;
+  }
+
+  return base::WrapUnique<VideoDecoder>(new V4L2SliceVideoDecoder(
+      std::move(client_task_runner), std::move(device), std::move(frame_pool),
+      std::move(frame_converter)));
+}
+
+V4L2SliceVideoDecoder::V4L2SliceVideoDecoder(
+    scoped_refptr<base::SequencedTaskRunner> client_task_runner,
+    scoped_refptr<V4L2Device> device,
+    std::unique_ptr<DmabufVideoFramePool> frame_pool,
+    std::unique_ptr<VideoFrameConverter> frame_converter)
+    : device_(std::move(device)),
+      frame_pool_(std::move(frame_pool)),
+      frame_converter_(std::move(frame_converter)),
+      client_task_runner_(std::move(client_task_runner)),
+      decoder_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+          {base::WithBaseSyncPrimitives(), base::TaskPriority::USER_VISIBLE})),
+      device_poll_thread_("V4L2SliceVideoDecoderDevicePollThread"),
+      state_(State::kUninitialized),
+      weak_this_factory_(this) {
+  VLOGF(2);
+  weak_this_ = weak_this_factory_.GetWeakPtr();
+
+  frame_pool_->set_parent_task_runner(decoder_task_runner_);
+  frame_converter_->set_parent_task_runner(decoder_task_runner_);
+}
+
+V4L2SliceVideoDecoder::~V4L2SliceVideoDecoder() {
+  VLOGF(2);
+}
+
+std::string V4L2SliceVideoDecoder::GetDisplayName() const {
+  return "V4L2SliceVideoDecoder";
+}
+
+bool V4L2SliceVideoDecoder::IsPlatformDecoder() const {
+  return true;
+}
+
+int V4L2SliceVideoDecoder::GetMaxDecodeRequests() const {
+  return 4;
+}
+
+bool V4L2SliceVideoDecoder::NeedsBitstreamConversion() const {
+  return needs_bitstream_conversion_;
+}
+
+bool V4L2SliceVideoDecoder::CanReadWithoutStalling() const {
+  return frame_pool_ && !frame_pool_->IsExhausted();
+}
+
+void V4L2SliceVideoDecoder::Destroy() {
+  DCHECK(client_task_runner_->RunsTasksInCurrentSequence());
+  VLOGF(2);
+
+  decoder_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&V4L2SliceVideoDecoder::DestroyTask, weak_this_));
+}
+
+void V4L2SliceVideoDecoder::DestroyTask() {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DVLOGF(2);
+
+  if (avd_) {
+    avd_->Reset();
+    avd_ = nullptr;
+  }
+
+  // Call all pending decode callback.
+  ClearPendingRequests(DecodeStatus::ABORTED);
+
+  // Stop and Destroy device.
+  StopStreamV4L2Queue();
+  input_queue_->DeallocateBuffers();
+  output_queue_->DeallocateBuffers();
+  DCHECK(surfaces_at_device_.empty());
+
+  weak_this_factory_.InvalidateWeakPtrs();
+  delete this;
+  VLOGF(2) << "Destroyed";
+}
+
+void V4L2SliceVideoDecoder::Initialize(const VideoDecoderConfig& config,
+                                       bool low_delay,
+                                       CdmContext* cdm_context,
+                                       InitCB init_cb,
+                                       const OutputCB& output_cb,
+                                       const WaitingCB& /* waiting_cb */) {
+  DCHECK(client_task_runner_->RunsTasksInCurrentSequence());
+  VLOGF(2) << "config: " << config.AsHumanReadableString();
+
+  if (!config.IsValidConfig()) {
+    VLOGF(1) << "config is not valid";
+    std::move(init_cb).Run(false);
+    return;
+  }
+  if (cdm_context) {
+    VLOGF(1) << "cdm_context is not supported.";
+    std::move(init_cb).Run(false);
+    return;
+  }
+
+  decoder_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&V4L2SliceVideoDecoder::InitializeTask, weak_this_, config,
+                     std::move(init_cb), std::move(output_cb)));
+}
+
+void V4L2SliceVideoDecoder::InitializeTask(const VideoDecoderConfig& config,
+                                           InitCB init_cb,
+                                           const OutputCB& output_cb) {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(state_ == State::kUninitialized || state_ == State::kDecoding);
+  DVLOGF(3);
+
+  if (!output_request_queue_.empty() || flush_cb_ || current_decode_request_ ||
+      !decode_request_queue_.empty()) {
+    VLOGF(1) << "Should not call Initialize() during pending decode";
+    client_task_runner_->PostTask(FROM_HERE,
+                                  base::BindOnce(std::move(init_cb), false));
+    return;
+  }
+
+  // Reset V4L2 device and queue if reinitializing decoder.
+  if (state_ != State::kUninitialized) {
+    if (!StopStreamV4L2Queue()) {
+      client_task_runner_->PostTask(FROM_HERE,
+                                    base::BindOnce(std::move(init_cb), false));
+      return;
+    }
+
+    input_queue_->DeallocateBuffers();
+    output_queue_->DeallocateBuffers();
+    input_queue_ = nullptr;
+    output_queue_ = nullptr;
+
+    device_ = V4L2Device::Create();
+    if (!device_) {
+      VLOGF(1) << "Failed to create V4L2 device.";
+      client_task_runner_->PostTask(FROM_HERE,
+                                    base::BindOnce(std::move(init_cb), false));
+      return;
+    }
+
+    if (avd_) {
+      avd_->Reset();
+      avd_ = nullptr;
+    }
+    SetState(State::kUninitialized);
+  }
+
+  // Open V4L2 device.
+  VideoCodecProfile profile = config.profile();
+  uint32_t input_format_fourcc =
+      V4L2Device::VideoCodecProfileToV4L2PixFmt(profile, true);
+  if (!device_->Open(V4L2Device::Type::kDecoder, input_format_fourcc)) {
+    VLOGF(1) << "Failed to open device for profile: " << profile
+             << " fourcc: " << FourccToString(input_format_fourcc);
+    client_task_runner_->PostTask(FROM_HERE,
+                                  base::BindOnce(std::move(init_cb), false));
+    return;
+  }
+
+  struct v4l2_capability caps;
+  const __u32 kCapsRequired = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+  if (device_->Ioctl(VIDIOC_QUERYCAP, &caps) ||
+      (caps.capabilities & kCapsRequired) != kCapsRequired) {
+    VLOGF(1) << "ioctl() failed: VIDIOC_QUERYCAP, "
+             << "caps check failed: 0x" << std::hex << caps.capabilities;
+    client_task_runner_->PostTask(FROM_HERE,
+                                  base::BindOnce(std::move(init_cb), false));
+    return;
+  }
+
+  // Create codec-specific AcceleratedVideoDecoder.
+  // TODO(akahuang): Check the profile is supported.
+  if (profile >= H264PROFILE_MIN && profile <= H264PROFILE_MAX) {
+    avd_.reset(new H264Decoder(
+        std::make_unique<V4L2H264Accelerator>(this, device_.get())));
+  } else if (profile >= VP8PROFILE_MIN && profile <= VP8PROFILE_MAX) {
+    avd_.reset(new VP8Decoder(
+        std::make_unique<V4L2VP8Accelerator>(this, device_.get())));
+  } else if (profile >= VP9PROFILE_MIN && profile <= VP9PROFILE_MAX) {
+    avd_.reset(new VP9Decoder(
+        std::make_unique<V4L2VP9Accelerator>(this, device_.get())));
+  } else {
+    VLOGF(1) << "Unsupported profile " << GetProfileName(profile);
+    client_task_runner_->PostTask(FROM_HERE,
+                                  base::BindOnce(std::move(init_cb), false));
+    return;
+  }
+  needs_bitstream_conversion_ = (config.codec() == kCodecH264);
+
+  // Setup input format.
+  if (!SetupInputFormat(input_format_fourcc)) {
+    VLOGF(1) << "Failed to setup input format.";
+    client_task_runner_->PostTask(FROM_HERE,
+                                  base::BindOnce(std::move(init_cb), false));
+    return;
+  }
+
+  // Setup output format.
+  uint32_t output_format_fourcc = NegotiateOutputFormat();
+  num_output_planes_ = V4L2Device::V4L2PixFmtToNumPlanes(output_format_fourcc);
+  if (!SetupOutputFormat(output_format_fourcc)) {
+    VLOGF(1) << "Failed to setup output format.";
+    client_task_runner_->PostTask(FROM_HERE,
+                                  base::BindOnce(std::move(init_cb), false));
+    return;
+  }
+
+  // Setup frame pool.
+  VideoPixelFormat output_format =
+      V4L2Device::V4L2PixFmtToVideoPixelFormat(output_format_fourcc);
+  frame_layout_ = VideoFrameLayout::Create(output_format, config.coded_size());
+  if (!frame_layout_) {
+    VLOGF(1) << "Failed to create video frame layout.";
+    client_task_runner_->PostTask(FROM_HERE,
+                                  base::BindOnce(std::move(init_cb), false));
+    return;
+  }
+  visible_rect_ = config.visible_rect();
+  natural_size_ = config.natural_size();
+  frame_pool_->SetFrameFormat(*frame_layout_, visible_rect_, natural_size_);
+
+  // Create Input/Output V4L2Queue
+  input_queue_ = device_->GetQueue(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+  output_queue_ = device_->GetQueue(V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+  if (!input_queue_ || !output_queue_) {
+    VLOGF(1) << "Failed to create V4L2 queue.";
+    client_task_runner_->PostTask(FROM_HERE,
+                                  base::BindOnce(std::move(init_cb), false));
+    return;
+  }
+  if (input_queue_->AllocateBuffers(kNumInputBuffers, V4L2_MEMORY_MMAP) == 0) {
+    VLOGF(1) << "Failed to allocate input buffer.";
+    client_task_runner_->PostTask(FROM_HERE,
+                                  base::BindOnce(std::move(init_cb), false));
+    return;
+  }
+  input_record_map_.clear();
+
+  // Call init_cb
+  output_cb_ = output_cb;
+  SetState(State::kDecoding);
+  client_task_runner_->PostTask(FROM_HERE,
+                                base::BindOnce(std::move(init_cb), true));
+}
+
+bool V4L2SliceVideoDecoder::SetupInputFormat(uint32_t input_format_fourcc) {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK_EQ(state_, State::kUninitialized);
+
+  // Check if the format is supported.
+  std::vector<uint32_t> formats = device_->EnumerateSupportedPixelformats(
+      V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+  if (std::find(formats.begin(), formats.end(), input_format_fourcc) ==
+      formats.end()) {
+    DVLOGF(3) << "Input fourcc " << input_format_fourcc
+              << " not supported by device.";
+    return false;
+  }
+
+  // Determine the input buffer size.
+  gfx::Size max_size, min_size;
+  device_->GetSupportedResolution(input_format_fourcc, &min_size, &max_size);
+  size_t input_size = max_size.GetArea() > k1080pArea
+                          ? kInputBufferMaxSizeFor4k
+                          : kInputBufferMaxSizeFor1080p;
+
+  // Setup the input format.
+  struct v4l2_format format;
+  memset(&format, 0, sizeof(format));
+  format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+  format.fmt.pix_mp.pixelformat = input_format_fourcc;
+  format.fmt.pix_mp.plane_fmt[0].sizeimage = input_size;
+  format.fmt.pix_mp.num_planes = kNumInputPlanes;
+  if (device_->Ioctl(VIDIOC_S_FMT, &format) != 0) {
+    VPLOGF(1) << "Failed to call IOCTL to set input format.";
+    return false;
+  }
+  DCHECK_EQ(format.fmt.pix_mp.pixelformat, input_format_fourcc);
+
+  return true;
+}
+
+uint32_t V4L2SliceVideoDecoder::NegotiateOutputFormat() {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+
+  const std::vector<uint32_t> formats = device_->EnumerateSupportedPixelformats(
+      V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+  DCHECK(!formats.empty());
+  for (const auto format : formats) {
+    if (device_->CanCreateEGLImageFrom(format)) {
+      return format;
+    }
+  }
+
+  // TODO(akahuang): Use ImageProcessor in this case.
+  VLOGF(2) << "WARNING: Cannot find format that can create EGL image. "
+           << "We need ImageProcessor to convert pixel format.";
+  return formats[0];
+}
+
+bool V4L2SliceVideoDecoder::SetupOutputFormat(uint32_t output_format_fourcc) {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DVLOGF(3) << "output_format_fourcc = " << output_format_fourcc;
+
+  // Only set fourcc for output; resolution, etc., will come from the
+  // driver once surface_it extracts surface_it from the stream.
+  struct v4l2_format format;
+  memset(&format, 0, sizeof(format));
+  format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+  format.fmt.pix_mp.pixelformat = output_format_fourcc;
+  format.fmt.pix_mp.num_planes = num_output_planes_;
+  if (device_->Ioctl(VIDIOC_S_FMT, &format) != 0) {
+    VPLOGF(1) << "Failed to call IOCTL to set output format.";
+    return false;
+  }
+  DCHECK_EQ(format.fmt.pix_mp.pixelformat, output_format_fourcc);
+
+  return true;
+}
+
+void V4L2SliceVideoDecoder::Reset(base::OnceClosure closure) {
+  DCHECK(client_task_runner_->RunsTasksInCurrentSequence());
+  DVLOGF(3);
+
+  decoder_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&V4L2SliceVideoDecoder::ResetTask, weak_this_,
+                                std::move(closure)));
+}
+
+void V4L2SliceVideoDecoder::ResetTask(base::OnceClosure closure) {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DVLOGF(3);
+
+  if (avd_)
+    avd_->Reset();
+
+  // Call all pending decode callback.
+  ClearPendingRequests(DecodeStatus::ABORTED);
+
+  // Streamoff V4L2 queues to drop input and output buffers.
+  // If the queues are streaming before reset, then we need to start streaming
+  // them after stopping.
+  bool poll_thread_running = device_poll_thread_.IsRunning();
+  if (!StopStreamV4L2Queue())
+    return;
+
+  if (poll_thread_running) {
+    if (!StartStreamV4L2Queue())
+      return;
+  }
+
+  client_task_runner_->PostTask(FROM_HERE, std::move(closure));
+}
+
+void V4L2SliceVideoDecoder::ClearPendingRequests(DecodeStatus status) {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DVLOGF(3);
+
+  // Clear output_request_queue_.
+  while (!output_request_queue_.empty())
+    output_request_queue_.pop();
+
+  if (flush_cb_)
+    RunDecodeCB(std::move(flush_cb_), status);
+
+  // Clear current_decode_request_ and decode_request_queue_.
+  if (current_decode_request_) {
+    RunDecodeCB(std::move(current_decode_request_->decode_cb), status);
+    current_decode_request_ = nullptr;
+  }
+
+  while (!decode_request_queue_.empty()) {
+    auto request = std::move(decode_request_queue_.front());
+    decode_request_queue_.pop();
+    RunDecodeCB(std::move(request->decode_cb), status);
+  }
+}
+
+void V4L2SliceVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer,
+                                   DecodeCB decode_cb) {
+  DCHECK(client_task_runner_->RunsTasksInCurrentSequence());
+
+  decoder_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &V4L2SliceVideoDecoder::EnqueueDecodeTask, weak_this_,
+          std::make_unique<DecodeRequest>(
+              std::move(buffer), std::move(decode_cb), GetNextBitstreamId())));
+}
+
+void V4L2SliceVideoDecoder::EnqueueDecodeTask(
+    std::unique_ptr<DecodeRequest> request) {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(state_ == State::kDecoding || state_ == State::kPause);
+
+  decode_request_queue_.push(std::move(request));
+  PumpDecodeTask();
+}
+
+void V4L2SliceVideoDecoder::PumpDecodeTask() {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(state_ == State::kDecoding || state_ == State::kPause);
+  DVLOGF(3) << "state_:" << static_cast<int>(state_)
+            << " Number of Decode requests: " << decode_request_queue_.size();
+
+  if (state_ == State::kPause)
+    return;
+
+  while (true) {
+    switch (avd_->Decode()) {
+      case AcceleratedVideoDecoder::kAllocateNewSurfaces:
+        DVLOGF(3) << "Need to change resolution. Pause decoding.";
+        SetState(State::kPause);
+
+        output_request_queue_.push(std::make_unique<OutputRequest>(
+            OutputRequest::kChangeResolutionFence));
+        PumpOutputSurfaces();
+        return;
+
+      case AcceleratedVideoDecoder::kRanOutOfStreamData:
+        // Current decode request is finished processing.
+        if (current_decode_request_) {
+          DCHECK(current_decode_request_->decode_cb);
+          RunDecodeCB(std::move(current_decode_request_->decode_cb),
+                      DecodeStatus::OK);
+          current_decode_request_ = nullptr;
+        }
+
+        // Process next decodee request.
+        if (decode_request_queue_.empty())
+          return;
+        current_decode_request_ = std::move(decode_request_queue_.front());
+        decode_request_queue_.pop();
+
+        if (current_decode_request_->buffer->end_of_stream()) {
+          if (!avd_->Flush()) {
+            VLOGF(1) << "Failed flushing the decoder.";
+            SetState(State::kError);
+            return;
+          }
+          // Put the decoder in an idle state, ready to resume.
+          avd_->Reset();
+
+          SetState(State::kPause);
+          DCHECK(!flush_cb_);
+          flush_cb_ = std::move(current_decode_request_->decode_cb);
+
+          output_request_queue_.push(
+              std::make_unique<OutputRequest>(OutputRequest::kFlushFence));
+          PumpOutputSurfaces();
+          current_decode_request_ = nullptr;
+          return;
+        }
+
+        avd_->SetStream(current_decode_request_->bitstream_id,
+                        current_decode_request_->buffer->data(),
+                        current_decode_request_->buffer->data_size());
+        break;
+
+      case AcceleratedVideoDecoder::kRanOutOfSurfaces:
+        DVLOGF(3) << "Ran out of surfaces. Resume when buffer is returned.";
+        return;
+
+      case AcceleratedVideoDecoder::kNeedContextUpdate:
+        DVLOGF(3) << "Awaiting context update";
+        return;
+
+      case AcceleratedVideoDecoder::kDecodeError:
+        DVLOGF(3) << "Error decoding stream";
+        SetState(State::kError);
+        return;
+
+      case AcceleratedVideoDecoder::kTryAgain:
+        NOTREACHED() << "Should not reach here unless this class accepts "
+                        "encrypted streams.";
+        DVLOGF(4) << "No key for decoding stream.";
+        SetState(State::kError);
+        return;
+    }
+  }
+}
+
+void V4L2SliceVideoDecoder::PumpOutputSurfaces() {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DVLOGF(3) << "state_: " << static_cast<int>(state_)
+            << " Number of display surfaces: " << output_request_queue_.size();
+
+  bool resume_decode = false;
+  while (!output_request_queue_.empty()) {
+    if (!output_request_queue_.front()->IsReady()) {
+      DVLOGF(3) << "The first surface is not ready yet.";
+      break;
+    }
+
+    std::unique_ptr<OutputRequest> request =
+        std::move(output_request_queue_.front());
+    output_request_queue_.pop();
+    switch (request->type) {
+      case OutputRequest::kFlushFence:
+        DCHECK(output_request_queue_.empty());
+        DVLOGF(2) << "Flush finished.";
+        RunDecodeCB(std::move(flush_cb_), DecodeStatus::OK);
+        resume_decode = true;
+        break;
+
+      case OutputRequest::kChangeResolutionFence:
+        DCHECK(output_request_queue_.empty());
+        if (!ChangeResolution()) {
+          SetState(State::kError);
+          return;
+        }
+        resume_decode = true;
+        break;
+
+      case OutputRequest::kSurface:
+        scoped_refptr<V4L2DecodeSurface> surface = std::move(request->surface);
+        auto surface_it = output_record_map_.find(surface->output_record());
+        DCHECK(surface_it != output_record_map_.end());
+        OutputRecord* output_record = surface_it->second.get();
+
+        DCHECK_NE(output_record->frame, nullptr);
+        RunOutputCB(output_record->frame);
+        break;
+    }
+  }
+
+  if (resume_decode) {
+    SetState(State::kDecoding);
+    decoder_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&V4L2SliceVideoDecoder::PumpDecodeTask, weak_this_));
+  }
+}
+
+bool V4L2SliceVideoDecoder::ChangeResolution() {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK_EQ(state_, State::kPause);
+  // We change resolution after outputting all pending surfaces, there should
+  // be no V4L2DecodeSurface left. Also, the corresponding OutputRecord in
+  // |output_record_map_| is erased when the surface is released. Therefore
+  // |output_record_map_| should also be empty.
+  DCHECK(surfaces_at_device_.empty());
+  DCHECK(output_record_map_.empty());
+  DCHECK(input_record_map_.empty());
+
+  DCHECK(output_request_queue_.empty());
+  if (!StopStreamV4L2Queue())
+    return false;
+
+  // Set the new resolution.
+  gfx::Size pic_size = avd_->GetPicSize();
+  DCHECK(!pic_size.IsEmpty());
+  DVLOGF(3) << "Change resolution to " << pic_size.width() << "x"
+            << pic_size.height();
+  struct v4l2_format format;
+  memset(&format, 0, sizeof(format));
+  format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+  if (device_->Ioctl(VIDIOC_G_FMT, &format) != 0) {
+    VLOGF(1) << "Failed getting output format.";
+    return false;
+  }
+  format.fmt.pix_mp.width = pic_size.width();
+  format.fmt.pix_mp.height = pic_size.height();
+  if (device_->Ioctl(VIDIOC_S_FMT, &format) != 0) {
+    VLOGF(1) << "Failed setting resolution.";
+    return false;
+  }
+
+  // Update frame layout.
+  gfx::Size coded_size(base::checked_cast<int>(format.fmt.pix_mp.width),
+                       base::checked_cast<int>(format.fmt.pix_mp.height));
+  DCHECK_EQ(coded_size.width() % 16, 0);
+  DCHECK_EQ(coded_size.height() % 16, 0);
+  if (!gfx::Rect(coded_size).Contains(gfx::Rect(pic_size))) {
+    VLOGF(1) << "Got invalid adjusted coded size: " << coded_size.ToString();
+    return false;
+  }
+  frame_layout_ = VideoFrameLayout::Create(frame_layout_->format(), coded_size);
+  frame_pool_->SetFrameFormat(*frame_layout_, visible_rect_, natural_size_);
+
+  // Allocate new output buffers.
+  size_t num_output_frames = avd_->GetRequiredNumOfPictures();
+  DCHECK_GT(num_output_frames, 0u);
+  if (output_queue_->AllocateBuffers(num_output_frames, V4L2_MEMORY_DMABUF) ==
+      0) {
+    VLOGF(1) << "Failed to request output buffers.";
+    return false;
+  }
+  if (output_queue_->AllocatedBuffersCount() != num_output_frames) {
+    VLOGF(1) << "Could not allocate requested number of output buffers.";
+    return false;
+  }
+  frame_pool_->SetMaxNumFrames(num_output_frames);
+
+  if (!StartStreamV4L2Queue())
+    return false;
+
+  SetState(State::kDecoding);
+  return true;
+}
+
+scoped_refptr<V4L2DecodeSurface> V4L2SliceVideoDecoder::CreateSurface() {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DVLOGF(4);
+
+  // Request VideoFrame.
+  scoped_refptr<VideoFrame> frame = frame_pool_->GetFrame();
+  if (!frame) {
+    // We allocate the same number of output buffer slot in V4L2 device and the
+    // output VideoFrame. If there is free output buffer slot but no free
+    // VideoFrame, surface_it means the VideoFrame is not released at client
+    // side. Post PumpDecodeTask for busy waiting VideoFrame released.
+    //
+    // TODO(akahuang): WARNING: This is a temporary hack.
+    // Switch to event-driven mechanism instead of busy-polling.
+    DVLOGF(3) << "There is no available VideoFrame.";
+    decoder_task_runner_->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&V4L2SliceVideoDecoder::PumpDecodeTask, weak_this_),
+        base::TimeDelta::FromMilliseconds(2));
+    return nullptr;
+  }
+  frame->set_timestamp(current_decode_request_->buffer->timestamp());
+
+  // Request V4L2 input and output buffers.
+  V4L2WritableBufferRef input_buf = input_queue_->GetFreeBuffer();
+  V4L2WritableBufferRef output_buf = output_queue_->GetFreeBuffer();
+  if (!input_buf.IsValid() || !output_buf.IsValid())
+    return nullptr;
+
+  // Record the frame and V4L2 buffers to the input and output records.
+  int input_record_id = input_buf.BufferId();
+  DCHECK(input_record_map_.find(input_record_id) == input_record_map_.end());
+  input_record_map_.insert(std::make_pair(
+      input_record_id, std::make_unique<InputRecord>(std::move(input_buf))));
+
+  int output_record_id = output_buf.BufferId();
+  DCHECK(output_record_map_.find(output_record_id) == output_record_map_.end());
+  output_record_map_.insert(std::make_pair(
+      output_record_id,
+      std::make_unique<OutputRecord>(std::move(frame), std::move(output_buf))));
+
+  return scoped_refptr<V4L2DecodeSurface>(new V4L2ConfigStoreDecodeSurface(
+      input_record_id, output_record_id,
+      base::BindOnce(&V4L2SliceVideoDecoder::ReuseOutputBuffer, weak_this_,
+                     output_record_id)));
+}
+
+void V4L2SliceVideoDecoder::ReuseOutputBuffer(int index) {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DVLOGF(3) << "Reuse output surface #" << index;
+
+  // Release the VideoFrame and V4L2 output buffer in the output record.
+  output_record_map_.erase(index);
+
+  // Resume decoding in case of ran out of surface.
+  if (state_ == State::kDecoding) {
+    decoder_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&V4L2SliceVideoDecoder::PumpDecodeTask, weak_this_));
+  }
+}
+
+bool V4L2SliceVideoDecoder::SubmitSlice(
+    const scoped_refptr<V4L2DecodeSurface>& dec_surface,
+    const uint8_t* data,
+    size_t size) {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DVLOGF(3);
+
+  auto surface_it = input_record_map_.find(dec_surface->input_record());
+  DCHECK(surface_it != input_record_map_.end());
+  InputRecord* input_record = surface_it->second.get();
+
+  size_t plane_size = input_record->input_buf.GetPlaneSize(0);
+  size_t bytes_used = input_record->input_buf.GetPlaneBytesUsed(0);
+  if (size > plane_size - bytes_used) {
+    VLOGF(1) << "The size of submitted slice(" << size
+             << ") is larger than the remaining buffer size("
+             << plane_size - bytes_used << "). Plane size is " << plane_size;
+    SetState(State::kError);
+    return false;
+  }
+
+  void* mapping = input_record->input_buf.GetPlaneMapping(0);
+  memcpy(reinterpret_cast<uint8_t*>(mapping) + bytes_used, data, size);
+  input_record->input_buf.SetPlaneBytesUsed(0, bytes_used + size);
+  return true;
+}
+
+void V4L2SliceVideoDecoder::DecodeSurface(
+    const scoped_refptr<V4L2DecodeSurface>& dec_surface) {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DVLOGF(3);
+
+  // Enqueue input_buf and output_buf
+  auto input_it = input_record_map_.find(dec_surface->input_record());
+  DCHECK(input_it != input_record_map_.end());
+  InputRecord* input_record = input_it->second.get();
+  input_record->input_buf.PrepareQueueBuffer(dec_surface);
+  if (!std::move(input_record->input_buf).QueueMMap()) {
+    SetState(State::kError);
+    return;
+  }
+  input_record_map_.erase(input_it);
+
+  auto surface_it = output_record_map_.find(dec_surface->output_record());
+  DCHECK(surface_it != output_record_map_.end());
+  OutputRecord* output_record = surface_it->second.get();
+  std::vector<base::ScopedFD> dmabuf_fds =
+      ExtractAdditionalDmabuf(output_record->frame, num_output_planes_);
+  if (dmabuf_fds.empty()) {
+    SetState(State::kError);
+    return;
+  }
+  if (!std::move(output_record->output_buf).QueueDMABuf(dmabuf_fds)) {
+    SetState(State::kError);
+    return;
+  }
+  surfaces_at_device_.insert(
+      std::make_pair(dec_surface->output_record(), dec_surface));
+
+  if (!dec_surface->Submit()) {
+    VLOGF(1) << "Error while submitting frame for decoding!";
+    SetState(State::kError);
+    return;
+  }
+
+  SchedulePollTaskIfNeeded();
+}
+
+void V4L2SliceVideoDecoder::SurfaceReady(
+    const scoped_refptr<V4L2DecodeSurface>& dec_surface,
+    int32_t bitstream_id,
+    const gfx::Rect& visible_rect,
+    const VideoColorSpace& /* color_space */) {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DVLOGF(3);
+
+  // TODO(akahuang): Update visible_rect at the output frame.
+  dec_surface->SetVisibleRect(visible_rect);
+
+  output_request_queue_.push(std::make_unique<OutputRequest>(dec_surface));
+  PumpOutputSurfaces();
+}
+
+bool V4L2SliceVideoDecoder::StartStreamV4L2Queue() {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DVLOGF(3);
+
+  if (!device_poll_thread_.IsRunning()) {
+    if (!device_poll_thread_.Start()) {
+      VLOGF(1) << "Failed to start device poll thread.";
+      SetState(State::kError);
+      return false;
+    }
+  }
+
+  if (!input_queue_->Streamon() || !output_queue_->Streamon()) {
+    VLOGF(1) << "Failed to streamon V4L2 queue.";
+    SetState(State::kError);
+    return false;
+  }
+
+  SchedulePollTaskIfNeeded();
+  return true;
+}
+
+bool V4L2SliceVideoDecoder::StopStreamV4L2Queue() {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK_NE(state_, State::kUninitialized);
+  DVLOGF(3);
+
+  if (!device_poll_thread_.IsRunning())
+    return true;
+
+  if (!device_->SetDevicePollInterrupt()) {
+    VLOGF(1) << "Failed to interrupt device poll.";
+    SetState(State::kError);
+    return false;
+  }
+
+  DVLOGF(3) << "Stop device poll thead";
+  device_poll_thread_.Stop();
+  if (!device_->ClearDevicePollInterrupt()) {
+    VLOGF(1) << "Failed to clear interrupting device poll.";
+    SetState(State::kError);
+    return false;
+  }
+
+  // Streamoff input queue.
+  if (input_queue_->IsStreaming())
+    input_queue_->Streamoff();
+  input_record_map_.clear();
+
+  // Streamoff output queue.
+  if (output_queue_->IsStreaming())
+    output_queue_->Streamoff();
+  output_record_map_.clear();
+  surfaces_at_device_.clear();
+
+  return true;
+}
+
+// Poke when we want to dequeue buffer from V4L2 device
+void V4L2SliceVideoDecoder::SchedulePollTaskIfNeeded() {
+  DVLOGF(3);
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(input_queue_->IsStreaming() && output_queue_->IsStreaming());
+
+  if (!device_poll_thread_.IsRunning()) {
+    DVLOGF(4) << "Device poll thread stopped, will not schedule poll";
+    return;
+  }
+
+  if (input_queue_->QueuedBuffersCount() == 0 &&
+      output_queue_->QueuedBuffersCount() == 0) {
+    DVLOGF(4) << "No buffers queued, will not schedule poll";
+    return;
+  }
+
+  device_poll_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&V4L2SliceVideoDecoder::DevicePollTask,
+                                base::Unretained(this)));
+}
+
+void V4L2SliceVideoDecoder::DevicePollTask() {
+  DCHECK(device_poll_thread_.task_runner()->RunsTasksInCurrentSequence());
+  DVLOGF(3);
+
+  bool event_pending;
+  if (!device_->Poll(true, &event_pending)) {
+    decoder_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&V4L2SliceVideoDecoder::SetState, weak_this_,
+                                  State::kError));
+    return;
+  }
+
+  decoder_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&V4L2SliceVideoDecoder::ServiceDeviceTask, weak_this_));
+}
+
+void V4L2SliceVideoDecoder::ServiceDeviceTask() {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DVLOGF(3) << "Number of queued input buffers: "
+            << input_queue_->QueuedBuffersCount()
+            << ", Number of queued output buffers: "
+            << output_queue_->QueuedBuffersCount();
+
+  // Dequeue V4L2 output buffer first to reduce output latency.
+  bool success;
+  V4L2ReadableBufferRef dequeued_buffer;
+  while (output_queue_->QueuedBuffersCount() > 0) {
+    std::tie(success, dequeued_buffer) = output_queue_->DequeueBuffer();
+    if (!success) {
+      SetState(State::kError);
+      return;
+    }
+    if (!dequeued_buffer)
+      break;
+
+    // Mark the output buffer decoded, and try to output surface.
+    auto surface_it = surfaces_at_device_.find(dequeued_buffer->BufferId());
+    DCHECK(surface_it != surfaces_at_device_.end());
+    surface_it->second->SetDecoded();
+    surfaces_at_device_.erase(surface_it);
+
+    auto output_it = output_record_map_.find(dequeued_buffer->BufferId());
+    DCHECK(output_it != output_record_map_.end());
+    output_it->second->decoded_output_buf = std::move(dequeued_buffer);
+
+    PumpOutputSurfaces();
+  }
+
+  // Dequeue V4L2 input buffer.
+  while (input_queue_->QueuedBuffersCount() > 0) {
+    std::tie(success, dequeued_buffer) = input_queue_->DequeueBuffer();
+    if (!success) {
+      SetState(State::kError);
+      return;
+    }
+    if (!dequeued_buffer)
+      break;
+  }
+
+  SchedulePollTaskIfNeeded();
+}
+
+int32_t V4L2SliceVideoDecoder::GetNextBitstreamId() {
+  DCHECK(client_task_runner_->RunsTasksInCurrentSequence());
+
+  next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & 0x7FFFFFFF;
+  return next_bitstream_buffer_id_;
+}
+
+void V4L2SliceVideoDecoder::RunDecodeCB(DecodeCB cb, DecodeStatus status) {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+
+  client_task_runner_->PostTask(FROM_HERE,
+                                base::BindOnce(std::move(cb), status));
+}
+
+void V4L2SliceVideoDecoder::RunOutputCB(scoped_refptr<VideoFrame> frame) {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+
+  frame->metadata()->SetBoolean(VideoFrameMetadata::POWER_EFFICIENT, true);
+  scoped_refptr<VideoFrame> converted_frame =
+      frame_converter_->ConvertFrame(std::move(frame));
+  if (!converted_frame) {
+    VLOGF(1) << "Converter return null frame.";
+    SetState(State::kError);
+    return;
+  }
+  // Although the document of VideoDecoder says "should run |output_cb| as soon
+  // as possible (without thread trampolining)", MojoVideoDecoderService still
+  // assumes the callback is called at original thread.
+  // TODO(akahuang): call the callback directly after updating MojoVDService.
+  client_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(output_cb_, std::move(converted_frame)));
+}
+
+void V4L2SliceVideoDecoder::SetState(State new_state) {
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
+  DVLOGF(3) << "Change state from " << static_cast<int>(state_) << " to "
+            << static_cast<int>(new_state);
+
+  if (state_ == new_state)
+    return;
+  if (state_ == State::kError) {
+    DVLOGF(3) << "Already in kError state.";
+    return;
+  }
+
+  // Check if the state transition is valid.
+  switch (new_state) {
+    case State::kUninitialized:
+      if (state_ != State::kDecoding) {
+        VLOGF(1) << "Should not set to kUninitialized.";
+        new_state = State::kError;
+      }
+      break;
+
+    case State::kDecoding:
+      break;
+
+    case State::kPause:
+      if (state_ != State::kDecoding) {
+        VLOGF(1) << "kPause should only be set when kDecoding.";
+        new_state = State::kError;
+      }
+      break;
+
+    case State::kError:
+      break;
+  }
+
+  if (new_state == State::kError) {
+    VLOGF(1) << "Error occurred.";
+    ClearPendingRequests(DecodeStatus::DECODE_ERROR);
+    return;
+  }
+  state_ = new_state;
+  return;
+}
+
+}  // namespace media
diff --git a/media/gpu/v4l2/v4l2_slice_video_decoder.h b/media/gpu/v4l2/v4l2_slice_video_decoder.h
new file mode 100644
index 0000000..dd1f01c
--- /dev/null
+++ b/media/gpu/v4l2/v4l2_slice_video_decoder.h
@@ -0,0 +1,232 @@
+// 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 MEDIA_GPU_V4L2_V4L2_SLICE_VIDEO_DECODER_H_
+#define MEDIA_GPU_V4L2_V4L2_SLICE_VIDEO_DECODER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind_helpers.h"
+#include "base/containers/queue.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/thread.h"
+#include "media/base/video_decoder.h"
+#include "media/base/video_types.h"
+#include "media/gpu/media_gpu_export.h"
+#include "media/gpu/v4l2/v4l2_decode_surface_handler.h"
+#include "media/gpu/v4l2/v4l2_device.h"
+
+namespace media {
+
+class AcceleratedVideoDecoder;
+class DmabufVideoFramePool;
+class V4L2DecodeSurface;
+class VideoFrameConverter;
+
+class MEDIA_GPU_EXPORT V4L2SliceVideoDecoder : public VideoDecoder,
+                                               public V4L2DecodeSurfaceHandler {
+ public:
+  // Create V4L2SliceVideoDecoder instance. The success of the creation doesn't
+  // ensure V4L2SliceVideoDecoder is available on the device. It will be
+  // determined in Initialize().
+  static std::unique_ptr<VideoDecoder> Create(
+      scoped_refptr<base::SequencedTaskRunner> client_task_runner,
+      std::unique_ptr<DmabufVideoFramePool> frame_pool,
+      std::unique_ptr<VideoFrameConverter> frame_converter);
+
+  // VideoDecoder implementation.
+  std::string GetDisplayName() const override;
+  bool IsPlatformDecoder() const override;
+  int GetMaxDecodeRequests() const override;
+  bool NeedsBitstreamConversion() const override;
+  bool CanReadWithoutStalling() const override;
+
+  void Initialize(const VideoDecoderConfig& config,
+                  bool low_delay,
+                  CdmContext* cdm_context,
+                  InitCB init_cb,
+                  const OutputCB& output_cb,
+                  const WaitingCB& waiting_cb) override;
+  void Reset(base::OnceClosure closure) override;
+  void Decode(scoped_refptr<DecoderBuffer> buffer, DecodeCB decode_cb) override;
+
+  // V4L2DecodeSurfaceHandler implementation.
+  scoped_refptr<V4L2DecodeSurface> CreateSurface() override;
+  bool SubmitSlice(const scoped_refptr<V4L2DecodeSurface>& dec_surface,
+                   const uint8_t* data,
+                   size_t size) override;
+  void DecodeSurface(
+      const scoped_refptr<V4L2DecodeSurface>& dec_surface) override;
+  void SurfaceReady(const scoped_refptr<V4L2DecodeSurface>& dec_surface,
+                    int32_t bitstream_id,
+                    const gfx::Rect& visible_rect,
+                    const VideoColorSpace& /* color_space */) override;
+
+ private:
+  friend class V4L2SliceVideoDecoderTest;
+
+  V4L2SliceVideoDecoder(
+      scoped_refptr<base::SequencedTaskRunner> client_task_runner,
+      scoped_refptr<V4L2Device> device,
+      std::unique_ptr<DmabufVideoFramePool> frame_pool,
+      std::unique_ptr<VideoFrameConverter> frame_converter);
+  ~V4L2SliceVideoDecoder() override;
+  void Destroy() override;
+
+  // Record for the V4L2 input buffer.
+  struct InputRecord;
+  // Record for the V4L2 output buffer.
+  struct OutputRecord;
+  // Request for decoding buffer. Every Decode() call generates 1 DecodeRequest.
+  struct DecodeRequest;
+  // Request for displaying the surface or calling the decode callback.
+  struct OutputRequest;
+
+  enum class State {
+    // Initial state. Transitions to |kDecoding| if Initialize() is successful,
+    // |kError| otherwise.
+    kUninitialized,
+    // Transitions to |kPause| when flushing or changing resolution,
+    // |kError| if any unexpected error occurs.
+    kDecoding,
+    // Transitions to |kDecoding| when flushing or changing resolution is
+    // finished.
+    kPause,
+    // Error state. Cannot transition to other state anymore.
+    kError,
+  };
+
+  // Initialize on decoder thread.
+  void InitializeTask(const VideoDecoderConfig& config,
+                      InitCB init_cb,
+                      const OutputCB& output_cb);
+  // Setup format for V4L2 input buffers.
+  bool SetupInputFormat(uint32_t input_format_fourcc);
+  // Negotiate the pixel output format with V4L2 device. Return fourcc of the
+  // pixel format that is supported by V4L2 device.
+  uint32_t NegotiateOutputFormat();
+  // Setup format for V4L2 output buffers.
+  bool SetupOutputFormat(uint32_t output_format_fourcc);
+
+  // Destroy on decoder thread.
+  void DestroyTask();
+  // Reset on decoder thread.
+  void ResetTask(base::OnceClosure closure);
+  // Clear all pending requests, and call all pending decode callback with
+  // |status| argument.
+  void ClearPendingRequests(DecodeStatus status);
+
+  // Enqueue |request| to the pending decode request queue, and try to decode
+  // from the queue.
+  void EnqueueDecodeTask(std::unique_ptr<DecodeRequest> request);
+  // Try to decode buffer from the pending decode request queue.
+  // This method stops decoding when:
+  // - Run out of surface
+  // - Flushing or changing resolution
+  // Invoke this method again when these situation ends.
+  void PumpDecodeTask();
+  // Try to output surface from |output_request_queue_|.
+  // This method stops outputting surface when the first surface is not dequeued
+  // from the V4L2 device. Invoke this method again when any surface is
+  // dequeued from the V4L2 device.
+  void PumpOutputSurfaces();
+  // Setup the format of V4L2 output buffer, and allocate new buffer set.
+  bool ChangeResolution();
+  // Callback which is called when V4L2 surface is destroyed.
+  void ReuseOutputBuffer(int index);
+
+  // Start streaming V4L2 input and output queues. Attempt to start
+  // |device_poll_thread_| before starting streaming.
+  bool StartStreamV4L2Queue();
+  // Stop streaming V4L2 input and output queues. Stop |device_poll_thread_|
+  // before stopping streaming.
+  bool StopStreamV4L2Queue();
+  // Schedule poll if we have any buffers queued and the poll thread is not
+  // stopped (on surface set change).
+  void SchedulePollTaskIfNeeded();
+  // Ran on device_poll_thread_ to wait for device events.
+  void DevicePollTask();
+  // Try to dequeue input and output buffers from device.
+  void ServiceDeviceTask();
+
+  // Get the next bitsream ID.
+  int32_t GetNextBitstreamId();
+  // Convert the frame and call the output callback.
+  void RunOutputCB(scoped_refptr<VideoFrame> frame);
+  // Call the decode callback and count the number of pending callbacks.
+  void RunDecodeCB(DecodeCB cb, DecodeStatus status);
+  // Change the state and check the state transition is valid.
+  void SetState(State new_state);
+
+  // V4L2 device in use.
+  scoped_refptr<V4L2Device> device_;
+  // VideoFrame manager used to allocate and recycle video frame.
+  std::unique_ptr<DmabufVideoFramePool> frame_pool_;
+  std::unique_ptr<VideoFrameConverter> frame_converter_;
+  // Video decoder used to parse stream headers by software.
+  std::unique_ptr<AcceleratedVideoDecoder> avd_;
+
+  // Client task runner. All public methods of VideoDecoder interface are
+  // executed at this task runner.
+  const scoped_refptr<base::SequencedTaskRunner> client_task_runner_;
+  // Thread to communicate with the device on. Most of internal methods and data
+  // members are manipulated on this thread.
+  const scoped_refptr<base::SequencedTaskRunner> decoder_task_runner_;
+
+  // Thread used to poll the device for events.
+  base::Thread device_poll_thread_;
+
+  // State of the instance.
+  State state_;
+
+  // Parameters for generating output VideoFrame.
+  base::Optional<VideoFrameLayout> frame_layout_;
+  gfx::Rect visible_rect_;
+  gfx::Size natural_size_;
+  // Callbacks passed from Initialize().
+  OutputCB output_cb_;
+  // Callbacks of EOS buffer passed from Decode().
+  DecodeCB flush_cb_;
+
+  // V4L2 input and output queue.
+  scoped_refptr<V4L2Queue> input_queue_;
+  scoped_refptr<V4L2Queue> output_queue_;
+  // Mapping from input_record() of surface to its InputRecord.
+  std::map<size_t, std::unique_ptr<InputRecord>> input_record_map_;
+  // Mapping from output_record() of surface to its OutputRecord.
+  std::map<size_t, std::unique_ptr<OutputRecord>> output_record_map_;
+  // Surfaces enqueued to V4L2 device, mapping from the output_record() of the
+  // surface.
+  std::map<size_t, scoped_refptr<V4L2DecodeSurface>> surfaces_at_device_;
+
+  // Queue of pending decode request.
+  base::queue<std::unique_ptr<DecodeRequest>> decode_request_queue_;
+  // The decode request which is currently processed.
+  std::unique_ptr<DecodeRequest> current_decode_request_;
+  // Queue of pending output request.
+  base::queue<std::unique_ptr<OutputRequest>> output_request_queue_;
+
+  // The number of planes, which is the number of DMA-buf fds we enqueue into
+  // the V4L2 device.
+  size_t num_output_planes_ = 0;
+  // True if the decoder needs bitstream conversion before decoding.
+  bool needs_bitstream_conversion_ = false;
+  // Next bitstream ID.
+  int32_t next_bitstream_buffer_id_ = 0;
+
+  // |weak_this_| must be dereferenced and invalidated on
+  // |decoder_task_runner_|.
+  base::WeakPtr<V4L2SliceVideoDecoder> weak_this_;
+  base::WeakPtrFactory<V4L2SliceVideoDecoder> weak_this_factory_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_V4L2_V4L2_SLICE_VIDEO_DECODER_H_
diff --git a/media/gpu/v4l2/v4l2_stateful_workaround.cc b/media/gpu/v4l2/v4l2_stateful_workaround.cc
index 221856a..8ce8798 100644
--- a/media/gpu/v4l2/v4l2_stateful_workaround.cc
+++ b/media/gpu/v4l2/v4l2_stateful_workaround.cc
@@ -50,27 +50,29 @@
 std::unique_ptr<V4L2StatefulWorkaround>
 SupportResolutionChecker::CreateIfNeeded(V4L2Device::Type device_type,
                                          VideoCodecProfile profile) {
-  scoped_refptr<V4L2Device> device = V4L2Device::Create();
-  if (device_type == V4L2Device::Type::kDecoder && profile >= VP8PROFILE_MIN &&
-      profile <= VP8PROFILE_MAX) {
-    if (!device->Open(V4L2Device::Type::kDecoder, V4L2_PIX_FMT_VP8)) {
-      VPLOGF(1) << "Failed to open device for profile: " << profile
-                << " fourcc: " << FourccToString(V4L2_PIX_FMT_VP8);
-      return nullptr;
-    }
-
-    // Get the driver name.
-    struct v4l2_capability caps;
-    if (device->Ioctl(VIDIOC_QUERYCAP, &caps) != 0) {
-      VPLOGF(1) << "ioctl() failed: VIDIOC_QUERYCAP"
-                << ", caps check failed: 0x" << std::hex << caps.capabilities;
-      return nullptr;
-    }
-    constexpr char go2001[] = "go2001";
-    if (strcmp(reinterpret_cast<const char*>(caps.driver), go2001))
-      return nullptr;
+  if (device_type != V4L2Device::Type::kDecoder || profile < VP8PROFILE_MIN ||
+      profile > VP8PROFILE_MAX) {
+    return nullptr;
   }
 
+  scoped_refptr<V4L2Device> device = V4L2Device::Create();
+  if (!device->Open(V4L2Device::Type::kDecoder, V4L2_PIX_FMT_VP8)) {
+    VPLOGF(1) << "Failed to open device for profile: " << profile
+              << " fourcc: " << FourccToString(V4L2_PIX_FMT_VP8);
+    return nullptr;
+  }
+
+  // Get the driver name.
+  struct v4l2_capability caps;
+  if (device->Ioctl(VIDIOC_QUERYCAP, &caps) != 0) {
+    VPLOGF(1) << "ioctl() failed: VIDIOC_QUERYCAP"
+              << ", caps check failed: 0x" << std::hex << caps.capabilities;
+    return nullptr;
+  }
+  constexpr char go2001[] = "go2001";
+  if (strcmp(reinterpret_cast<const char*>(caps.driver), go2001))
+    return nullptr;
+
   constexpr uint32_t supported_input_fourccs[] = {
       V4L2_PIX_FMT_VP8,
   };
diff --git a/media/mojo/clients/mojo_audio_decoder_unittest.cc b/media/mojo/clients/mojo_audio_decoder_unittest.cc
index 51fce89e..f3da287 100644
--- a/media/mojo/clients/mojo_audio_decoder_unittest.cc
+++ b/media/mojo/clients/mojo_audio_decoder_unittest.cc
@@ -9,12 +9,12 @@
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread.h"
 #include "media/base/audio_decoder_config.h"
 #include "media/base/audio_timestamp_helper.h"
 #include "media/base/decoder_buffer.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/media_util.h"
 #include "media/base/mock_filters.h"
 #include "media/base/test_helpers.h"
@@ -27,6 +27,8 @@
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::base::test::RunCallback;
+using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::DoAll;
 using ::testing::InSequence;
diff --git a/media/mojo/clients/mojo_renderer_unittest.cc b/media/mojo/clients/mojo_renderer_unittest.cc
index 46390de..1a81694 100644
--- a/media/mojo/clients/mojo_renderer_unittest.cc
+++ b/media/mojo/clients/mojo_renderer_unittest.cc
@@ -8,13 +8,13 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/test_message_loop.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/timer/elapsed_timer.h"
 #include "media/base/cdm_config.h"
 #include "media/base/cdm_context.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/mock_filters.h"
 #include "media/base/test_helpers.h"
 #include "media/cdm/default_cdm_factory.h"
@@ -32,6 +32,8 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 
+using ::base::test::RunCallback;
+using ::base::test::RunClosure;
 using ::testing::_;
 using ::testing::DoAll;
 using ::testing::Return;
diff --git a/media/mojo/services/mojo_cdm_proxy_unittest.cc b/media/mojo/services/mojo_cdm_proxy_unittest.cc
index 0eec104..da416a9 100644
--- a/media/mojo/services/mojo_cdm_proxy_unittest.cc
+++ b/media/mojo/services/mojo_cdm_proxy_unittest.cc
@@ -10,8 +10,8 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/test_message_loop.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/mock_filters.h"
 #include "media/cdm/cdm_proxy_context.h"
 #include "media/mojo/interfaces/cdm_proxy.mojom.h"
diff --git a/media/mojo/test/mojo_video_decoder_integration_test.cc b/media/mojo/test/mojo_video_decoder_integration_test.cc
index cba2b8e..56d2d256 100644
--- a/media/mojo/test/mojo_video_decoder_integration_test.cc
+++ b/media/mojo/test/mojo_video_decoder_integration_test.cc
@@ -15,6 +15,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/time/time.h"
@@ -22,7 +23,6 @@
 #include "media/base/decode_status.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/decrypt_config.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/media_log.h"
 #include "media/base/mock_media_log.h"
 #include "media/base/test_helpers.h"
@@ -38,11 +38,12 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/color_space.h"
 
+using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::AtLeast;
 using ::testing::HasSubstr;
-using ::testing::Invoke;
 using ::testing::InSequence;
+using ::testing::Invoke;
 using ::testing::Mock;
 using ::testing::Return;
 using ::testing::SaveArg;
diff --git a/media/renderers/BUILD.gn b/media/renderers/BUILD.gn
index 838b4d8..3340b86d 100644
--- a/media/renderers/BUILD.gn
+++ b/media/renderers/BUILD.gn
@@ -89,5 +89,7 @@
     "//testing/gtest",
     "//third_party/libyuv",
     "//ui/gfx",
+    "//ui/gl",
+    "//ui/gl:test_support",
   ]
 }
diff --git a/media/renderers/audio_renderer_impl_unittest.cc b/media/renderers/audio_renderer_impl_unittest.cc
index c553b7c..8a692816 100644
--- a/media/renderers/audio_renderer_impl_unittest.cc
+++ b/media/renderers/audio_renderer_impl_unittest.cc
@@ -15,12 +15,12 @@
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "media/base/audio_buffer_converter.h"
 #include "media/base/fake_audio_renderer_sink.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/media_client.h"
 #include "media/base/media_util.h"
 #include "media/base/mock_audio_renderer_sink.h"
@@ -29,6 +29,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::base::TimeDelta;
+using ::base::test::RunCallback;
+using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::Return;
 using ::testing::SaveArg;
diff --git a/media/renderers/decrypting_renderer_unittest.cc b/media/renderers/decrypting_renderer_unittest.cc
index 69634d8..728371c 100644
--- a/media/renderers/decrypting_renderer_unittest.cc
+++ b/media/renderers/decrypting_renderer_unittest.cc
@@ -7,16 +7,17 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/single_thread_task_runner.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
 #include "media/base/demuxer_stream.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/media_util.h"
 #include "media/base/mock_filters.h"
 #include "media/base/test_helpers.h"
 #include "media/filters/decrypting_media_resource.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
+using ::base::test::RunCallback;
 using ::testing::_;
 using ::testing::AnyNumber;
 using ::testing::Invoke;
diff --git a/media/renderers/paint_canvas_video_renderer_unittest.cc b/media/renderers/paint_canvas_video_renderer_unittest.cc
index 9dccbedb..fe361b9 100644
--- a/media/renderers/paint_canvas_video_renderer_unittest.cc
+++ b/media/renderers/paint_canvas_video_renderer_unittest.cc
@@ -13,6 +13,8 @@
 #include "cc/paint/skia_paint_canvas.h"
 #include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/test/test_context_provider.h"
+#include "components/viz/test/test_gpu_service_holder.h"
+#include "components/viz/test/test_in_process_context_provider.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/gles2_interface_stub.h"
 #include "gpu/command_buffer/common/capabilities.h"
@@ -28,6 +30,8 @@
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 #include "ui/gfx/geometry/rect_f.h"
+#include "ui/gl/gl_implementation.h"
+#include "ui/gl/test/gl_surface_test_support.h"
 
 using media::VideoFrame;
 
@@ -74,6 +78,13 @@
   PaintCanvasVideoRendererTest();
   ~PaintCanvasVideoRendererTest() override;
 
+  void SetUp() override { gl::GLSurfaceTestSupport::InitializeOneOff(); }
+
+  void TearDown() override {
+    viz::TestGpuServiceHolder::ResetInstance();
+    gl::GLSurfaceTestSupport::ShutdownGL();
+  }
+
   // Paints to |canvas| using |renderer_| without any frame data.
   void PaintWithoutFrame(cc::PaintCanvas* canvas);
 
@@ -783,4 +794,120 @@
       2 /*xoffset*/, 1 /*yoffset*/, false /*flip_y*/, true);
 }
 
+TEST_F(PaintCanvasVideoRendererTest, CopyVideoFrameYUVDataToGLTexture) {
+  gl::DisableNullDrawGLBindings enable_pixels;
+
+  auto media_context = base::MakeRefCounted<viz::TestInProcessContextProvider>(
+      false /* enable_oop_rasterization */, false /* support_locking */);
+  gpu::ContextResult result = media_context->BindToCurrentThread();
+  ASSERT_EQ(result, gpu::ContextResult::kSuccess);
+
+  auto destination_context =
+      base::MakeRefCounted<viz::TestInProcessContextProvider>(
+          false /* enable_oop_rasterization */, false /* support_locking */);
+  result = destination_context->BindToCurrentThread();
+  ASSERT_EQ(result, gpu::ContextResult::kSuccess);
+
+  gpu::gles2::GLES2Interface* destination_gl = destination_context->ContextGL();
+  DCHECK(destination_gl);
+  GLenum target = GL_TEXTURE_2D;
+  GLuint texture = 0;
+  destination_gl->GenTextures(1, &texture);
+  destination_gl->BindTexture(target, texture);
+
+  renderer_.CopyVideoFrameYUVDataToGLTexture(
+      media_context.get(), destination_gl, *cropped_frame(), target, texture,
+      GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 0, false /* premultiply_alpha */,
+      false /* flip_y */);
+
+  gfx::Size expected_size = cropped_frame()->visible_rect().size();
+  size_t pixel_count = expected_size.width() * expected_size.height();
+
+  GLuint fbo = 0;
+  destination_gl->GenFramebuffers(1, &fbo);
+  destination_gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
+  destination_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                                       GL_TEXTURE_2D, texture, 0);
+  auto pixels = std::make_unique<uint8_t[]>(pixel_count * 4);
+  uint8_t* raw_pixels = pixels.get();
+  destination_gl->ReadPixels(0, 0, expected_size.width(),
+                             expected_size.height(), GL_RGBA, GL_UNSIGNED_BYTE,
+                             raw_pixels);
+  destination_gl->DeleteFramebuffers(1, &fbo);
+
+  auto get_color = [raw_pixels, expected_size](size_t x, size_t y) {
+    uint8_t* p = raw_pixels + (expected_size.width() * y + x) * 4;
+    return SkColorSetARGB(p[3], p[0], p[1], p[2]);
+  };
+
+  // Avoid checking around the seams.
+  EXPECT_EQ(SK_ColorBLACK, get_color(0, 0));
+  EXPECT_EQ(SK_ColorRED, get_color(3, 0));
+  EXPECT_EQ(SK_ColorRED, get_color(7, 0));
+  EXPECT_EQ(SK_ColorGREEN, get_color(0, 3));
+  EXPECT_EQ(SK_ColorGREEN, get_color(0, 5));
+  EXPECT_EQ(SK_ColorBLUE, get_color(3, 3));
+  EXPECT_EQ(SK_ColorBLUE, get_color(7, 5));
+
+  destination_gl->DeleteTextures(1, &texture);
+}
+
+TEST_F(PaintCanvasVideoRendererTest, CopyVideoFrameYUVDataToGLTexture_FlipY) {
+  gl::DisableNullDrawGLBindings enable_pixels;
+
+  auto media_context = base::MakeRefCounted<viz::TestInProcessContextProvider>(
+      false /* enable_oop_rasterization */, false /* support_locking */);
+  gpu::ContextResult result = media_context->BindToCurrentThread();
+  ASSERT_EQ(result, gpu::ContextResult::kSuccess);
+
+  auto destination_context =
+      base::MakeRefCounted<viz::TestInProcessContextProvider>(
+          false /* enable_oop_rasterization */, false /* support_locking */);
+  result = destination_context->BindToCurrentThread();
+  ASSERT_EQ(result, gpu::ContextResult::kSuccess);
+
+  gpu::gles2::GLES2Interface* destination_gl = destination_context->ContextGL();
+  DCHECK(destination_gl);
+  GLenum target = GL_TEXTURE_2D;
+  GLuint texture = 0;
+  destination_gl->GenTextures(1, &texture);
+  destination_gl->BindTexture(target, texture);
+
+  renderer_.CopyVideoFrameYUVDataToGLTexture(
+      media_context.get(), destination_gl, *cropped_frame(), target, texture,
+      GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 0, false /* premultiply_alpha */,
+      true /* flip_y */);
+
+  gfx::Size expected_size = cropped_frame()->visible_rect().size();
+  size_t pixel_count = expected_size.width() * expected_size.height();
+
+  GLuint fbo = 0;
+  destination_gl->GenFramebuffers(1, &fbo);
+  destination_gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
+  destination_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                                       GL_TEXTURE_2D, texture, 0);
+  auto pixels = std::make_unique<uint8_t[]>(pixel_count * 4);
+  uint8_t* raw_pixels = pixels.get();
+  destination_gl->ReadPixels(0, 0, expected_size.width(),
+                             expected_size.height(), GL_RGBA, GL_UNSIGNED_BYTE,
+                             raw_pixels);
+  destination_gl->DeleteFramebuffers(1, &fbo);
+
+  auto get_color = [raw_pixels, expected_size](size_t x, size_t y) {
+    uint8_t* p = raw_pixels + (expected_size.width() * y + x) * 4;
+    return SkColorSetARGB(p[3], p[0], p[1], p[2]);
+  };
+
+  // Avoid checking around the seams.
+  EXPECT_EQ(SK_ColorBLACK, get_color(0, 5));
+  EXPECT_EQ(SK_ColorRED, get_color(3, 5));
+  EXPECT_EQ(SK_ColorRED, get_color(7, 5));
+  EXPECT_EQ(SK_ColorGREEN, get_color(0, 2));
+  EXPECT_EQ(SK_ColorGREEN, get_color(0, 0));
+  EXPECT_EQ(SK_ColorBLUE, get_color(3, 2));
+  EXPECT_EQ(SK_ColorBLUE, get_color(7, 0));
+
+  destination_gl->DeleteTextures(1, &texture);
+}
+
 }  // namespace media
diff --git a/media/renderers/renderer_impl_unittest.cc b/media/renderers/renderer_impl_unittest.cc
index 445a37c3..82b08ce 100644
--- a/media/renderers/renderer_impl_unittest.cc
+++ b/media/renderers/renderer_impl_unittest.cc
@@ -12,15 +12,17 @@
 #include "base/macros.h"
 #include "base/optional.h"
 #include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/mock_filters.h"
 #include "media/base/test_helpers.h"
 #include "media/renderers/renderer_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::base::test::RunCallback;
+using ::base::test::RunClosure;
 using ::testing::_;
 using ::testing::DoAll;
 using ::testing::InSequence;
diff --git a/media/renderers/video_renderer_impl_unittest.cc b/media/renderers/video_renderer_impl_unittest.cc
index dbea330..4c6e7d8 100644
--- a/media/renderers/video_renderer_impl_unittest.cc
+++ b/media/renderers/video_renderer_impl_unittest.cc
@@ -20,11 +20,11 @@
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/lock.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "media/base/data_buffer.h"
-#include "media/base/gmock_callback_support.h"
 #include "media/base/limits.h"
 #include "media/base/media_switches.h"
 #include "media/base/media_util.h"
@@ -38,6 +38,9 @@
 #include "testing/gmock_mutant.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::base::test::RunCallback;
+using ::base::test::RunClosure;
+using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::AnyNumber;
 using ::testing::CreateFunctor;
diff --git a/mojo/core/README.md b/mojo/core/README.md
new file mode 100644
index 0000000..050f5e5
--- /dev/null
+++ b/mojo/core/README.md
@@ -0,0 +1,309 @@
+# Mojo Core Overview
+
+[TOC]
+
+## Overview
+
+Mojo Core implements an IPC mechanism based on Message routing over a network
+of "Nodes". A Node in typical usage corresponds to a system process.
+"Messages" are exchanged between pairs of "Ports", where a pair of Ports
+represent a "MessagePipe" from the outside. A MessagePipe provides
+reliable in-order delivery of Messages.
+Messages can be used to transport Ports and platform handles (file descriptors,
+Mach ports, Windows handles, etc.) between Nodes.
+Communication between Nodes is done through platform IPC channels, such as
+AF_UNIX sockets, pipes or Fuchsia channels. A new Node has to be invited into
+the network by giving it an IPC channel to an existing Node in the network,
+or else provided with a way to establish one, such as e.g. the name/path to a
+listening pipe or an AF_UNIX socket.
+
+Mojo Core also provides a shared memory buffer primitive and assists in the
+creation and transport of shared memory buffers as required by the underlying
+operating system and process permissions.
+
+A single Node in the network is termed the "Broker".
+The Broker has some special responsibilities in the network, notably the Broker
+is responsible for:
+  - Introducing a pair of Nodes in the network to one another to establish a
+    direct IPC link between them.
+  - Copying handles between Nodes, where they cannot themselves do so. This is
+    e.g. the case for sandboxed processes on Windows.
+  - Creating shared memory objects for processes that can't themselves do so.
+
+Mojo Core exposes two API sets:
+  1. The [C system API](../public/c/system/README.md).
+  1. The [embedder API](embedder/README.md).
+
+This document is to describe how these APIs are implemented by Mojo Core.
+
+## Layering
+
+This image provides an overview of how the Mojo Core implementation is layered:
+
+![Mojo Core Layering](doc/layering.png)
+
+The C system API is a stable, versioned API, exposed through a singleton
+structure containing C function thunks. This allows multiple clients in the same
+application to use a single embedder-provided instance of Mojo, and allows these
+clients to be versioned and distributed independently of Mojo.
+Using C thunks also makes it easier to provide compatibility with other
+programming languages than C and C++.
+
+When clients invoke on the Mojo public API, they go through the dispatch
+functions implemented in [dispatcher.cc](dispatcher.cc), which in turn forwards
+the call to a global instance of [mojo::core::Core](core.h).
+Core further dispatches the calls to the implementation instance, which is
+either a [mojo::core::Dispatcher](dispatcher.h), or a
+[mojo::core::UserMessageImpl](user_message_impl.h).
+In the case of a Dispatcher, the incoming MojoHandle is looked up in the
+[mojo::core::HandleTable](handle_table.h), which returns the Dispatcher
+corresponding to the MojoHandle.
+
+The public API exposes the following concepts as MojoHandles:
+  1. [DataPipeConsumer](data_pipe_consumer_dispatcher.h).
+  1. [DataPipeProducer](data_pipe_producer_dispatcher.h).
+  1. [Invitation](invitation_dispatcher.h).
+  1. [MessagePipe](message_pipe_dispatcher.h).
+  1. [PlatformHandle](platform_handle_dispatcher.h).
+  1. [SharedBuffer](shared_buffer_dispatcher.h).
+  1. [Watcher](watcher_dispatcher.h) - this is know as "Trap" in the public API.
+
+These are implemented as subclasses of [mojo::core::Dispatcher](dispatcher.h).
+The Dispatcher class implements the union of all possible operations on a
+MojoHandle, and the various subclasses implement the subset appropriate to their
+type.
+
+Additionally the public API exposes Messages as MojoMessageHandles, which are
+simply an instance of a [mojo::core::UserMessageImpl](user_message_impl.h).
+
+## Threading model
+
+Mojo Core is multi-threaded, so any API can be used from any thread in a
+process. Receiving Messages from other Nodes is done on the "IO Thread", which
+is an [embedder-supplied task runner](embedder/README.md#ipc-initialization).
+Sending Messages to other Nodes is typically done on the sending thread.
+However, if the native IPC channel's buffer is full, the outgoing Message will
+be queued and subsequently sent on the "IO Thread".
+
+## Event Dispatching
+
+During the processing of an API call and during activity in the NodeController,
+various entities may change states, which can lead to the generation of
+[TrapEvents](../public/c/system/README.md#Signals-Traps) to the user.
+The [mojo::core::RequestContext](request_context.h) takes care of aggregating
+Watchers that need dispatching and issuing outstanding TrapEvents as the last
+RequestContext in a thread goes out of scope.
+
+This avoids calling out to user code from deep within Core, and the resulting
+reentrancy that might ensue.
+
+## Messaging implementation
+
+Mojo Core is implemented on top of "[Ports](ports)" and is an embedder of Ports.
+
+Ports provides the base abstractions of addressing,
+[mojo::core::ports::Node](ports/node.h) and
+[mojo::core::ports::Port](ports/port.h) as well as the messaging and event
+handling for maintaining and transporting Port pairs across Nodes.
+
+All routing and IPC is however delegated to the embedder, Mojo Core, via
+the [mojo::core::NodeController](node_controller.h) which owns an instance of
+Node, and implements its delegate interface.
+
+### Naming and Addressing
+
+Nodes are named by a large (128 bit) random number. Ports are likewise named
+by a large (128 bit) random number.
+Messages are directed at a Port, and so are addressed by a (Node:Port) pair.
+
+Each Port has precisely one "conjugate" Port at any point in time. When a
+Message is sent through a Port, its conjugate Port is the implicit destination,
+and this is a symmetrical relationship. The address of the conjugate Port can
+change as it is moved between nodes.
+In practice a Port is renamed each time it's moved between Nodes, and so changes
+in both components of the (Node:Port) address pair.
+
+Note that since each Port has a single conjugate Port at any time, en-route
+Messages are addressed only with the destination Port address, as the source
+is implicit.
+
+### Routing and Topology
+
+A process network initially starts with only the Broker process Node.
+New Nodes must be introduced to the broker by some means. This can be done by
+inheriting an IPC handle into a newly created process, or by providing them
+the name of a listening IPC channel they can connect to.
+
+Each Node thus starts with only a direct IPC connection to a single other Node
+in the network. The first time a Node tries to forward a Message to a Node it
+doesn't have a direct IPC channel with, it will send a message to the broker to
+request an introduction to the new peer Node.
+
+If the broker knows of the peer Node, it will construct a new native IPC
+channel, then hand one end of it to the requesting Node and one end to the new
+peer Node. This will result in a direct Node-to-Node link between the two, and
+so the topology of a process network will trend towards a fully connected graph.
+
+### Messages
+
+A UserMessageImpl is the unit of transport in Mojo Core.
+This is a collection of
+  1. Handles,
+  1. Ports,
+  1. User data.
+
+Mojo Core takes care of transferring and serializing and deserializing Handles
+and Ports.
+The user data on the other hand has to be serialized and deserialized by the
+user. Mojo Core optimizes this by allowing the user data to be serialized only
+at need, e.g. when a Message is routed to another Node.
+
+### Port
+
+A Node maintains a set of Ports local to that Node.
+
+Logically, each Port has a conjugate Port where all its Messages are destined.
+However, when a Port is transferred to a different Node, the newly created Port
+on the destination Node will have the transferred Port as its next-hop "Proxy"
+Port it sends Messages to. The Proxy Port is responsible for delivering
+in-progress Messages across to the new destination Port, and may buffer Messages
+to that end. Once a Proxy Port has delivered all in-progress Messages, it
+is dissolved and Messages start flowing through the next Peer Port in turn,
+which may be the conjugate Port or another Proxy Port.
+
+Each Port maintains state relating to its:
+  - Current Peer Port, which may be a Proxy Port, but in the stable state will
+    be its conjugate Port.
+  - Its current state of messaging, which changes as Ports move across Nodes.
+  - Its outgoing Message state, notably Message serial numbers.
+  - Its incoming Message queue.
+
+The unit of messaging at this level is the
+[mojo::core::ports::Event](ports/event.h), which has several sub-types relating
+to the business of maintaining and transporting Ports and keeping their
+Peer Port address up to date.
+
+The UserMessageEvent subclass is the event type that carries all user messaging.
+A UserMessageEvent owns a
+[mojo::core::ports::UserMessage](ports/user_message.h), which is the interface
+to the embedder's Message type. Mojo Core implements this in
+[mojoe::core::UserMessageImpl](user_message_impl.h). Note that at the Ports
+level, this is simply opaque user data. It's the Mojo Core embedder that is
+aware of the handles and data attached to a UserMessageImpl.
+
+Note that if a Port is transferred across multiple Nodes, it may end up with a
+multi-leg Peer relationship. As result, and because different Messages for the
+same Port may take different routes through the process network, out of order
+Message delivery is possible. The incoming
+[mojo::core::ports::MessageQueue](ports/message_queue.h) takes care of
+re-ordering incoming Messages.
+
+The Node does all Message handling and delivery for its local Ports, but
+delegates all routing to its delegate through the
+[mojo::core::ports::NodeDelegate](ports/node_delegate.h) interface.
+Mojo Core implements this in [mojo::core::NodeController](node_controller.h).
+
+### Node to Node IPC
+
+The business of handling Node to Node IPC is primarily handled in
+[mojo::core::NodeChannel](node_channel.h) as well as
+[mojo::core::Channel](channel.h) and its OS-specific subclasses by exchanging
+[mojo::core::Channel::Message](channel.h)s.
+
+Note that a Channel::Message always goes from a source Node to a destination
+Node through a Channel (strictly speaking one on each side). As the Channels on
+either side (or the OS-provided IPC mechanism) conspire to copy the handles
+across, the processes involved in the handle transfer are always implicit.
+This means that a low-privilege or sandboxed process can only send its own
+handles in a Message, and it's not possible to abuse this to acquire another
+process's handles.
+
+A NodeChannel wraps a Channel that communicates to and from another given Node.
+It implements all messaging to another specific Node, both relating to general
+messaging (see NodeChannel::OnEventMessage), as well as messaging relating to
+invitations and Port management.
+Since the NodeChannel is associated with a single given Node, the peer Node name
+is an implicit given in any incoming Message from the Channel.
+
+The Channel takes care of maintaining and servicing a queue of Messages outgoing
+to the peer node, as well as reading and parsing the next incoming Message from
+the IPC channel.
+Depending on the platform and the Node topology, it may also take care of moving
+outgoing and incoming handles between the processes hosting the Nodes on either
+side of the Channel.
+
+Note that on Windows, the Broker always takes care of handle copying, as the
+Broker will typically be the only process with sufficient privilege to copy
+handles. This means that any Message carrying handles is routed through the
+Broker on Windows.
+
+### Packet Framing
+
+An in-transit Message on an IPC channel will be serialized as a
+[mojo::core::Channel::Message](channel.h).
+
+For user Messages, this will contain:
+  1. A [mojo::core::Channel::Message::Header](channel.h) containing:
+    - The length of the full Message
+    - The length of the header.
+    - The Message type.
+    - The number of handles.
+  2. The Platform handles.
+  3. The serialized [mojo::core::ports::Event](ports/event.h). Specifically for
+     a UserMessageEvent:
+    - [mojo::core::ports::SerializedHeader](ports/event.cc).
+    - [mojo::core::ports::UserMessageEventData](ports/event.cc).
+    - Ports; num_ports times core::Ports::PortDescriptor.
+    - The serialized user data.
+
+## Buffering
+
+Message buffering in Mojo Core primarily occurs at two levels:
+  - In the incoming Message queue of a receiving Port.
+  - In the outgoing queue for an IPC Channel to a peer Node.
+
+There is also transient buffering of Messages to a new Peer Node while
+introductions are in-flight.
+Finally, while a Port is in transport to different node, it buffers inbound data
+until the destination Node accepts the new Peer, at which time any buffered data
+is forwarded to the new Port in the destination Node.
+
+Mojo Core does implement quotas on Port receiving queue Message number and byte
+size. If the receive length quota is exceeded on a Port, it signals an
+over-quota TrapEvent on the receiving Port. This doesn't however limit buffering
+as the caller needs to handle the TrapEvent in some way - most likely by simply
+closing the Port.
+
+Since Mojo Core does not limit buffering, the producers and consumers in a
+process network must be balanced.
+If a producer runs wild, the producer's process may buffer an arbirarly large
+number of Messages (and bytes) in the outgoing Message queue on the IPC channel
+to the consumer's Node.
+Alternatively the consumer's process may buffer an arbitrarily large
+number of Messages (and bytes) in the incoming Message queue on the consumer's
+Port.
+
+## Security
+
+Mojo can be viewed as a Capability system, where a Port is a Capability.
+If a Node can name a Port, that Port can be considered a granted Capability
+to that Node. Many Ports will grant other Ports upon request, and so the
+transitive closure of those Ports can be considered in the set of granted
+Capabilities to a Node.
+
+Native handles can also be viewed as Capabilities, and so any native handle
+reachable from the set of Ports granted to a Node can also be considered in
+the Node's set of granted Capabilities.
+
+There's however a significant difference between Ports and native handles, in
+that native handles are kernel-mediated capabilities. This means there's no way
+for a process that doesn't hold a handle to operate on it.
+
+Mojo Ports, in contrast, are a pure user-mode construct and any Node can send
+any Message to any Port of any other Node so long as it has knowledge of the
+Port and Node names. If it doesn't already have an IPC channel to that node,
+it can either send the Message through the Broker, or request an invitation to
+the destination Node from the Broker and then send the Message directly.
+
+It is therefore important not to leak Port names into Nodes that shouldn't be
+granted the corresponding Capability.
diff --git a/mojo/core/doc/layering.png b/mojo/core/doc/layering.png
new file mode 100644
index 0000000..72c6d0b
--- /dev/null
+++ b/mojo/core/doc/layering.png
Binary files differ
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index 7ba10d4..e5d45f8b 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -188,6 +188,7 @@
     "sync_handle_registry.h",
     "sync_handle_watcher.h",
     "thread_safe_interface_ptr.h",
+    "unique_associated_receiver_set.h",
     "unique_ptr_impl_ref_traits.h",
     "unique_receiver_set.h",
   ]
diff --git a/mojo/public/cpp/bindings/tests/associated_interface_unittest.cc b/mojo/public/cpp/bindings/tests/associated_interface_unittest.cc
index 3e3fcff..18ee64a 100644
--- a/mojo/public/cpp/bindings/tests/associated_interface_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/associated_interface_unittest.cc
@@ -27,24 +27,11 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/shared_associated_remote.h"
-#include "mojo/public/cpp/bindings/unique_receiver_set.h"
+#include "mojo/public/cpp/bindings/unique_associated_receiver_set.h"
 #include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
 #include "mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-// TODO(crbug.com/969789): Fix memory leaks in tests and re-enable on LSAN.
-#ifdef LEAK_SANITIZER
-#define MAYBE_ReceiverWaitAndPauseWhenNoAssociatedInterfaces \
-  DISABLED_ReceiverWaitAndPauseWhenNoAssociatedInterfaces
-#define MAYBE_PassAssociatedInterfaces DISABLED_PassAssociatedInterfaces
-#define MAYBE_SharedAssociatedRemote DISABLED_SharedAssociatedRemote
-#else
-#define MAYBE_ReceiverWaitAndPauseWhenNoAssociatedInterfaces \
-  ReceiverWaitAndPauseWhenNoAssociatedInterfaces
-#define MAYBE_PassAssociatedInterfaces PassAssociatedInterfaces
-#define MAYBE_SharedAssociatedRemote SharedAssociatedRemote
-#endif
-
 namespace mojo {
 namespace test {
 namespace {
@@ -53,6 +40,7 @@
 
 class IntegerSenderImpl : public IntegerSender {
  public:
+  IntegerSenderImpl() = default;
   explicit IntegerSenderImpl(PendingAssociatedReceiver<IntegerSender> receiver)
       : receiver_(this, std::move(receiver)) {}
 
@@ -71,7 +59,7 @@
   AssociatedReceiver<IntegerSender>* receiver() { return &receiver_; }
 
  private:
-  AssociatedReceiver<IntegerSender> receiver_;
+  AssociatedReceiver<IntegerSender> receiver_{this};
   base::RepeatingCallback<void(int32_t)> notify_send_method_called_;
 };
 
@@ -84,9 +72,8 @@
   ~IntegerSenderConnectionImpl() override = default;
 
   void GetSender(PendingAssociatedReceiver<IntegerSender> receiver) override {
-    IntegerSenderImpl* sender_impl = new IntegerSenderImpl(std::move(receiver));
-    sender_impl->receiver()->set_disconnect_handler(
-        base::BindOnce(&DeleteSender, sender_impl));
+    DCHECK(receiver.is_valid());
+    senders_.Add(std::make_unique<IntegerSenderImpl>(), std::move(receiver));
   }
 
   void AsyncGetSender(AsyncGetSenderCallback callback) override {
@@ -98,9 +85,8 @@
   Receiver<IntegerSenderConnection>* receiver() { return &receiver_; }
 
  private:
-  static void DeleteSender(IntegerSenderImpl* sender) { delete sender; }
-
   Receiver<IntegerSenderConnection> receiver_;
+  UniqueAssociatedReceiverSet<IntegerSender> senders_;
 };
 
 class AssociatedInterfaceTest : public testing::Test {
@@ -509,7 +495,7 @@
   }
 }
 
-TEST_F(AssociatedInterfaceTest, MAYBE_PassAssociatedInterfaces) {
+TEST_F(AssociatedInterfaceTest, PassAssociatedInterfaces) {
   Remote<IntegerSenderConnection> connection_remote;
   IntegerSenderConnectionImpl connection(
       connection_remote.BindNewPipeAndPassReceiver());
@@ -543,7 +529,7 @@
 }
 
 TEST_F(AssociatedInterfaceTest,
-       MAYBE_ReceiverWaitAndPauseWhenNoAssociatedInterfaces) {
+       ReceiverWaitAndPauseWhenNoAssociatedInterfaces) {
   Remote<IntegerSenderConnection> connection_remote;
   IntegerSenderConnectionImpl connection(
       connection_remote.BindNewPipeAndPassReceiver());
@@ -970,7 +956,7 @@
   run_loop.Run();
 }
 
-TEST_F(AssociatedInterfaceTest, MAYBE_SharedAssociatedRemote) {
+TEST_F(AssociatedInterfaceTest, SharedAssociatedRemote) {
   Remote<IntegerSenderConnection> connection_remote;
   IntegerSenderConnectionImpl connection(
       connection_remote.BindNewPipeAndPassReceiver());
diff --git a/mojo/public/cpp/bindings/unique_associated_receiver_set.h b/mojo/public/cpp/bindings/unique_associated_receiver_set.h
new file mode 100644
index 0000000..cabffa4
--- /dev/null
+++ b/mojo/public/cpp/bindings/unique_associated_receiver_set.h
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_UNIQUE_ASSOCIATED_RECEIVER_SET_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_UNIQUE_ASSOCIATED_RECEIVER_SET_H_
+
+#include "mojo/public/cpp/bindings/associated_receiver.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,
+// there is a dedicated unique_ptr<T> owned by the set and receiving messages.
+//
+// Each owned implementation of T has its lifetime automatically managed by the
+// UniqueAssociatedReceiverSet, destroying an instance whenever its receiver is
+// disconnected because the remote endpoint intentionally hung up, crashed, or
+// sent malformed message.
+template <typename Interface,
+          typename ContextType = void,
+          typename Deleter = std::default_delete<Interface>>
+using UniqueAssociatedReceiverSet = ReceiverSetBase<
+    AssociatedReceiver<Interface, UniquePtrImplRefTraits<Interface, Deleter>>,
+    ContextType>;
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_UNIQUE_ASSOCIATED_RECEIVER_SET_H_
diff --git a/mojo/public/js/interface_support.js b/mojo/public/js/interface_support.js
index 77ab0f9..5c5423cb 100644
--- a/mojo/public/js/interface_support.js
+++ b/mojo/public/js/interface_support.js
@@ -11,7 +11,7 @@
 
 
 /**
- * Handles incoming interface control messages on a proxy or router endpoint.
+ * Handles incoming interface control messages on a remote or router endpoint.
  */
 mojo.internal.interfaceSupport.ControlMessageHandler = class {
   /** @param {!MojoHandle} handle */
@@ -72,8 +72,8 @@
 };
 
 /**
- * Captures metadata about a request which was sent by a local proxy, for which
- * a response is expected.
+ * Captures metadata about a request which was sent by a remote, for which a
+ * response is expected.
  */
 mojo.internal.interfaceSupport.PendingResponse = class {
   /**
@@ -150,16 +150,16 @@
 };
 
 /**
- * Generic helper used to implement all generated proxy classes. Knows how to
+ * Generic helper used to implement all generated remote classes. Knows how to
  * serialize requests and deserialize their replies, both according to
  * declarative message structure specs.
  * @template T
  * @export
  */
-mojo.internal.interfaceSupport.InterfaceProxyBase = class {
+mojo.internal.interfaceSupport.InterfaceRemoteBase = class {
   /**
    * @param {!function(new:T, !MojoHandle)} requestType
-   * @param {MojoHandle=} opt_handle The message pipe handle to use as a proxy
+   * @param {MojoHandle=} opt_handle The message pipe handle to use as a remote
    *     endpoint. If null, this object must be bound with bindHandle before
    *     it can be used to send any messages.
    * @public
@@ -196,7 +196,7 @@
   /**
    * @return {!T}
    */
-  createRequest() {
+  bindNewPipeAndPassReceiver() {
     let {handle0, handle1} = Mojo.createMessagePipe();
     this.bindHandle(handle0);
     return new this.requestType_(handle1);
@@ -208,7 +208,7 @@
    */
   bindHandle(handle) {
     if (this.handle)
-      throw new Error('Proxy already bound.');
+      throw new Error('Remote already bound.');
     this.handle = handle;
 
     const reader = new mojo.internal.interfaceSupport.HandleReader(handle);
@@ -253,7 +253,8 @@
   sendMessage(ordinal, paramStruct, responseStruct, args) {
     if (!this.handle) {
       throw new Error(
-          'Attempting to use an unbound proxy. Try $.createRequest() first.')
+        'Attempting to use an unbound remote. Try ' +
+        '$.bindNewPipeAndPassReceiver() first.')
     }
 
     // The pipe has already been closed, so just drop the message.
@@ -344,33 +345,42 @@
 };
 
 /**
- * Wrapper around mojo.internal.interfaceSupport.InterfaceProxyBase that
- * exposes the subset of InterfaceProxyBase's method that users are allowed
+ * Wrapper around mojo.internal.interfaceSupport.InterfaceRemoteBase that
+ * exposes the subset of InterfaceRemoteBase's method that users are allowed
  * to use.
  * @template T
  * @export
  */
-mojo.internal.interfaceSupport.InterfaceProxyBaseWrapper = class {
+mojo.internal.interfaceSupport.InterfaceRemoteBaseWrapper = class {
   /**
-   * @param {!mojo.internal.interfaceSupport.InterfaceProxyBase<T>} proxy
+   * @param {!mojo.internal.interfaceSupport.InterfaceRemoteBase<T>} remote
    * @public
    */
-  constructor(proxy) {
-    /** @private {!mojo.internal.interfaceSupport.InterfaceProxyBase<T>} */
-    this.proxy_ = proxy;
+  constructor(remote) {
+    /** @private {!mojo.internal.interfaceSupport.InterfaceRemoteBase<T>} */
+    this.remote_ = remote;
+  }
+
+  // TODO(ortuno): Remove once new names are used in the exposed interfaces.
+  /**
+   * @return {!T}
+   * @export
+   */
+  createRequest() {
+    return this.remote_.bindNewPipeAndPassReceiver();
   }
 
   /**
    * @return {!T}
    * @export
    */
-  createRequest() {
-    return this.proxy_.createRequest();
+  bindNewPipeAndPassReceiver() {
+    return this.remote_.bindNewPipeAndPassReceiver();
   }
 
   /** @export */
   close() {
-    this.proxy_.close();
+    this.remote_.close();
   }
 
   /**
@@ -378,7 +388,7 @@
    * @export
    */
   flushForTesting() {
-    return this.proxy_.flushForTesting();
+    return this.remote_.flushForTesting();
   }
 }
 
@@ -417,7 +427,7 @@
  * messages to listeners.
  * @export
  */
-mojo.internal.interfaceSupport.InterfaceCallbackTarget = class {
+mojo.internal.interfaceSupport.InterfaceCallbackReceiver = class {
   /**
    * @public
    * @param {!mojo.internal.interfaceSupport.CallbackRouter} callbackRouter
@@ -449,7 +459,7 @@
    * @return {!Function}
    * @export
    */
-  createTargetHandler(expectsResponse) {
+  createReceiverHandler(expectsResponse) {
     if (expectsResponse)
       return this.dispatchWithResponse_.bind(this);
     return this.dispatch_.bind(this);
@@ -488,7 +498,7 @@
 };
 
 /**
- * Wraps message handlers attached to an InterfaceTarget.
+ * Wraps message handlers attached to an InterfaceReceiver.
  */
 mojo.internal.interfaceSupport.MessageHandler = class {
   /**
@@ -515,7 +525,7 @@
  * message number.
  * @export
  */
-mojo.internal.interfaceSupport.InterfaceTarget = class {
+mojo.internal.interfaceSupport.InterfaceReceiver = class {
   /** @public */
   constructor() {
     /**
@@ -592,7 +602,7 @@
     if (this.controlMessageHandler_.maybeHandleControlMessage(header, buffer))
       return;
     if (header.flags & mojo.internal.kMessageFlagIsResponse)
-      throw new Error('Received unexpected response on interface target');
+      throw new Error('Received unexpected response on interface receiver');
     const handler = this.messageHandlers_.get(header.ordinal);
     if (!handler)
       throw new Error('Received unknown message');
@@ -654,8 +664,8 @@
 
 /**
  * Watches a MojoHandle for readability or peer closure, forwarding either event
- * to one of two callbacks on the reader. Used by both InterfaceProxyBase and
- * InterfaceTarget to watch for incoming messages.
+ * to one of two callbacks on the reader. Used by both InterfaceRemoteBase and
+ * InterfaceReceiver to watch for incoming messages.
  */
 mojo.internal.interfaceSupport.HandleReader = class {
   /**
diff --git a/mojo/public/tools/bindings/generators/js_templates/lite/interface_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/lite/interface_definition.tmpl
index 094f0dd8..afe81e5 100644
--- a/mojo/public/tools/bindings/generators/js_templates/lite/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/lite/interface_definition.tmpl
@@ -56,17 +56,17 @@
   /** @param {MojoHandle=} opt_handle */
   constructor(opt_handle) {
     /**
-     * @private {!mojo.internal.interfaceSupport.InterfaceProxyBase<!{{module.namespace}}.{{interface.name}}Request>}
+     * @private {!mojo.internal.interfaceSupport.InterfaceRemoteBase<!{{module.namespace}}.{{interface.name}}Request>}
      */
     this.proxy =
-        new mojo.internal.interfaceSupport.InterfaceProxyBase(
+        new mojo.internal.interfaceSupport.InterfaceRemoteBase(
           {{module.namespace}}.{{interface.name}}Request,
           opt_handle);
 
     /**
-     * @public {!mojo.internal.interfaceSupport.InterfaceProxyBaseWrapper<!{{module.namespace}}.{{interface.name}}Request>}
+     * @public {!mojo.internal.interfaceSupport.InterfaceRemoteBaseWrapper<!{{module.namespace}}.{{interface.name}}Request>}
      */
-    this.$ = new mojo.internal.interfaceSupport.InterfaceProxyBaseWrapper(this.proxy);
+    this.$ = new mojo.internal.interfaceSupport.InterfaceRemoteBaseWrapper(this.proxy);
 
     /** @public {!mojo.internal.interfaceSupport.ConnectionErrorEventRouter} */
     this.onConnectionError = this.proxy.getConnectionErrorEventRouter();
@@ -114,11 +114,11 @@
    * @param {!{{module.namespace}}.{{interface.name}}Interface } impl
    */
   constructor(impl) {
-    this.target_ = new mojo.internal.interfaceSupport.InterfaceTarget;
+    this.receiver_ = new mojo.internal.interfaceSupport.InterfaceReceiver;
 {%  for method in interface.methods %}
 {%-   set interface_message_id =
           interface.mojom_name ~ "_" ~ method.mojom_name %}
-    this.target_.registerHandler(
+    this.receiver_.registerHandler(
         {{method.ordinal}},
         {{module.namespace}}.{{interface_message_id}}_ParamsSpec.$,
 {%-   if method.response_parameters != None %}
@@ -129,7 +129,7 @@
         impl.{{method.name}}.bind(impl));
 {%- endfor %}
     /** @public {!mojo.internal.interfaceSupport.ConnectionErrorEventRouter} */
-    this.onConnectionError = this.target_.getConnectionErrorEventRouter();
+    this.onConnectionError = this.receiver_.getConnectionErrorEventRouter();
   }
 
   /**
@@ -140,7 +140,7 @@
    * @export
    */
   bindHandle(handle) {
-    this.target_.bindHandle(handle);
+    this.receiver_.bindHandle(handle);
   }
 
   /**
@@ -149,7 +149,7 @@
    * @export
    */
   closeBindings() {
-    this.target_.closeBindings();
+    this.receiver_.closeBindings();
   }
 
   /**
@@ -163,7 +163,7 @@
   static getProxy() {
     let proxy = new {{module.namespace}}.{{interface.name}}Proxy;
     Mojo.bindInterface('{{mojom_namespace}}.{{interface.name}}',
-                       proxy.$.createRequest().handle);
+                       proxy.$.bindNewPipeAndPassReceiver().handle);
     return proxy;
   }
 
@@ -176,7 +176,7 @@
    */
   createProxy() {
     let proxy = new {{module.namespace}}.{{interface.name}}Proxy;
-    this.target_.bindHandle(proxy.$.createRequest().handle);
+    this.receiver_.bindHandle(proxy.$.bindNewPipeAndPassReceiver().handle);
     return proxy;
   }
 };
@@ -198,39 +198,39 @@
 
 /**
  * An object which receives request messages for the {{interface.name}}
- * mojom interface and dispatches them as callbacks. One callback target exists
+ * mojom interface and dispatches them as callbacks. One callback receiver exists
  * on this object for each message defined in the mojom interface, and each
- * target can have any number of listeners added to it.
+ * receiver can have any number of listeners added to it.
  *
  * @export
  */
 {{module.namespace}}.{{interface.name}}CallbackRouter = class {
   constructor() {
-    this.target_ = new mojo.internal.interfaceSupport.InterfaceTarget;
+    this.receiver_ = new mojo.internal.interfaceSupport.InterfaceReceiver;
     this.router_ = new mojo.internal.interfaceSupport.CallbackRouter;
 {%  for method in interface.methods %}
 {%-   set interface_message_id =
           interface.mojom_name ~ "_" ~ method.mojom_name %}
     /**
-     * @public {!mojo.internal.interfaceSupport.InterfaceCallbackTarget}
+     * @public {!mojo.internal.interfaceSupport.InterfaceCallbackReceiver}
      */
     this.{{method.name}} =
-        new mojo.internal.interfaceSupport.InterfaceCallbackTarget(
+        new mojo.internal.interfaceSupport.InterfaceCallbackReceiver(
             this.router_);
 
-    this.target_.registerHandler(
+    this.receiver_.registerHandler(
         {{method.ordinal}},
         {{module.namespace}}.{{interface_message_id}}_ParamsSpec.$,
 {%-   if method.response_parameters != None %}
         {{module.namespace}}.{{interface_message_id}}_ResponseParamsSpec.$,
-        this.{{method.name}}.createTargetHandler(true /* expectsResponse */));
+        this.{{method.name}}.createReceiverHandler(true /* expectsResponse */));
 {%-   else %}
         null,
-        this.{{method.name}}.createTargetHandler(false /* expectsResponse */));
+        this.{{method.name}}.createReceiverHandler(false /* expectsResponse */));
 {%-   endif %}
 {%- endfor %}
     /** @public {!mojo.internal.interfaceSupport.ConnectionErrorEventRouter} */
-    this.onConnectionError = this.target_.getConnectionErrorEventRouter();
+    this.onConnectionError = this.receiver_.getConnectionErrorEventRouter();
   }
 
   /**
@@ -241,15 +241,15 @@
    * @export
    */
   bindHandle(handle) {
-    this.target_.bindHandle(handle);
+    this.receiver_.bindHandle(handle);
   }
 
   /**
-   * Closes all bindings bound to this target. The target will not receive any
+   * Closes all bindings bound to this receiver. The receiver will not receive any
    * further message message events unless rebound to one or more handles.
    */
   closeBindings() {
-    this.target_.closeBindings();
+    this.receiver_.closeBindings();
   }
 
   /**
@@ -261,7 +261,7 @@
    */
   createProxy() {
     let proxy = new {{module.namespace}}.{{interface.name}}Proxy;
-    this.target_.bindHandle(proxy.$.createRequest().handle);
+    this.receiver_.bindHandle(proxy.$.bindNewPipeAndPassReceiver().handle);
     return proxy;
   }
 
diff --git a/net/BUILD.gn b/net/BUILD.gn
index edfcdc1..b10fa57 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -2751,6 +2751,8 @@
     "proxy_resolution/mock_proxy_resolver.h",
     "proxy_resolution/proxy_config_service_common_unittest.cc",
     "proxy_resolution/proxy_config_service_common_unittest.h",
+    "quic/quic_test_packet_printer.cc",
+    "quic/quic_test_packet_printer.h",
     "socket/socket_test_util.cc",
     "socket/socket_test_util.h",
     "spdy/spdy_test_util_common.cc",
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc
index 8eb68e8b..b8a0dad 100644
--- a/net/cert/cert_verify_proc_unittest.cc
+++ b/net/cert/cert_verify_proc_unittest.cc
@@ -274,6 +274,22 @@
                      std::end(kSha256WithRSAEncryption));
 }
 
+std::string Sha1WithRSAEncryption() {
+  const uint8_t kSha1WithRSAEncryption[] = {0x30, 0x0D, 0x06, 0x09, 0x2a,
+                                            0x86, 0x48, 0x86, 0xf7, 0x0d,
+                                            0x01, 0x01, 0x05, 0x05, 0x00};
+  return std::string(std::begin(kSha1WithRSAEncryption),
+                     std::end(kSha1WithRSAEncryption));
+}
+
+std::string Md5WithRSAEncryption() {
+  const uint8_t kMd5WithRSAEncryption[] = {0x30, 0x0d, 0x06, 0x09, 0x2a,
+                                           0x86, 0x48, 0x86, 0xf7, 0x0d,
+                                           0x01, 0x01, 0x04, 0x05, 0x00};
+  return std::string(std::begin(kMd5WithRSAEncryption),
+                     std::end(kMd5WithRSAEncryption));
+}
+
 // Adds bytes (specified as a StringPiece) to the given CBB.
 // The argument ordering follows the boringssl CBB_* api style.
 bool CBBAddBytes(CBB* cbb, base::StringPiece bytes) {
@@ -460,11 +476,7 @@
       }
 
       case DigestAlgorithm::Sha1: {
-        const uint8_t kSha1WithRSAEncryption[] = {0x30, 0x0D, 0x06, 0x09, 0x2a,
-                                                  0x86, 0x48, 0x86, 0xf7, 0x0d,
-                                                  0x01, 0x01, 0x05, 0x05, 0x00};
-        SetSignatureAlgorithm(std::string(std::begin(kSha1WithRSAEncryption),
-                                          std::end(kSha1WithRSAEncryption)));
+        SetSignatureAlgorithm(Sha1WithRSAEncryption());
         break;
       }
 
@@ -2998,7 +3010,33 @@
   // as revoked.
   // Returns the DER-encoded CRL.
   static std::string CreateCrl(CertBuilder* crl_issuer,
-                               const std::vector<uint64_t>& revoked_serials) {
+                               const std::vector<uint64_t>& revoked_serials,
+                               DigestAlgorithm digest) {
+    std::string signature_algorithm;
+    const EVP_MD* md = nullptr;
+    switch (digest) {
+      case DigestAlgorithm::Sha256: {
+        signature_algorithm = Sha256WithRSAEncryption();
+        md = EVP_sha256();
+        break;
+      }
+
+      case DigestAlgorithm::Sha1: {
+        signature_algorithm = Sha1WithRSAEncryption();
+        md = EVP_sha1();
+        break;
+      }
+
+      case DigestAlgorithm::Md5: {
+        signature_algorithm = Md5WithRSAEncryption();
+        md = EVP_md5();
+        break;
+      }
+
+      default:
+        ADD_FAILURE();
+        return std::string();
+    }
     //    TBSCertList  ::=  SEQUENCE  {
     //         version                 Version OPTIONAL,
     //                                      -- if present, MUST be v2
@@ -3020,7 +3058,7 @@
     if (!CBB_init(tbs_cbb.get(), 10) ||
         !CBB_add_asn1(tbs_cbb.get(), &tbs_cert_list, CBS_ASN1_SEQUENCE) ||
         !CBB_add_asn1_uint64(&tbs_cert_list, 1 /* V2 */) ||
-        !CBBAddBytes(&tbs_cert_list, Sha256WithRSAEncryption()) ||
+        !CBBAddBytes(&tbs_cert_list, signature_algorithm) ||
         !CBBAddBytes(&tbs_cert_list, crl_issuer->GetSubject()) ||
         !CBBAddTime(&tbs_cert_list,
                     base::Time::Now() - base::TimeDelta::FromDays(1)) ||
@@ -3063,10 +3101,10 @@
     if (!CBB_init(crl_cbb.get(), 10) ||
         !CBB_add_asn1(crl_cbb.get(), &cert_list, CBS_ASN1_SEQUENCE) ||
         !CBBAddBytes(&cert_list, tbs_tlv) ||
-        !CBBAddBytes(&cert_list, Sha256WithRSAEncryption()) ||
+        !CBBAddBytes(&cert_list, signature_algorithm) ||
         !CBB_add_asn1(&cert_list, &signature, CBS_ASN1_BITSTRING) ||
         !CBB_add_u8(&signature, 0 /* no unused bits */) ||
-        !EVP_DigestSignInit(ctx.get(), nullptr, EVP_sha256(), nullptr,
+        !EVP_DigestSignInit(ctx.get(), nullptr, md, nullptr,
                             crl_issuer->GetKey()) ||
         !EVP_DigestSign(ctx.get(), nullptr, &sig_len,
                         reinterpret_cast<const uint8_t*>(tbs_tlv.data()),
@@ -3086,8 +3124,9 @@
   // as revoked, and registers it to be served by the test server.
   // Returns the full URL to retrieve the CRL from the test server.
   GURL CreateAndServeCrl(CertBuilder* crl_issuer,
-                         const std::vector<uint64_t>& revoked_serials) {
-    std::string crl = CreateCrl(crl_issuer, revoked_serials);
+                         const std::vector<uint64_t>& revoked_serials,
+                         DigestAlgorithm digest = DigestAlgorithm::Sha256) {
+    std::string crl = CreateCrl(crl_issuer, revoked_serials, digest);
     std::string crl_path = MakeRandomPath(".crl");
     return RegisterSimpleTestServerHandler(crl_path, HTTP_OK,
                                            "application/pkix-crl", crl);
@@ -4006,6 +4045,106 @@
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
 }
 
+TEST_P(CertVerifyProcInternalWithNetFetchingTest,
+       RevocationSoftFailLeafRevokedBySha1Crl) {
+  if (!SupportsSoftFailRevChecking()) {
+    LOG(INFO) << "Skipping test as verifier doesn't support "
+                 "VERIFY_REV_CHECKING_ENABLED";
+    return;
+  }
+
+  const char kHostname[] = "www.example.com";
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Root-issued CRL which does not revoke intermediate.
+  intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {}));
+
+  // Leaf is revoked by intermediate issued CRL which is signed with
+  // sha1WithRSAEncryption.
+  leaf->SetCrlDistributionPointUrl(CreateAndServeCrl(
+      intermediate.get(), {leaf->GetSerialNumber()}, DigestAlgorithm::Sha1));
+
+  // Trust the root and build a chain to verify that includes the intermediate.
+  ScopedTestRoot scoped_root(root->GetX509Certificate().get());
+  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
+      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  ASSERT_TRUE(chain.get());
+
+  // Verify with soft-fail revocation checking.
+  const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED;
+  CertVerifyResult verify_result;
+  int error =
+      Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
+
+  if (verify_proc_type() == CERT_VERIFY_PROC_BUILTIN) {
+    // TODO(mattm): CertVerifyProcBuiltin CRL handling.
+    EXPECT_THAT(error, IsOk());
+  } else if (verify_proc_type() == CERT_VERIFY_PROC_MAC &&
+             IsMacAtLeastOS10_12()) {
+    // CRL handling seems broken on macOS >= 10.12.
+    // TODO(mattm): followup on this.
+    EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION));
+  } else {
+    // Should fail, leaf is revoked.
+    EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
+  }
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+
+TEST_P(CertVerifyProcInternalWithNetFetchingTest,
+       RevocationSoftFailLeafRevokedByMd5Crl) {
+  if (!SupportsSoftFailRevChecking()) {
+    LOG(INFO) << "Skipping test as verifier doesn't support "
+                 "VERIFY_REV_CHECKING_ENABLED";
+    return;
+  }
+
+  const char kHostname[] = "www.example.com";
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Root-issued CRL which does not revoke intermediate.
+  intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {}));
+
+  // Leaf is revoked by intermediate issued CRL which is signed with
+  // md5WithRSAEncryption.
+  leaf->SetCrlDistributionPointUrl(CreateAndServeCrl(
+      intermediate.get(), {leaf->GetSerialNumber()}, DigestAlgorithm::Md5));
+
+  // Trust the root and build a chain to verify that includes the intermediate.
+  ScopedTestRoot scoped_root(root->GetX509Certificate().get());
+  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
+      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  ASSERT_TRUE(chain.get());
+
+  // Verify with soft-fail revocation checking.
+  const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED;
+  CertVerifyResult verify_result;
+  int error =
+      Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
+
+  if (verify_proc_type() == CERT_VERIFY_PROC_MAC && IsMacAtLeastOS10_12()) {
+    // CRL handling seems broken on macOS >= 10.12.
+    // TODO(mattm): followup on this.
+    EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION));
+  } else if (verify_proc_type() == CERT_VERIFY_PROC_WIN ||
+             verify_proc_type() == CERT_VERIFY_PROC_MAC) {
+    // Windows and Mac <= 10.11 honor MD5 CRLs. ¯\_(ツ)_/¯
+    EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
+  } else {
+    // Verification should succeed: MD5 signature algorithm is not supported
+    // and soft-fail checking will ignore the inability to get revocation
+    // status.
+    EXPECT_THAT(error, IsOk());
+  }
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+
 // CRL soft fail test where the intermediate certificate has a good CRL, but
 // the leaf's distribution point returns an http error. Verification should
 // succeed.
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index 52f18829..fc932a2 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -1496,7 +1496,9 @@
       DnsQueryType query_type,
       HostResolverFlags host_resolver_flags,
       HostResolverSource requested_source,
+      ResolveHostParameters::CacheUsage cache_usage,
       URLRequestContext* request_context,
+      HostCache* host_cache,
       std::deque<TaskType> tasks,
       RequestPriority priority,
       scoped_refptr<base::TaskRunner> proc_task_runner,
@@ -1507,7 +1509,9 @@
         query_type_(query_type),
         host_resolver_flags_(host_resolver_flags),
         requested_source_(requested_source),
+        cache_usage_(cache_usage),
         request_context_(request_context),
+        host_cache_(host_cache),
         tasks_(tasks),
         job_running_(false),
         priority_tracker_(priority),
@@ -1573,6 +1577,10 @@
   }
 
   void AddRequest(RequestImpl* request) {
+    // Job currently assumes a 1:1 correspondence between URLRequestContext and
+    // HostCache. Since the URLRequestContext is part of the JobKey, any request
+    // added to any existing Job should share the same HostCache.
+    DCHECK_EQ(host_cache_, request->host_cache());
     DCHECK_EQ(hostname_, request->request_host().host());
 
     request->AssignJob(this);
@@ -1893,8 +1901,21 @@
   }
 
   void InsecureCacheLookup() {
-    // TODO(crbug.com/878582): Check insecure cache.
-    RunNextTask();
+    // Insecure cache lookups for requests allowing stale results should have
+    // occurred prior to Job creation.
+    DCHECK(cache_usage_ != ResolveHostParameters::CacheUsage::STALE_ALLOWED);
+    base::Optional<HostCache::EntryStaleness> stale_info;
+    base::Optional<HostCache::Entry> resolved = resolver_->MaybeServeFromCache(
+        host_cache_, GenerateCacheKey(false), cache_usage_,
+        false /* ignore_secure */, net_log_, &stale_info);
+
+    if (resolved) {
+      DCHECK(stale_info);
+      DCHECK(!stale_info.value().is_stale());
+      CompleteRequestsWithoutCache(resolved.value(), std::move(stale_info));
+    } else {
+      RunNextTask();
+    }
   }
 
   void StartDnsTask(bool secure) {
@@ -2052,7 +2073,7 @@
     } else {
       // MDNS uses a separate cache, so skip saving result to cache.
       // TODO(crbug.com/926300): Consider merging caches.
-      CompleteRequestsWithoutCache(results);
+      CompleteRequestsWithoutCache(results, base::nullopt /* stale_info */);
     }
   }
 
@@ -2149,18 +2170,8 @@
     // If the request did not complete, don't cache it.
     if (!results.did_complete())
       return;
-
-    // Multiple requests will usually share caches, so avoid duplicate cache
-    // adds.
-    std::set<HostCache*> caches;
-    for (auto* node = requests_.head(); node != requests_.end();
-         node = node->next()) {
-      if (node->value()->host_cache())
-        caches.insert(node->value()->host_cache());
-    }
     HostCache::Key cache_key = GenerateCacheKey(secure);
-    for (HostCache* cache : caches)
-      resolver_->CacheResult(cache, cache_key, results, ttl);
+    resolver_->CacheResult(host_cache_, cache_key, results, ttl);
   }
 
   // Performs Job's last rites. Completes all Requests. Deletes this.
@@ -2241,7 +2252,17 @@
     }
   }
 
-  void CompleteRequestsWithoutCache(const HostCache::Entry& results) {
+  void CompleteRequestsWithoutCache(
+      const HostCache::Entry& results,
+      base::Optional<HostCache::EntryStaleness> stale_info) {
+    // Record the stale_info for all non-speculative requests, if it exists.
+    if (stale_info) {
+      for (auto* node = requests_.head(); node != requests_.end();
+           node = node->next()) {
+        if (!node->value()->parameters().is_speculative)
+          node->value()->set_stale_info(std::move(stale_info).value());
+      }
+    }
     CompleteRequests(results, base::TimeDelta(), false /* allow_cache */,
                      false /* secure */);
   }
@@ -2267,7 +2288,11 @@
   const DnsQueryType query_type_;
   const HostResolverFlags host_resolver_flags_;
   const HostResolverSource requested_source_;
+  const ResolveHostParameters::CacheUsage cache_usage_;
   URLRequestContext* const request_context_;
+  // TODO(crbug.com/969847): Consider allowing requests within a single Job to
+  // have different HostCaches.
+  HostCache* const host_cache_;
 
   struct CompletionResult {
     const HostCache::Entry entry;
@@ -2676,28 +2701,31 @@
   if (resolved)
     return resolved.value();
 
-  // TODO(crbug.com/878582): Respect the type of cache lookup that should be
-  // performed.
+  // Do initial cache lookup.
   if (!out_tasks->empty() &&
       (out_tasks->front() == TaskType::SECURE_CACHE_LOOKUP ||
        out_tasks->front() == TaskType::INSECURE_CACHE_LOOKUP ||
        out_tasks->front() == TaskType::CACHE_LOOKUP)) {
+    HostCache::Key key(hostname, *out_effective_query_type,
+                       *out_effective_host_resolver_flags, source);
+
+    if (out_tasks->front() == TaskType::SECURE_CACHE_LOOKUP)
+      key.secure = true;
+
+    bool ignore_secure = false;
+    if (out_tasks->front() == TaskType::CACHE_LOOKUP)
+      ignore_secure = true;
+
     out_tasks->pop_front();
-    if (cache_usage == ResolveHostParameters::CacheUsage::ALLOWED ||
-        cache_usage == ResolveHostParameters::CacheUsage::STALE_ALLOWED) {
-      HostCache::Key key(hostname, *out_effective_query_type,
-                         *out_effective_host_resolver_flags, source);
-      resolved = ServeFromCache(
-          cache, key,
-          cache_usage == ResolveHostParameters::CacheUsage::STALE_ALLOWED,
-          out_stale_info);
-      if (resolved) {
-        DCHECK(out_stale_info->has_value());
-        source_net_log.AddEvent(NetLogEventType::HOST_RESOLVER_IMPL_CACHE_HIT,
-                                resolved.value().CreateNetLogCallback());
-        // |ServeFromCache()| will update |*stale_info| as needed.
-        return resolved.value();
-      }
+
+    resolved = MaybeServeFromCache(cache, key, cache_usage, ignore_secure,
+                                   source_net_log, out_stale_info);
+    if (resolved) {
+      // |MaybeServeFromCache()| will update |*out_stale_info| as needed.
+      DCHECK(out_stale_info->has_value());
+      source_net_log.AddEvent(NetLogEventType::HOST_RESOLVER_IMPL_CACHE_HIT,
+                              resolved.value().CreateNetLogCallback());
+      return resolved.value();
     }
     DCHECK(!out_stale_info->has_value());
   }
@@ -2731,9 +2759,10 @@
     auto new_job = std::make_unique<Job>(
         weak_ptr_factory_.GetWeakPtr(), request->request_host().host(),
         effective_query_type, effective_host_resolver_flags,
-        request->parameters().source, request->request_context(),
-        std::move(tasks), request->priority(), proc_task_runner_,
-        request->source_net_log(), tick_clock_);
+        request->parameters().source, request->parameters().cache_usage,
+        request->request_context(), request->host_cache(), std::move(tasks),
+        request->priority(), proc_task_runner_, request->source_net_log(),
+        tick_clock_);
     job = new_job.get();
     new_job->Schedule(false);
     DCHECK(new_job->is_running() || new_job->is_queued());
@@ -2789,10 +2818,12 @@
                           HostCache::Entry::SOURCE_UNKNOWN);
 }
 
-base::Optional<HostCache::Entry> HostResolverManager::ServeFromCache(
+base::Optional<HostCache::Entry> HostResolverManager::MaybeServeFromCache(
     HostCache* cache,
     const HostCache::Key& key,
-    bool allow_stale,
+    ResolveHostParameters::CacheUsage cache_usage,
+    bool ignore_secure,
+    const NetLogWithSource& source_net_log,
     base::Optional<HostCache::EntryStaleness>* out_stale_info) {
   DCHECK(out_stale_info);
   *out_stale_info = base::nullopt;
@@ -2800,6 +2831,9 @@
   if (!cache)
     return base::nullopt;
 
+  if (cache_usage == ResolveHostParameters::CacheUsage::DISALLOWED)
+    return base::nullopt;
+
   // Local-only requests search the cache for non-local-only results.
   HostCache::Key effective_key = key;
   if (effective_key.host_resolver_source == HostResolverSource::LOCAL_ONLY)
@@ -2807,19 +2841,22 @@
 
   const std::pair<const HostCache::Key, HostCache::Entry>* cache_result;
   HostCache::EntryStaleness staleness;
-  if (allow_stale) {
+  if (cache_usage == ResolveHostParameters::CacheUsage::STALE_ALLOWED) {
     cache_result = cache->LookupStale(effective_key, tick_clock_->NowTicks(),
-                                      &staleness, true /* ignore_secure */);
+                                      &staleness, ignore_secure);
   } else {
-    cache_result = cache->Lookup(effective_key, tick_clock_->NowTicks(),
-                                 true /* ignore_secure */);
+    DCHECK(cache_usage == ResolveHostParameters::CacheUsage::ALLOWED);
+    cache_result =
+        cache->Lookup(effective_key, tick_clock_->NowTicks(), ignore_secure);
     staleness = HostCache::kNotStale;
   }
-  if (!cache_result)
-    return base::nullopt;
-
-  *out_stale_info = std::move(staleness);
-  return cache_result->second;
+  if (cache_result) {
+    *out_stale_info = std::move(staleness);
+    source_net_log.AddEvent(NetLogEventType::HOST_RESOLVER_IMPL_CACHE_HIT,
+                            cache_result->second.CreateNetLogCallback());
+    return cache_result->second;
+  }
+  return base::nullopt;
 }
 
 base::Optional<HostCache::Entry> HostResolverManager::ServeFromHosts(
diff --git a/net/dns/host_resolver_manager.h b/net/dns/host_resolver_manager.h
index 841771e..428fead 100644
--- a/net/dns/host_resolver_manager.h
+++ b/net/dns/host_resolver_manager.h
@@ -127,7 +127,10 @@
   ~HostResolverManager() override;
 
   // If |host_cache| is non-null, its HostCache::Invalidator must have already
-  // been added (via AddHostCacheInvalidator()).
+  // been added (via AddHostCacheInvalidator()). If |optional_parameters|
+  // specifies any cache usage other than LOCAL_ONLY, there must be a 1:1
+  // correspondence between |request_context| and |host_cache|, and both should
+  // come from the same ContextHostResolver.
   std::unique_ptr<CancellableRequest> CreateRequest(
       const HostPortPair& host,
       const NetLogWithSource& net_log,
@@ -303,16 +306,16 @@
                                                bool resolve_canonname,
                                                const IPAddress* ip_address);
 
-  // Returns the result iff a positive match is found for |key| in the cache.
-  // |out_stale_info| must be non-null, and will be filled in with details of
-  // the entry's staleness if an entry is returned, otherwise it will be set to
-  // |base::nullopt|.
-  //
-  // If |allow_stale| is true, then stale cache entries can be returned.
-  base::Optional<HostCache::Entry> ServeFromCache(
+  // Returns the result iff |cache_usage| permits cache lookups and a positive
+  // match is found for |key| in |cache|. |out_stale_info| must be non-null, and
+  // will be filled in with details of the entry's staleness if an entry is
+  // returned, otherwise it will be set to |base::nullopt|.
+  base::Optional<HostCache::Entry> MaybeServeFromCache(
       HostCache* cache,
       const HostCache::Key& key,
-      bool allow_stale,
+      ResolveHostParameters::CacheUsage cache_usage,
+      bool ignore_secure,
+      const NetLogWithSource& source_net_log,
       base::Optional<HostCache::EntryStaleness>* out_stale_info);
 
   // Iff we have a DnsClient with a valid DnsConfig, and |key| can be resolved
diff --git a/net/dns/host_resolver_manager_unittest.cc b/net/dns/host_resolver_manager_unittest.cc
index 1c1057f..1945760 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -563,6 +563,13 @@
     return resolver_->IsIPv6Reachable(net_log);
   }
 
+  void PopulateCache(HostCache::Key& key, IPEndPoint endpoint) {
+    resolver_->CacheResult(host_cache_.get(), key,
+                           HostCache::Entry(OK, AddressList(endpoint),
+                                            HostCache::Entry::SOURCE_UNKNOWN),
+                           base::TimeDelta::FromSeconds(1));
+  }
+
   const std::pair<const HostCache::Key, HostCache::Entry>* GetCacheHit(
       const HostCache::Key& key) {
     DCHECK(host_cache_);
@@ -2355,52 +2362,6 @@
   EXPECT_EQ(1u, proc_->GetCaptureList().size());  // No increase.
 }
 
-// Test that if a Job with multiple requests, each with its own different
-// HostCache, completes, the result is cached in all request HostCaches.
-TEST_F(HostResolverManagerTest, MultipleCachesForMultipleRequests) {
-  proc_->AddRuleForAllFamilies("just.testing", "192.168.1.42");
-
-  std::unique_ptr<HostCache> cache2 = HostCache::CreateDefaultCache();
-  resolver_->AddHostCacheInvalidator(cache2->invalidator());
-
-  ResolveHostResponseHelper response1(resolver_->CreateRequest(
-      HostPortPair("just.testing", 80), NetLogWithSource(), base::nullopt,
-      request_context_.get(), host_cache_.get()));
-  ResolveHostResponseHelper response2(resolver_->CreateRequest(
-      HostPortPair("just.testing", 85), NetLogWithSource(), base::nullopt,
-      request_context_.get(), cache2.get()));
-  ASSERT_EQ(1u, resolver_->num_jobs_for_testing());
-
-  proc_->SignalMultiple(1u);
-  EXPECT_THAT(response1.result_error(), IsOk());
-  EXPECT_THAT(response2.result_error(), IsOk());
-
-  HostResolver::ResolveHostParameters local_resolve_parameters;
-  local_resolve_parameters.source = HostResolverSource::LOCAL_ONLY;
-
-  // Confirm |host_cache_| contains the result.
-  ResolveHostResponseHelper cached_response1(resolver_->CreateRequest(
-      HostPortPair("just.testing", 81), NetLogWithSource(),
-      local_resolve_parameters, request_context_.get(), host_cache_.get()));
-  EXPECT_THAT(cached_response1.result_error(), IsOk());
-  EXPECT_THAT(
-      cached_response1.request()->GetAddressResults().value().endpoints(),
-      testing::ElementsAre(CreateExpected("192.168.1.42", 81)));
-  EXPECT_TRUE(cached_response1.request()->GetStaleInfo());
-
-  // Confirm |cache2| contains the result.
-  ResolveHostResponseHelper cached_response2(resolver_->CreateRequest(
-      HostPortPair("just.testing", 82), NetLogWithSource(),
-      local_resolve_parameters, request_context_.get(), cache2.get()));
-  EXPECT_THAT(cached_response2.result_error(), IsOk());
-  EXPECT_THAT(
-      cached_response2.request()->GetAddressResults().value().endpoints(),
-      testing::ElementsAre(CreateExpected("192.168.1.42", 82)));
-  EXPECT_TRUE(cached_response2.request()->GetStaleInfo());
-
-  resolver_->RemoveHostCacheInvalidator(cache2->invalidator());
-}
-
 #if BUILDFLAG(ENABLE_MDNS)
 const uint8_t kMdnsResponseA[] = {
     // Header
@@ -4699,6 +4660,59 @@
               testing::ElementsAre(CreateExpected("192.168.1.100", 80)));
 }
 
+TEST_F(HostResolverManagerDnsTest, SecureDnsMode_Automatic_SecureCache) {
+  ChangeDnsConfig(CreateValidDnsConfig());
+  DnsConfigOverrides overrides;
+  overrides.secure_dns_mode = DnsConfig::SecureDnsMode::AUTOMATIC;
+  resolver_->SetDnsConfigOverrides(overrides);
+
+  // Populate cache with a secure entry.
+  HostCache::Key cached_secure_key =
+      HostCache::Key("automatic_cached", DnsQueryType::UNSPECIFIED,
+                     0 /* host_resolver_flags */, HostResolverSource::ANY);
+  cached_secure_key.secure = true;
+  IPEndPoint kExpectedSecureIP = CreateExpected("192.168.1.102", 80);
+  PopulateCache(cached_secure_key, kExpectedSecureIP);
+
+  // The secure cache should be checked prior to any DoH request being sent.
+  ResolveHostResponseHelper response_secure_cached(resolver_->CreateRequest(
+      HostPortPair("automatic_cached", 80), NetLogWithSource(), base::nullopt,
+      request_context_.get(), host_cache_.get()));
+  EXPECT_THAT(response_secure_cached.result_error(), IsOk());
+  EXPECT_THAT(
+      response_secure_cached.request()->GetAddressResults().value().endpoints(),
+      testing::ElementsAre(kExpectedSecureIP));
+  EXPECT_FALSE(
+      response_secure_cached.request()->GetStaleInfo().value().is_stale());
+}
+
+TEST_F(HostResolverManagerDnsTest, SecureDnsMode_Automatic_InsecureCache) {
+  ChangeDnsConfig(CreateValidDnsConfig());
+  DnsConfigOverrides overrides;
+  overrides.secure_dns_mode = DnsConfig::SecureDnsMode::AUTOMATIC;
+  resolver_->SetDnsConfigOverrides(overrides);
+
+  // Populate cache with an insecure entry.
+  HostCache::Key cached_insecure_key =
+      HostCache::Key("insecure_automatic_cached", DnsQueryType::UNSPECIFIED,
+                     0 /* host_resolver_flags */, HostResolverSource::ANY);
+  IPEndPoint kExpectedInsecureIP = CreateExpected("192.168.1.103", 80);
+  PopulateCache(cached_insecure_key, kExpectedInsecureIP);
+
+  // The insecure cache should be checked after DoH requests fail.
+  ResolveHostResponseHelper response_insecure_cached(resolver_->CreateRequest(
+      HostPortPair("insecure_automatic_cached", 80), NetLogWithSource(),
+      base::nullopt, request_context_.get(), host_cache_.get()));
+  EXPECT_THAT(response_insecure_cached.result_error(), IsOk());
+  EXPECT_THAT(response_insecure_cached.request()
+                  ->GetAddressResults()
+                  .value()
+                  .endpoints(),
+              testing::ElementsAre(kExpectedInsecureIP));
+  EXPECT_FALSE(
+      response_insecure_cached.request()->GetStaleInfo().value().is_stale());
+}
+
 TEST_F(HostResolverManagerDnsTest, SecureDnsMode_Automatic_Downgrade) {
   ChangeDnsConfig(CreateValidDnsConfig());
   // Remove all DoH servers from the config so there is no DoH server available.
@@ -4709,6 +4723,39 @@
   resolver_->SetDnsConfigOverrides(overrides);
   const std::pair<const HostCache::Key, HostCache::Entry>* cache_result;
 
+  // Populate cache with both secure and insecure entries.
+  HostCache::Key cached_secure_key =
+      HostCache::Key("automatic_cached", DnsQueryType::UNSPECIFIED,
+                     0 /* host_resolver_flags */, HostResolverSource::ANY);
+  cached_secure_key.secure = true;
+  IPEndPoint kExpectedSecureIP = CreateExpected("192.168.1.102", 80);
+  PopulateCache(cached_secure_key, kExpectedSecureIP);
+  HostCache::Key cached_insecure_key =
+      HostCache::Key("insecure_automatic_cached", DnsQueryType::UNSPECIFIED,
+                     0 /* host_resolver_flags */, HostResolverSource::ANY);
+  IPEndPoint kExpectedInsecureIP = CreateExpected("192.168.1.103", 80);
+  PopulateCache(cached_insecure_key, kExpectedInsecureIP);
+
+  // The secure cache should still be checked first.
+  ResolveHostResponseHelper response_cached(resolver_->CreateRequest(
+      HostPortPair("automatic_cached", 80), NetLogWithSource(), base::nullopt,
+      request_context_.get(), host_cache_.get()));
+  EXPECT_THAT(response_cached.result_error(), IsOk());
+  EXPECT_THAT(
+      response_cached.request()->GetAddressResults().value().endpoints(),
+      testing::ElementsAre(kExpectedSecureIP));
+
+  // The insecure cache should be checked before any insecure requests are sent.
+  ResolveHostResponseHelper insecure_response_cached(resolver_->CreateRequest(
+      HostPortPair("insecure_automatic_cached", 80), NetLogWithSource(),
+      base::nullopt, request_context_.get(), host_cache_.get()));
+  EXPECT_THAT(insecure_response_cached.result_error(), IsOk());
+  EXPECT_THAT(insecure_response_cached.request()
+                  ->GetAddressResults()
+                  .value()
+                  .endpoints(),
+              testing::ElementsAre(kExpectedInsecureIP));
+
   // The DnsConfig doesn't contain DoH servers so AUTOMATIC mode will be
   // downgraded to OFF. A successful plaintext DNS request should result in an
   // insecure cache entry.
@@ -4726,6 +4773,35 @@
   EXPECT_TRUE(!!cache_result);
 }
 
+TEST_F(HostResolverManagerDnsTest, SecureDnsMode_Automatic_Stale) {
+  ChangeDnsConfig(CreateValidDnsConfig());
+  DnsConfigOverrides overrides;
+  overrides.secure_dns_mode = DnsConfig::SecureDnsMode::AUTOMATIC;
+  resolver_->SetDnsConfigOverrides(overrides);
+
+  // Populate cache with insecure entry.
+  HostCache::Key cached_stale_key =
+      HostCache::Key("automatic_stale", DnsQueryType::UNSPECIFIED,
+                     0 /* host_resolver_flags */, HostResolverSource::ANY);
+  IPEndPoint kExpectedStaleIP = CreateExpected("192.168.1.102", 80);
+  PopulateCache(cached_stale_key, kExpectedStaleIP);
+  MakeCacheStale();
+
+  HostResolver::ResolveHostParameters stale_allowed_parameters;
+  stale_allowed_parameters.cache_usage =
+      HostResolver::ResolveHostParameters::CacheUsage::STALE_ALLOWED;
+
+  // The insecure cache should be checked before secure requests are made since
+  // stale results are allowed.
+  ResolveHostResponseHelper response_stale(resolver_->CreateRequest(
+      HostPortPair("automatic_stale", 80), NetLogWithSource(),
+      stale_allowed_parameters, request_context_.get(), host_cache_.get()));
+  EXPECT_THAT(response_stale.result_error(), IsOk());
+  EXPECT_THAT(response_stale.request()->GetAddressResults().value().endpoints(),
+              testing::ElementsAre(kExpectedStaleIP));
+  EXPECT_TRUE(response_stale.request()->GetStaleInfo()->is_stale());
+}
+
 TEST_F(HostResolverManagerDnsTest, SecureDnsMode_Secure) {
   proc_->AddRuleForAllFamilies("nx_succeed", "192.168.1.100");
   set_allow_fallback_to_proctask(true);
diff --git a/net/docs/proxy.md b/net/docs/proxy.md
index 8b99c8e2..b60e59b1 100644
--- a/net/docs/proxy.md
+++ b/net/docs/proxy.md
@@ -130,7 +130,7 @@
 
 This works like an [HTTP proxy](#HTTP-proxy-scheme), except the
 communication to the proxy server is protected by TLS, and may negotiate
-HTTP/2.
+HTTP/2 (but not QUIC).
 
 Because the connection to the proxy server is secure, https:// requests
 sent through the proxy are not sent in the clear as with an HTTP proxy.
@@ -198,7 +198,19 @@
 * Example identifier (PAC): `QUIC proxy:8080`
 * Example identifier (URI): `quic://proxy:8080`
 
-TODO
+A QUIC proxy uses QUIC (UDP) as the underlying transport, but otherwise
+behaves as an HTTP proxy. It has similar properties to an [HTTPS
+proxy](#HTTPS-proxy-scheme), in that the connection to the proxy server
+is secure, and connection limits are less restrictive.
+
+Support for QUIC proxies in Chrome is currently experimental and not
+ready for production use. In particular, sending https:// and wss://
+URLs through a QUIC proxy is [disabled by
+default](https://bugs.chromium.org/p/chromium/issues/detail?id=969859).
+
+Another caveat is that QUIC does not currently support
+client certificates since it does not use a TLS
+handshake. This may change in future versions.
 
 ## Manual proxy settings
 
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index a23d7bc..d7997e50 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -56,6 +56,9 @@
 
 namespace net {
 
+const char HttpCache::kDoubleKeyPrefix[] = "_dk_";
+const char HttpCache::kDoubleKeySeparator[] = " ";
+
 HttpCache::DefaultBackend::DefaultBackend(CacheType type,
                                           BackendType backend_type,
                                           const base::FilePath& path,
@@ -527,6 +530,33 @@
                   base::trace_event::MemoryAllocatorDump::kUnitsBytes, size);
 }
 
+std::string HttpCache::GetResourceURLFromHttpCacheKey(const std::string& key) {
+  // Search the key to see whether it begins with |kDoubleKeyPrefix|. If so,
+  // then the entry was double-keyed.
+  if (base::StartsWith(key, kDoubleKeyPrefix, base::CompareCase::SENSITIVE)) {
+    // Find the rightmost occurrence of |kDoubleKeySeparator|, as when both
+    // the top-frame origin and the initiator are added to the key, there will
+    // be two occurrences of |kDoubleKeySeparator|.  When the cache entry is
+    // originally written to disk, GenerateCacheKey method calls
+    // HttpUtil::SpecForRequest method, which has a DCHECK to ensure that
+    // the original resource url is valid, and hence will not contain the
+    // unescaped whitespace of |kDoubleKeySeparator|.
+    size_t separator_position = key.rfind(kDoubleKeySeparator);
+    DCHECK_NE(separator_position, std::string::npos);
+
+    size_t separator_size = strlen(kDoubleKeySeparator);
+    size_t start_position = separator_position + separator_size;
+    DCHECK_LE(start_position, key.size() - 1);
+
+    return key.substr(start_position);
+  }
+  return key;
+}
+
+std::string HttpCache::GenerateCacheKeyForTest(const HttpRequestInfo* request) {
+  return GenerateCacheKey(request);
+}
+
 //-----------------------------------------------------------------------------
 
 net::Error HttpCache::CreateAndSetWorkItem(ActiveEntry** entry,
@@ -604,11 +634,13 @@
   std::string isolation_key;
 
   if (base::FeatureList::IsEnabled(features::kSplitCacheByTopFrameOrigin)) {
-    // Prepend the key with "_dk_" to mark it as double keyed (and makes it an
-    // invalid url so that it doesn't get confused with a single-keyed
-    // entry). Separate the origin and url with invalid whitespace character.
-    isolation_key =
-        base::StrCat({"_dk_", request->network_isolation_key.ToString(), " "});
+    // Prepend the key with |kDoubleKeyPrefix| = "_dk_" to mark it as
+    // double-keyed (and makes it an invalid url so that it doesn't get
+    // confused with a single-keyed entry). Separate the origin and url
+    // with invalid whitespace character |kDoubleKeySeparator|.
+    isolation_key = base::StrCat({kDoubleKeyPrefix,
+                                  request->network_isolation_key.ToString(),
+                                  kDoubleKeySeparator});
   }
 
   // Strip out the reference, username, and password sections of the URL and
diff --git a/net/http/http_cache.h b/net/http/http_cache.h
index bd2dd22..03707d1 100644
--- a/net/http/http_cache.h
+++ b/net/http/http_cache.h
@@ -277,6 +277,13 @@
   void DumpMemoryStats(base::trace_event::ProcessMemoryDump* pmd,
                        const std::string& parent_absolute_name) const;
 
+  // Get the URL from the entry's cache key. If double-keying is not enabled,
+  // this will be the key itself.
+  static std::string GetResourceURLFromHttpCacheKey(const std::string& key);
+
+  // Function to generate cache key for testing.
+  std::string GenerateCacheKeyForTest(const HttpRequestInfo* request);
+
  private:
   // Types --------------------------------------------------------------------
 
@@ -625,6 +632,12 @@
   // Processes the backend creation notification.
   void OnBackendCreated(int result, PendingOp* pending_op);
 
+  // Constants ----------------------------------------------------------------
+
+  // Used when generating and accessing keys if cache is split.
+  static const char kDoubleKeyPrefix[];
+  static const char kDoubleKeySeparator[];
+
   // Variables ----------------------------------------------------------------
 
   NetLog* net_log_;
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 5145213..a64269c 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -736,6 +736,23 @@
   }
 };
 
+class HttpSplitCacheKeyTest : public HttpCacheTest {
+ public:
+  HttpSplitCacheKeyTest() {}
+  ~HttpSplitCacheKeyTest() override = default;
+
+  std::string ComputeCacheKey(const std::string& url_string) {
+    GURL url(url_string);
+    net::HttpRequestInfo request_info;
+    request_info.url = url;
+    request_info.method = "GET";
+    request_info.network_isolation_key =
+        net::NetworkIsolationKey(url::Origin::Create(url));
+    MockHttpCache cache;
+    return cache.http_cache()->GenerateCacheKeyForTest(&request_info);
+  }
+};
+
 //-----------------------------------------------------------------------------
 // Tests.
 
@@ -11343,6 +11360,20 @@
             response_info.cache_entry_status);
 }
 
+TEST_F(HttpSplitCacheKeyTest, GetResourceURLFromKey) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(net::features::kSplitCacheByTopFrameOrigin);
+  MockHttpCache cache;
+  std::string urls[] = {"http://www.a.com/", "https://b.com/example.html",
+                        "http://example.com/Some Path/Some Leaf?some query"};
+
+  for (const std::string& url : urls) {
+    std::string key = ComputeCacheKey(url);
+    EXPECT_EQ(GURL(url).spec(),
+              cache.http_cache()->GetResourceURLFromHttpCacheKey(key));
+  }
+}
+
 class TestCompletionCallbackForHttpCache : public TestCompletionCallbackBase {
  public:
   TestCompletionCallbackForHttpCache() {}
diff --git a/net/http/http_stream_factory_job_controller_unittest.cc b/net/http/http_stream_factory_job_controller_unittest.cc
index 05c444f..59dc7f77d 100644
--- a/net/http/http_stream_factory_job_controller_unittest.cc
+++ b/net/http/http_stream_factory_job_controller_unittest.cc
@@ -706,7 +706,8 @@
       url::SchemeHostPort(GURL("http://www.example.com")), stats1);
 
   // Prepare the mocked data.
-  MockQuicData quic_data;
+  MockQuicData quic_data(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data.AddRead(ASYNC, ERR_QUIC_PROTOCOL_ERROR);
   quic_data.AddWrite(ASYNC, OK);
   quic_data.AddSocketDataToFactory(session_deps_.socket_factory.get());
@@ -796,7 +797,8 @@
   // Use COLD_START to make the alt job pending.
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::COLD_START);
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddRead(SYNCHRONOUS, OK);
 
   tcp_data_ = std::make_unique<SequencedSocketData>();
@@ -856,7 +858,8 @@
 
 void HttpStreamFactoryJobControllerTest::TestOnStreamFailedForBothJobs(
     bool alt_job_retried_on_non_default_network) {
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddConnect(ASYNC, ERR_FAILED);
   tcp_data_ = std::make_unique<SequencedSocketData>();
   tcp_data_->set_connect_data(MockConnect(ASYNC, ERR_FAILED));
@@ -907,7 +910,8 @@
 
 void HttpStreamFactoryJobControllerTest::TestAltJobFailsAfterMainJobSucceeded(
     bool alt_job_retried_on_non_default_network) {
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddRead(ASYNC, ERR_FAILED);
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::COLD_START);
@@ -976,7 +980,8 @@
 
 // Tests that when alt job succeeds, main job is destroyed.
 TEST_F(HttpStreamFactoryJobControllerTest, AltJobSucceedsMainJobDestroyed) {
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   // Use cold start and complete alt job manually.
   crypto_client_stream_factory_.set_handshake_mode(
@@ -1024,7 +1029,8 @@
 // Regression test for crbug.com/678768.
 TEST_F(HttpStreamFactoryJobControllerTest,
        AltJobSucceedsMainJobBlockedControllerDestroyed) {
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddWrite(SYNCHRONOUS,
                        client_maker_.MakeInitialSettingsPacket(1, nullptr));
   quic_data_->AddRead(ASYNC, OK);
@@ -1103,7 +1109,8 @@
 // JobController will be cleaned up.
 TEST_F(HttpStreamFactoryJobControllerTest,
        OrphanedJobCompletesControllerDestroyed) {
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   // Use cold start and complete alt job manually.
   crypto_client_stream_factory_.set_handshake_mode(
@@ -1157,7 +1164,8 @@
 
 void HttpStreamFactoryJobControllerTest::TestAltJobSucceedsAfterMainJobFailed(
     bool alt_job_retried_on_non_default_network) {
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   // Use cold start and complete alt job manually.
   crypto_client_stream_factory_.set_handshake_mode(
@@ -1225,7 +1233,8 @@
 void HttpStreamFactoryJobControllerTest::
     TestAltJobSucceedsAfterMainJobSucceeded(
         bool alt_job_retried_on_non_default_network) {
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   // Use cold start and complete alt job manually.
   crypto_client_stream_factory_.set_handshake_mode(
@@ -1308,7 +1317,8 @@
 void HttpStreamFactoryJobControllerTest::
     TestMainJobSucceedsAfterAltJobSucceeded(
         bool alt_job_retried_on_non_default_network) {
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   // Use cold start and complete alt job manually.
   crypto_client_stream_factory_.set_handshake_mode(
@@ -1383,7 +1393,8 @@
 
 void HttpStreamFactoryJobControllerTest::TestMainJobFailsAfterAltJobSucceeded(
     bool alt_job_retried_on_non_default_network) {
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   // Use cold start and complete alt job manually.
   crypto_client_stream_factory_.set_handshake_mode(
@@ -1446,7 +1457,8 @@
 
 void HttpStreamFactoryJobControllerTest::TestMainJobSucceedsAfterAltJobFailed(
     bool alt_job_retried_on_non_default_network) {
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddConnect(SYNCHRONOUS, ERR_FAILED);
 
   tcp_data_ = std::make_unique<SequencedSocketData>();
@@ -1514,7 +1526,8 @@
 // then the alternative service is not marked as broken.
 TEST_F(HttpStreamFactoryJobControllerTest,
        MainJobSucceedsAfterConnectionChanged) {
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddConnect(SYNCHRONOUS, ERR_NETWORK_CHANGED);
   tcp_data_ = std::make_unique<SequencedSocketData>();
   tcp_data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
@@ -1556,7 +1569,8 @@
 // Get load state after main job fails and before alternative job succeeds.
 TEST_F(HttpStreamFactoryJobControllerTest, GetLoadStateAfterMainJobFailed) {
   // Use COLD_START to complete alt job manually.
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::COLD_START);
@@ -1603,7 +1617,8 @@
 
 TEST_F(HttpStreamFactoryJobControllerTest, ResumeMainJobWhenAltJobStalls) {
   // Use COLD_START to stall alt job.
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::COLD_START);
@@ -1672,7 +1687,8 @@
   Initialize(request_info);
 
   // handshake will fail asynchronously after mock data is unpaused.
-  MockQuicData quic_data;
+  MockQuicData quic_data(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // Pause
   quic_data.AddRead(ASYNC, ERR_FAILED);
   quic_data.AddWrite(ASYNC, ERR_FAILED);
@@ -1744,7 +1760,8 @@
   Initialize(request_info);
 
   // Handshake will fail asynchronously after mock data is unpaused.
-  MockQuicData quic_data;
+  MockQuicData quic_data(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // Pause
   quic_data.AddRead(ASYNC, ERR_FAILED);
   quic_data.AddWrite(ASYNC, ERR_FAILED);
@@ -1884,7 +1901,8 @@
   Initialize(request_info);
 
   // handshake will fail asynchronously after mock data is unpaused.
-  MockQuicData quic_data;
+  MockQuicData quic_data(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // Pause
   quic_data.AddRead(ASYNC, ERR_FAILED);
   quic_data.AddWrite(ASYNC, ERR_FAILED);
@@ -1943,7 +1961,8 @@
   Initialize(request_info);
 
   // handshake will fail asynchronously after mock data is unpaused.
-  MockQuicData quic_data;
+  MockQuicData quic_data(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // Pause
   quic_data.AddRead(ASYNC, ERR_FAILED);
   quic_data.AddWrite(ASYNC, ERR_FAILED);
@@ -2064,7 +2083,8 @@
   EXPECT_TRUE(test_proxy_delegate()->alternative_proxy_server().is_quic());
 
   // Handshake will fail asynchronously after mock data is unpaused.
-  MockQuicData quic_data;
+  MockQuicData quic_data(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // Pause
   quic_data.AddRead(ASYNC, ERR_FAILED);
   quic_data.AddWrite(ASYNC, ERR_FAILED);
@@ -2122,7 +2142,8 @@
   ProxyClientSocketDataProvider proxy_data(SYNCHRONOUS, OK);
   session_deps_.socket_factory->AddProxyClientSocketDataProvider(&proxy_data);
 
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddConnect(SYNCHRONOUS, ERR_FAILED);
   tcp_data_ = std::make_unique<SequencedSocketData>();
   tcp_data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
@@ -2175,7 +2196,8 @@
   ProxyClientSocketDataProvider proxy_data(SYNCHRONOUS, OK);
   session_deps_.socket_factory->AddProxyClientSocketDataProvider(&proxy_data);
 
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddConnect(SYNCHRONOUS, ERR_INTERNET_DISCONNECTED);
   tcp_data_ = std::make_unique<SequencedSocketData>();
   tcp_data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
@@ -2228,7 +2250,8 @@
   // Use COLD_START to make the alt job pending.
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::COLD_START);
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   tcp_data_ = std::make_unique<SequencedSocketData>();
   tcp_data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
@@ -2276,7 +2299,8 @@
 }
 
 TEST_F(HttpStreamFactoryJobControllerTest, PreconnectToHostWithValidAltSvc) {
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddWrite(SYNCHRONOUS,
                        client_maker_.MakeInitialSettingsPacket(1, nullptr));
   quic_data_->AddRead(ASYNC, OK);
@@ -2707,7 +2731,8 @@
 TEST_F(JobControllerLimitMultipleH2Requests, QuicJobNotThrottled) {
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::COLD_START);
-  quic_data_ = std::make_unique<MockQuicData>();
+  quic_data_ = std::make_unique<MockQuicData>(
+      HttpNetworkSession::Params().quic_supported_versions.front());
   quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)};
   tcp_data_ =
@@ -2772,7 +2797,8 @@
   const bool enable_ip_based_pooling = ::testing::get<0>(GetParam());
   const bool enable_alternative_services = ::testing::get<1>(GetParam());
   if (enable_alternative_services) {
-    quic_data_ = std::make_unique<MockQuicData>();
+    quic_data_ = std::make_unique<MockQuicData>(
+        HttpNetworkSession::Params().quic_supported_versions.front());
     quic_data_->AddConnect(SYNCHRONOUS, OK);
     quic_data_->AddWrite(SYNCHRONOUS,
                          client_maker_.MakeInitialSettingsPacket(1, nullptr));
diff --git a/net/quic/mock_quic_data.cc b/net/quic/mock_quic_data.cc
index 28e0a5f6..9d4187b 100644
--- a/net/quic/mock_quic_data.cc
+++ b/net/quic/mock_quic_data.cc
@@ -7,7 +7,8 @@
 namespace net {
 namespace test {
 
-MockQuicData::MockQuicData() : sequence_number_(0) {}
+MockQuicData::MockQuicData(quic::ParsedQuicVersion version)
+    : sequence_number_(0), printer_(version) {}
 
 MockQuicData::~MockQuicData() {}
 
@@ -54,6 +55,7 @@
 
 SequencedSocketData* MockQuicData::InitializeAndGetSequencedSocketData() {
   socket_data_.reset(new SequencedSocketData(reads_, writes_));
+  socket_data_->set_printer(&printer_);
   if (connect_ != nullptr)
     socket_data_->set_connect_data(*connect_);
 
diff --git a/net/quic/mock_quic_data.h b/net/quic/mock_quic_data.h
index 62109c0..5ab220e 100644
--- a/net/quic/mock_quic_data.h
+++ b/net/quic/mock_quic_data.h
@@ -5,6 +5,7 @@
 #ifndef NET_QUIC_MOCK_QUIC_DATA_H_
 #define NET_QUIC_MOCK_QUIC_DATA_H_
 
+#include "net/quic/quic_test_packet_printer.h"
 #include "net/socket/socket_test_util.h"
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
 
@@ -15,7 +16,7 @@
 // Simplify ownership issues and the interaction with the MockSocketFactory.
 class MockQuicData {
  public:
-  MockQuicData();
+  explicit MockQuicData(quic::ParsedQuicVersion version);
   ~MockQuicData();
 
   // Makes the Connect() call return |rv| either
@@ -63,6 +64,7 @@
   std::vector<MockRead> reads_;
   size_t sequence_number_;
   std::unique_ptr<SequencedSocketData> socket_data_;
+  QuicPacketPrinter printer_;
 };
 
 }  // namespace test
diff --git a/net/quic/quic_chromium_client_session_test.cc b/net/quic/quic_chromium_client_session_test.cc
index 17ae6e8..1d269e2 100644
--- a/net/quic/quic_chromium_client_session_test.cc
+++ b/net/quic/quic_chromium_client_session_test.cc
@@ -318,7 +318,7 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, Handle) {
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeInitialSettingsPacket(1, nullptr));
   quic_data.AddRead(ASYNC, ERR_IO_PENDING);
@@ -404,7 +404,7 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, StreamRequest) {
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeInitialSettingsPacket(1, nullptr));
   quic_data.AddRead(ASYNC, ERR_IO_PENDING);
@@ -429,7 +429,7 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, ConfirmationRequiredStreamRequest) {
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeInitialSettingsPacket(1, nullptr));
   quic_data.AddRead(ASYNC, ERR_IO_PENDING);
@@ -454,7 +454,7 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, StreamRequestBeforeConfirmation) {
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeInitialSettingsPacket(1, nullptr));
   quic_data.AddRead(ASYNC, ERR_IO_PENDING);
@@ -484,7 +484,7 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, CancelStreamRequestBeforeRelease) {
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeInitialSettingsPacket(1, nullptr));
   quic_data.AddWrite(SYNCHRONOUS,
@@ -513,7 +513,7 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, AsyncStreamRequest) {
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeInitialSettingsPacket(1, nullptr));
   if (version_.transport_version == quic::QUIC_VERSION_99) {
@@ -590,7 +590,7 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, ClosedWithAsyncStreamRequest) {
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeInitialSettingsPacket(1, nullptr));
   if (version_.transport_version == quic::QUIC_VERSION_99) {
@@ -653,7 +653,7 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, CancelPendingStreamRequest) {
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeInitialSettingsPacket(1, nullptr));
   if (version_.transport_version == quic::QUIC_VERSION_99) {
@@ -725,7 +725,7 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, ConnectionCloseBeforeStreamRequest) {
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeInitialSettingsPacket(1, nullptr));
   quic_data.AddRead(
@@ -763,7 +763,7 @@
   // Force the connection close packet to use long headers with connection ID.
   server_maker_.SetEncryptionLevel(quic::ENCRYPTION_INITIAL);
 
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(ASYNC, ERR_IO_PENDING);
   quic_data.AddRead(
       ASYNC,
@@ -794,7 +794,7 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, ConnectionCloseWithPendingStreamRequest) {
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeInitialSettingsPacket(1, nullptr));
   if (version_.transport_version == quic::QUIC_VERSION_99) {
@@ -841,7 +841,7 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, MaxNumStreams) {
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeInitialSettingsPacket(1, nullptr));
   if (version_.transport_version == quic::QUIC_VERSION_99) {
@@ -1216,7 +1216,7 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, MaxNumStreamsViaRequest) {
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeInitialSettingsPacket(1, nullptr));
   if (version_.transport_version == quic::QUIC_VERSION_99) {
@@ -1678,7 +1678,7 @@
   }
   migrate_session_early_v2_ = true;
 
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read
   quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeDummyCHLOPacket(1));
   quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeDummyCHLOPacket(2));
@@ -1737,7 +1737,7 @@
 TEST_P(QuicChromiumClientSessionTest, RetransmittableOnWireTimeout) {
   migrate_session_early_v2_ = true;
 
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeInitialSettingsPacket(1, nullptr));
   quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakePingPacket(2, true));
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index 712f11a..c1344d1 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -1021,7 +1021,7 @@
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::CONFIRM_HANDSHAKE);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -1053,7 +1053,7 @@
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::CONFIRM_HANDSHAKE);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -1081,7 +1081,7 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -1118,7 +1118,7 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -1155,7 +1155,7 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -1226,7 +1226,7 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -1288,7 +1288,7 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -1356,7 +1356,7 @@
 
   AddQuicAlternateProtocolMapping(MockCryptoClientStream::CONFIRM_HANDSHAKE);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -1391,7 +1391,7 @@
   proxy_resolution_service_ = ProxyResolutionService::CreateFixedFromPacResult(
       "QUIC mail.example.org:70", TRAFFIC_ANNOTATION_FOR_TESTS);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -1440,7 +1440,7 @@
       "QUIC " + proxy_host + ":70", TRAFFIC_ANNOTATION_FOR_TESTS);
 
   client_maker_.set_hostname(origin_host);
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -1501,7 +1501,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   client_maker_.set_hostname(origin.host());
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -1594,7 +1594,7 @@
 
   // Second request should be sent via QUIC as a new list of verions supported
   // by the client has been advertised by the server.
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -1666,7 +1666,7 @@
   // |http_data| exits the socket pool before the main job is aborted
   // deterministic. The first main job gets aborted without the socket pool ever
   // dispensing the socket, making it available for the second try.
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset request_header_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &request_header_offset));
@@ -1726,12 +1726,12 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData mock_quic_data1;
+  MockQuicData mock_quic_data1(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data1.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
   mock_quic_data1.AddRead(ASYNC, ERR_SOCKET_NOT_CONNECTED);
-  MockQuicData mock_quic_data2;
+  MockQuicData mock_quic_data2(version_);
   header_stream_offset = 0;
   mock_quic_data2.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -1792,7 +1792,7 @@
   AddCertificate(&ssl_data_);
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -1859,7 +1859,7 @@
   AddCertificate(&ssl_data_);
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -1922,7 +1922,7 @@
   AddCertificate(&ssl_data_);
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -1967,7 +1967,7 @@
   AddCertificate(&ssl_data_);
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -2096,7 +2096,7 @@
   AddCertificate(&ssl_data_);
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -2161,7 +2161,7 @@
   AddCertificate(&ssl_data_);
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -2197,7 +2197,7 @@
     // Not available under version 99
     return;
   }
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -2277,7 +2277,7 @@
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
 
   // The request will initially go out over QUIC.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read
   int packet_num = 1;
   quic_data.AddWrite(SYNCHRONOUS,
@@ -2318,7 +2318,7 @@
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
   // Add data for the second QUIC connection to fail.
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic_data2.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);  // Write error.
   quic_data2.AddSocketDataToFactory(&socket_factory_);
@@ -2392,7 +2392,7 @@
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
 
   // The request will initially go out over QUIC.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read
   int packet_num = 1;
   quic_data.AddWrite(SYNCHRONOUS,
@@ -2434,7 +2434,7 @@
 
   // Quic connection will be retried on the alternate network after the initial
   // one fails on the default network.
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Handing read.
   quic_data2.AddWrite(SYNCHRONOUS,
@@ -2521,7 +2521,7 @@
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
 
   // The request will initially go out over QUIC.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read
   int packet_num = 1;
   quic_data.AddWrite(SYNCHRONOUS,
@@ -2552,7 +2552,7 @@
   AddHangingNonAlternateProtocolSocketData();
 
   // Quic connection will then be retried on the alternate network.
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   quic_data2.AddWrite(SYNCHRONOUS,
                       client_maker_.MakeDummyCHLOPacket(1));  // CHLO
@@ -2643,7 +2643,7 @@
   session_params_.quic_idle_connection_timeout_seconds = 5;
 
   // The request will initially go out over QUIC.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   spdy::SpdyPriority priority =
       ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
@@ -2760,7 +2760,7 @@
   session_params_.quic_connection_options.push_back(quic::k5RTO);
 
   // The request will initially go out over QUIC.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   spdy::SpdyPriority priority =
       ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
@@ -2887,7 +2887,7 @@
   session_params_.quic_connection_options.push_back(quic::k5RTO);
 
   // The request will initially go out over QUIC.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   spdy::SpdyPriority priority =
       ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
@@ -3018,7 +3018,7 @@
 TEST_P(QuicNetworkTransactionTest, ProtocolErrorAfterHandshakeConfirmed) {
   session_params_.retry_without_alt_svc_on_quic_errors = false;
   // The request will initially go out over QUIC.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
   quic_data.AddWrite(
@@ -3086,7 +3086,7 @@
   session_params_.quic_idle_connection_timeout_seconds = 5;
 
   // The request will initially go out over QUIC.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   spdy::SpdyPriority priority =
       ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
@@ -3227,7 +3227,7 @@
   session_params_.quic_idle_connection_timeout_seconds = 5;
 
   // The request will initially go out over QUIC.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   spdy::SpdyPriority priority =
       ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
@@ -3372,7 +3372,7 @@
   session_params_.quic_idle_connection_timeout_seconds = 5;
 
   // The request will initially go out over QUIC.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   spdy::SpdyPriority priority =
       ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
@@ -3524,7 +3524,7 @@
   session_params_.quic_connection_options.push_back(quic::k5RTO);
 
   // The request will initially go out over QUIC.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   spdy::SpdyPriority priority =
       ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
@@ -3676,7 +3676,7 @@
   session_params_.quic_connection_options.push_back(quic::k5RTO);
 
   // The request will initially go out over QUIC.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   spdy::SpdyPriority priority =
       ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
@@ -3810,7 +3810,7 @@
   session_params_.quic_idle_connection_timeout_seconds = 5;
 
   // The request will initially go out over QUIC.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
   quic_data.AddWrite(
@@ -3897,7 +3897,7 @@
 // retried over TCP.
 TEST_P(QuicNetworkTransactionTest, ResetAfterHandshakeConfirmedThenBroken) {
   // The request will initially go out over QUIC.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   spdy::SpdyPriority priority =
       ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
@@ -4003,7 +4003,7 @@
   verify_details.cert_verify_result.is_issued_by_known_root = true;
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset request_header_offset(0);
   quic::QuicStreamOffset response_header_offset(0);
   mock_quic_data.AddWrite(
@@ -4027,7 +4027,7 @@
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
 
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
-  MockQuicData mock_quic_data2;
+  MockQuicData mock_quic_data2(version_);
   mock_quic_data2.AddSocketDataToFactory(&socket_factory_);
   AddHangingNonAlternateProtocolSocketData();
 
@@ -4067,7 +4067,7 @@
   GURL origin2("https://www.example.org/");
   ASSERT_NE(origin1.host(), origin2.host());
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset request_header_offset(0);
   quic::QuicStreamOffset response_header_offset(0);
 
@@ -4227,7 +4227,7 @@
   // First QUIC request data.
   // Open a session to foo.example.org:443 using the first entry of the
   // alternative service list.
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &request_header_offset));
   mock_quic_data.AddWrite(
@@ -4298,7 +4298,7 @@
   // First QUIC request data.
   // Open a session to foo.example.org:443 using the first entry of the
   // alternative service list.
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &request_header_offset));
   mock_quic_data.AddWrite(
@@ -4379,7 +4379,7 @@
 // even if alternative service destination is different.
 TEST_P(QuicNetworkTransactionTest, PoolByOrigin) {
   session_params_.quic_allow_remote_alt_svc = true;
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset request_header_offset(0);
   quic::QuicStreamOffset response_header_offset(0);
 
@@ -4464,7 +4464,7 @@
   GURL origin2("https://www.example.org/");
   ASSERT_NE(origin1.host(), origin2.host());
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset request_header_offset(0);
   quic::QuicStreamOffset response_header_offset(0);
 
@@ -4606,7 +4606,7 @@
       client_headers_include_h2_stream_dependency_);
   server_maker_.set_hostname("www.example.org");
   client_maker_.set_hostname("www.example.org");
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &request_header_offset));
   // First QUIC request data.
@@ -4713,7 +4713,7 @@
   AddCertificate(&ssl_data_);
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -4770,7 +4770,7 @@
   socket_factory_.AddSocketDataProvider(&http_data);
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -4807,7 +4807,7 @@
   proxy_resolution_service_ = ProxyResolutionService::CreateFixedFromPacResult(
       "HTTPS mail.example.org:443", TRAFFIC_ANNOTATION_FOR_TESTS);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -4910,7 +4910,7 @@
 }
 
 TEST_P(QuicNetworkTransactionTest, ZeroRTTWithHttpRace) {
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
   mock_quic_data.AddWrite(
@@ -4947,7 +4947,7 @@
 }
 
 TEST_P(QuicNetworkTransactionTest, ZeroRTTWithNoHttpRace) {
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructClientRequestHeadersPacket(
@@ -5012,7 +5012,7 @@
 }
 
 TEST_P(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) {
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -5065,7 +5065,7 @@
 }
 
 TEST_P(QuicNetworkTransactionTest, ZeroRTTWithTooEarlyResponse) {
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset client_header_stream_offset = 0;
   quic::QuicStreamOffset server_header_stream_offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
@@ -5157,7 +5157,7 @@
 }
 
 TEST_P(QuicNetworkTransactionTest, ZeroRTTWithMultipleTooEarlyResponse) {
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset client_header_stream_offset = 0;
   quic::QuicStreamOffset server_header_stream_offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
@@ -5256,7 +5256,7 @@
 TEST_P(QuicNetworkTransactionTest,
        LogGranularQuicErrorCodeOnQuicProtocolErrorLocal) {
   session_params_.retry_without_alt_svc_on_quic_errors = false;
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -5308,7 +5308,7 @@
 TEST_P(QuicNetworkTransactionTest,
        LogGranularQuicErrorCodeOnQuicProtocolErrorRemote) {
   session_params_.retry_without_alt_svc_on_quic_errors = false;
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -5363,7 +5363,7 @@
 }
 
 TEST_P(QuicNetworkTransactionTest, RstSteamErrorHandling) {
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -5428,7 +5428,7 @@
 
 TEST_P(QuicNetworkTransactionTest, RstSteamBeforeHeaders) {
   session_params_.retry_without_alt_svc_on_quic_errors = false;
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -5565,7 +5565,7 @@
   // local IP address is sorted out after we configure the UDP socket.
   http_server_properties_.SetSupportsQuic(true, IPAddress(192, 0, 2, 33));
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
   mock_quic_data.AddWrite(
@@ -5624,7 +5624,7 @@
   // sorted out after we configure the UDP socket.
   http_server_properties_.SetSupportsQuic(true, IPAddress(1, 2, 3, 4));
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -5679,7 +5679,7 @@
   // handshake has not yet been confirmed and no stream has been created.
 
   // QUIC job will pause. When resumed, it will fail.
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);
   mock_quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
@@ -5801,7 +5801,7 @@
 }
 
 TEST_P(QuicNetworkTransactionTest, ConnectionCloseDuringConnect) {
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   mock_quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeDummyCHLOPacket(1));
   mock_quic_data.AddRead(ASYNC, ConstructServerConnectionClosePacket(1));
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
@@ -5860,7 +5860,7 @@
 }
 
 TEST_P(QuicNetworkTransactionTest, ConnectionCloseDuringConnectProxy) {
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   mock_quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeDummyCHLOPacket(1));
   mock_quic_data.AddRead(ASYNC, ConstructServerConnectionClosePacket(1));
   mock_quic_data.AddWrite(
@@ -5910,7 +5910,7 @@
   client_maker_.set_hostname("www.example.org");
   EXPECT_FALSE(
       test_socket_performance_watcher_factory_.rtt_notification_received());
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -6030,7 +6030,7 @@
       HostPortPair::FromString("mail.example.org:443"));
   session_params_.quic_migrate_sessions_on_network_change_v2 = true;
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic::QuicStreamOffset offset = 0;
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(1, &offset));
@@ -6042,7 +6042,7 @@
   socket_data.AddWrite(SYNCHRONOUS, ERR_FAILED);
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   socket_data2.AddConnect(SYNCHRONOUS, ERR_ADDRESS_INVALID);
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
@@ -6075,7 +6075,7 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset offset = 0;
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(1, &offset));
   socket_data.AddWrite(ASYNC, ERR_NO_BUFFER_SPACE);
@@ -6111,7 +6111,7 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset offset = 0;
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(1, &offset));
   socket_data.AddWrite(SYNCHRONOUS, ERR_NO_BUFFER_SPACE);
@@ -6148,7 +6148,7 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more data to read
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(1, &offset));
@@ -6185,7 +6185,7 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more data to read
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(1, &offset));
@@ -6225,7 +6225,7 @@
       quic::QuicStrCat("Write failed with error: ", ERR_MSG_TOO_BIG, " (",
                        strerror(ERR_MSG_TOO_BIG), ")");
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(1, &offset));
@@ -6254,7 +6254,7 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   uint64_t client_packet_number = 1;
   mock_quic_data.AddWrite(
@@ -6345,7 +6345,7 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   uint64_t client_packet_number = 1;
   // Initial SETTINGS frame.
@@ -6436,7 +6436,7 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
 
   quic::QuicStreamOffset offset = 0;
   int write_packet_index = 1;
@@ -6532,7 +6532,7 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -6600,7 +6600,7 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   uint64_t client_packet_number = 1;
   mock_quic_data.AddWrite(
@@ -6716,7 +6716,7 @@
   AddCertificate(&ssl_data_);
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -7063,7 +7063,7 @@
   verify_details.cert_verify_result.is_issued_by_known_root = true;
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);
   mock_quic_data.AddRead(ASYNC, 0);
 
@@ -7116,7 +7116,7 @@
   quic::QuicStreamOffset request_header_offset(0);
   quic::QuicStreamOffset response_header_offset(0);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   mock_quic_data.AddWrite(
       SYNCHRONOUS,
       ConstructInitialSettingsPacket(1, &request_header_offset, &client_maker));
@@ -7214,7 +7214,7 @@
       version_, quic::QuicUtils::CreateRandomConnectionId(&random_generator_),
       &clock_, origin1_, quic::Perspective::IS_SERVER, false);
 
-  MockQuicData mock_quic_data1;
+  MockQuicData mock_quic_data1(version_);
   quic::QuicStreamOffset header_stream_offset1 = 0;
   mock_quic_data1.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset1,
@@ -7246,7 +7246,7 @@
       version_, quic::QuicUtils::CreateRandomConnectionId(&random_generator_),
       &clock_, origin2_, quic::Perspective::IS_SERVER, false);
 
-  MockQuicData mock_quic_data2;
+  MockQuicData mock_quic_data2(version_);
   quic::QuicStreamOffset header_stream_offset2 = 0;
   mock_quic_data2.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset2,
@@ -7282,7 +7282,7 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   uint64_t client_packet_number = 1;
   mock_quic_data.AddWrite(
@@ -7410,7 +7410,7 @@
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
 
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
@@ -7473,7 +7473,7 @@
   proxy_resolution_service_ = ProxyResolutionService::CreateFixedFromPacResult(
       "QUIC proxy.example.org:70", TRAFFIC_ANNOTATION_FOR_TESTS);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -7566,7 +7566,7 @@
   proxy_resolution_service_ = ProxyResolutionService::CreateFixedFromPacResult(
       "QUIC proxy.example.org:70", TRAFFIC_ANNOTATION_FOR_TESTS);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -7666,7 +7666,7 @@
   proxy_resolution_service_ = ProxyResolutionService::CreateFixedFromPacResult(
       "QUIC proxy.example.org:70", TRAFFIC_ANNOTATION_FOR_TESTS);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   int write_packet_index = 1;
   mock_quic_data.AddWrite(
@@ -7831,7 +7831,7 @@
   proxy_resolution_service_ = ProxyResolutionService::CreateFixedFromPacResult(
       "QUIC proxy.example.org:70", TRAFFIC_ANNOTATION_FOR_TESTS);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset client_header_stream_offset = 0;
   quic::QuicStreamOffset server_header_stream_offset = 0;
   mock_quic_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(
@@ -8003,7 +8003,7 @@
   proxy_resolution_service_ = ProxyResolutionService::CreateFixedFromPacResult(
       "QUIC proxy.example.org:70", TRAFFIC_ANNOTATION_FOR_TESTS);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -8052,7 +8052,7 @@
   proxy_resolution_service_ = ProxyResolutionService::CreateFixedFromPacResult(
       "QUIC proxy.example.org:70", TRAFFIC_ANNOTATION_FOR_TESTS);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -8091,7 +8091,7 @@
   proxy_resolution_service_ = ProxyResolutionService::CreateFixedFromPacResult(
       "QUIC proxy.example.org:70", TRAFFIC_ANNOTATION_FOR_TESTS);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset client_header_stream_offset = 0;
   quic::QuicStreamOffset server_header_stream_offset = 0;
   mock_quic_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(
@@ -8215,7 +8215,7 @@
   proxy_resolution_service_ = ProxyResolutionService::CreateFixedFromPacResult(
       "QUIC proxy.example.org:70", TRAFFIC_ANNOTATION_FOR_TESTS);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -8265,7 +8265,7 @@
 
   const RequestPriority request_priority = MEDIUM;
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -8305,7 +8305,7 @@
   const RequestPriority kRequestPriority = MEDIUM;
   const RequestPriority kRequestPriority2 = LOWEST;
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
       ASYNC, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -8315,7 +8315,7 @@
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
 
   // Second connection attempt just fails - result doesn't really matter.
-  MockQuicData mock_quic_data2;
+  MockQuicData mock_quic_data2(version_);
   mock_quic_data2.AddConnect(SYNCHRONOUS, ERR_FAILED);
   mock_quic_data2.AddSocketDataToFactory(&socket_factory_);
 
@@ -8387,7 +8387,7 @@
         ProxyResolutionService::CreateFixedFromPacResult(
             "QUIC proxy.example.org:70", TRAFFIC_ANNOTATION_FOR_TESTS);
 
-    MockQuicData mock_quic_data;
+    MockQuicData mock_quic_data(version_);
     quic::QuicStreamOffset client_header_stream_offset = 0;
     quic::QuicStreamOffset server_header_stream_offset = 0;
     quic::QuicStreamOffset client_data_offset = 0;
@@ -8555,7 +8555,7 @@
   const quic::QuicStreamId push_stream_1 =
       GetNthServerInitiatedUnidirectionalStreamId(1);
 
-  MockQuicData mock_quic_data;
+  MockQuicData mock_quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   quic::QuicStreamOffset server_header_offset = 0;
   mock_quic_data.AddWrite(
@@ -8777,7 +8777,7 @@
       session_params_.origins_to_force_quic_on.insert(
           HostPortPair::FromString("mail.example.org:443"));
 
-      MockQuicData unpartitioned_mock_quic_data;
+      MockQuicData unpartitioned_mock_quic_data(version_);
       quic::QuicStreamOffset request_header_offset = 0;
       quic::QuicStreamOffset response_header_offset = 0;
       QuicTestPacketMaker client_maker1(
@@ -8855,7 +8855,7 @@
 
       // Reads and writes for the partitioned case, where two sockets are used.
 
-      MockQuicData partitioned_mock_quic_data1;
+      MockQuicData partitioned_mock_quic_data1(version_);
       request_header_offset = 0;
       response_header_offset = 0;
       QuicTestPacketMaker client_maker2(
@@ -8912,7 +8912,7 @@
 
       partitioned_mock_quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
 
-      MockQuicData partitioned_mock_quic_data2;
+      MockQuicData partitioned_mock_quic_data2(version_);
       request_header_offset = 0;
       response_header_offset = 0;
       QuicTestPacketMaker client_maker3(
@@ -9033,7 +9033,9 @@
       "HTTP/1.1 200 OK\r\n"
       "Content-Length: 10\r\n\r\n";
 
-  MockQuicData mock_quic_data[2];
+  std::unique_ptr<MockQuicData> mock_quic_data[2] = {
+      std::make_unique<MockQuicData>(version_),
+      std::make_unique<MockQuicData>(version_)};
 
   for (int index : {0, 1}) {
     QuicTestPacketMaker client_maker(
@@ -9045,11 +9047,11 @@
         &clock_, kDefaultServerHostName, quic::Perspective::IS_SERVER, false);
 
     quic::QuicStreamOffset header_stream_offset = 0;
-    mock_quic_data[index].AddWrite(
+    mock_quic_data[index]->AddWrite(
         SYNCHRONOUS,
         client_maker.MakeInitialSettingsPacket(1, &header_stream_offset));
 
-    mock_quic_data[index].AddWrite(
+    mock_quic_data[index]->AddWrite(
         SYNCHRONOUS,
         client_maker.MakeRequestHeadersPacketWithOffsetTracking(
             2, GetNthClientInitiatedBidirectionalStreamId(0), true, false,
@@ -9057,20 +9059,20 @@
                 HttpProxyConnectJob::kH2QuicTunnelPriority),
             ConnectRequestHeaders("mail.example.org:443"), 0,
             &header_stream_offset));
-    mock_quic_data[index].AddRead(
+    mock_quic_data[index]->AddRead(
         ASYNC, server_maker.MakeResponseHeadersPacketWithOffsetTracking(
                    1, GetNthClientInitiatedBidirectionalStreamId(0), false,
                    false, GetResponseHeaders("200 OK"), nullptr));
 
     std::string header = ConstructDataHeader(strlen(kGetRequest));
     if (version_.transport_version != quic::QUIC_VERSION_99) {
-      mock_quic_data[index].AddWrite(
+      mock_quic_data[index]->AddWrite(
           SYNCHRONOUS,
           client_maker.MakeAckAndDataPacket(
               3, false, GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1,
               false, 0, quic::QuicStringPiece(kGetRequest)));
     } else {
-      mock_quic_data[index].AddWrite(
+      mock_quic_data[index]->AddWrite(
           SYNCHRONOUS,
           client_maker.MakeAckAndMultipleDataFramesPacket(
               3, false, GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1,
@@ -9078,21 +9080,21 @@
     }
 
     std::string header2 = ConstructDataHeader(strlen(kGetResponse));
-    mock_quic_data[index].AddRead(
+    mock_quic_data[index]->AddRead(
         ASYNC, server_maker.MakeDataPacket(
                    2, GetNthClientInitiatedBidirectionalStreamId(0), false,
                    false, 0, header2 + std::string(kGetResponse)));
-    mock_quic_data[index].AddRead(
+    mock_quic_data[index]->AddRead(
         SYNCHRONOUS, server_maker.MakeDataPacket(
                          3, GetNthClientInitiatedBidirectionalStreamId(0),
                          false, false, strlen(kGetResponse) + header2.length(),
                          ConstructDataHeader(10) + std::string("0123456789")));
-    mock_quic_data[index].AddWrite(
+    mock_quic_data[index]->AddWrite(
         SYNCHRONOUS, client_maker.MakeAckPacket(4, 3, 2, 1, true));
-    mock_quic_data[index].AddRead(SYNCHRONOUS,
-                                  ERR_IO_PENDING);  // No more data to read
+    mock_quic_data[index]->AddRead(SYNCHRONOUS,
+                                   ERR_IO_PENDING);  // No more data to read
 
-    mock_quic_data[index].AddSocketDataToFactory(&socket_factory_);
+    mock_quic_data[index]->AddSocketDataToFactory(&socket_factory_);
   }
 
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
@@ -9114,10 +9116,10 @@
   RunTransaction(&trans2);
   CheckResponseData(&trans2, "0123456789");
 
-  EXPECT_TRUE(mock_quic_data[0].AllReadDataConsumed());
-  EXPECT_TRUE(mock_quic_data[0].AllWriteDataConsumed());
-  EXPECT_TRUE(mock_quic_data[1].AllReadDataConsumed());
-  EXPECT_TRUE(mock_quic_data[1].AllWriteDataConsumed());
+  EXPECT_TRUE(mock_quic_data[0]->AllReadDataConsumed());
+  EXPECT_TRUE(mock_quic_data[0]->AllWriteDataConsumed());
+  EXPECT_TRUE(mock_quic_data[1]->AllReadDataConsumed());
+  EXPECT_TRUE(mock_quic_data[1]->AllWriteDataConsumed());
 }
 
 }  // namespace test
diff --git a/net/quic/quic_proxy_client_socket_unittest.cc b/net/quic/quic_proxy_client_socket_unittest.cc
index d0d1ffb8..633d2d6 100644
--- a/net/quic/quic_proxy_client_socket_unittest.cc
+++ b/net/quic/quic_proxy_client_socket_unittest.cc
@@ -128,6 +128,7 @@
             quic::QuicUtils::GetHeadersStreamId(version_.transport_version) +
             quic::QuicUtils::StreamIdDelta(version_.transport_version)),
         client_headers_include_h2_stream_dependency_(std::get<1>(GetParam())),
+        mock_quic_data_(version_),
         crypto_config_(quic::test::crypto_test_utils::ProofVerifierForTesting(),
                        quic::TlsClientHandshaker::CreateSslCtx()),
         connection_id_(quic::test::TestConnectionId(2)),
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index 55c5305..9dfa951 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -92,6 +92,13 @@
   INITIAL_RTT_SOURCE_MAX,
 };
 
+enum class EmptyStaleResultLocation {
+  kResolveHost = 0,
+  kMatchFreshResult = 1,
+  kNotEmpty = 2,
+  kMaxValue = kNotEmpty,
+};
+
 // The maximum receive window sizes for QUIC sessions and streams.
 const int32_t kQuicSessionMaxRecvWindowSize = 15 * 1024 * 1024;  // 15 MB
 const int32_t kQuicStreamMaxRecvWindowSize = 6 * 1024 * 1024;    // 6 MB
@@ -157,6 +164,15 @@
   UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.ConnectionIpPooled", pooled);
 }
 
+void LogEmptyStaleResult(EmptyStaleResultLocation location) {
+  UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.StaleHostResolveFailed", location);
+}
+
+void LogSessionAvailabilityWhenValidatingHost(bool available) {
+  UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.SessionAvailableWhenValidatingDNS",
+                        available);
+}
+
 void SetInitialRttEstimate(base::TimeDelta estimate,
                            enum InitialRttEstimateSource source,
                            quic::QuicConfig* config) {
@@ -404,16 +420,31 @@
     if (session_) {
       QuicChromiumClientSession* session = session_;
       session_ = nullptr;
-      session->CloseSessionOnErrorLater(
-          ERR_ABORTED, quic::QUIC_STALE_CONNECTION_CANCELLED,
-          quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+      if (session) {
+        session->CloseSessionOnErrorLater(
+            ERR_ABORTED, quic::QUIC_STALE_CONNECTION_CANCELLED,
+            quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+      }
     }
   }
 
   bool DoesPeerAddressMatchWithFreshAddressList() {
+    LogSessionAvailabilityWhenValidatingHost(session_ != nullptr);
+
+    if (!session_)
+      return false;
+
     std::vector<net::IPEndPoint> endpoints =
         fresh_resolve_host_request_->GetAddressResults().value().endpoints();
-    IPEndPoint stale_address = ToIPEndPoint(session_->peer_address());
+
+    if (!resolve_host_request_->GetAddressResults()) {
+      LogEmptyStaleResult(EmptyStaleResultLocation::kMatchFreshResult);
+      return false;
+    }
+
+    LogEmptyStaleResult(EmptyStaleResultLocation::kNotEmpty);
+    IPEndPoint stale_address =
+        resolve_host_request_->GetAddressResults().value().front();
 
     if (std::find(endpoints.begin(), endpoints.end(), stale_address) !=
         endpoints.end()) {
@@ -696,7 +727,15 @@
     // Fresh request returned immediate results.
     LogStaleHostRacing(false);
     resolve_host_request_ = std::move(fresh_resolve_host_request_);
-    return rv;
+    return fresh_rv;
+  }
+
+  // Check to make sure stale host request does produce valid results.
+  if (!resolve_host_request_->GetAddressResults()) {
+    LogStaleHostRacing(false);
+    LogEmptyStaleResult(EmptyStaleResultLocation::kResolveHost);
+    resolve_host_request_ = std::move(fresh_resolve_host_request_);
+    return fresh_rv;
   }
 
   // No fresh host resolution is available at this time, but there is available
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index e6b6afe..1f7a86f 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -381,7 +381,7 @@
     EXPECT_FALSE(HasActiveSession(destination));
     size_t socket_count = socket_factory_->udp_client_socket_ports().size();
 
-    MockQuicData socket_data;
+    MockQuicData socket_data(version_);
     socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
     socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
     socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -528,13 +528,13 @@
     crypto_client_stream_factory_.SetConfig(config);
 
     // Set up first socket data provider.
-    MockQuicData socket_data1;
+    MockQuicData socket_data1(version_);
     socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
     socket_data1.AddSocketDataToFactory(socket_factory_.get());
 
     // Set up second socket data provider that is used after
     // migration.
-    MockQuicData socket_data2;
+    MockQuicData socket_data2(version_);
     socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
     socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
     socket_data2.AddWrite(
@@ -729,7 +729,7 @@
                                               "192.168.0.1", "");
 
     // Create a session and verify that the cached state is loaded.
-    MockQuicData socket_data;
+    MockQuicData socket_data(version_);
     socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
     socket_data.AddSocketDataToFactory(socket_factory_.get());
 
@@ -762,7 +762,7 @@
     EXPECT_TRUE(socket_data.AllWriteDataConsumed());
 
     // Create a session and verify that the cached state is loaded.
-    MockQuicData socket_data2;
+    MockQuicData socket_data2(version_);
     socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
     socket_data2.AddSocketDataToFactory(socket_factory_.get());
 
@@ -923,7 +923,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -976,7 +976,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
@@ -1005,7 +1005,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -1033,7 +1033,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -1063,7 +1063,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -1105,7 +1105,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -1144,7 +1144,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -1177,7 +1177,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -1210,7 +1210,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -1238,7 +1238,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -1270,7 +1270,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -1311,7 +1311,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -1406,11 +1406,11 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data1.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data1.AddSocketDataToFactory(socket_factory_.get());
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data2.AddSocketDataToFactory(socket_factory_.get());
@@ -1468,7 +1468,7 @@
 TEST_P(QuicStreamFactoryTest, HttpsPooling) {
   Initialize();
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -1512,7 +1512,7 @@
 
 TEST_P(QuicStreamFactoryTest, HttpsPoolingWithMatchingPins) {
   Initialize();
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -1563,11 +1563,11 @@
 TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithDifferentPins) {
   Initialize();
 
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data1.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data1.AddSocketDataToFactory(socket_factory_.get());
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data2.AddSocketDataToFactory(socket_factory_.get());
@@ -1629,11 +1629,11 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data2.AddSocketDataToFactory(socket_factory_.get());
@@ -1691,7 +1691,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   quic::QuicStreamId stream_id = GetNthClientInitiatedBidirectionalStreamId(0);
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   if (version_.transport_version == quic::QUIC_VERSION_99) {
     socket_data.AddWrite(SYNCHRONOUS, client_maker_.MakeStreamsBlockedPacket(
@@ -1779,7 +1779,7 @@
 
 TEST_P(QuicStreamFactoryTest, ResolutionErrorInCreate) {
   Initialize();
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
   host_resolver_->rules()->AddSimulatedFailure(kDefaultServerHostName);
@@ -1801,7 +1801,7 @@
 TEST_P(QuicStreamFactoryTest, ConnectErrorInCreate) {
   Initialize();
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddConnect(SYNCHRONOUS, ERR_ADDRESS_IN_USE);
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
@@ -1821,7 +1821,7 @@
 
 TEST_P(QuicStreamFactoryTest, CancelCreate) {
   Initialize();
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -1859,7 +1859,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddWrite(
@@ -1869,7 +1869,7 @@
                            3, true, quic::QUIC_INTERNAL_ERROR, "net error"));
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data2.AddSocketDataToFactory(socket_factory_.get());
@@ -1928,7 +1928,7 @@
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::COLD_START_WITH_CHLO_SENT);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   // Trigger PACKET_WRITE_ERROR when sending packets in crypto connect.
   socket_data.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
@@ -1951,7 +1951,7 @@
       MockCryptoClientStream::COLD_START);
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data2.AddSocketDataToFactory(socket_factory_.get());
@@ -1994,7 +1994,7 @@
   host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
                                             "192.168.0.1", "");
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   // Trigger PACKET_WRITE_ERROR when sending packets in crypto connect.
   socket_data.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
@@ -2017,7 +2017,7 @@
       MockCryptoClientStream::COLD_START);
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data2.AddSocketDataToFactory(socket_factory_.get());
@@ -2056,7 +2056,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddWrite(
@@ -2066,7 +2066,7 @@
                        3, true, quic::QUIC_IP_ADDRESS_CHANGED, "net error"));
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data2.AddSocketDataToFactory(socket_factory_.get());
@@ -2140,7 +2140,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData quic_data1;
+  MockQuicData quic_data1(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   quic_data1.AddWrite(SYNCHRONOUS,
                       ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -2156,7 +2156,7 @@
   quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   quic_data1.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   quic::QuicStreamOffset header_stream_offset2 = 0;
   quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   quic_data2.AddWrite(
@@ -2242,7 +2242,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddWrite(
@@ -2314,7 +2314,7 @@
   scoped_mock_network_change_notifier_->mock_network_change_notifier()
       ->QueueNetworkMadeDefault(kDefaultNetworkForTests);
 
-  MockQuicData quic_data1;
+  MockQuicData quic_data1(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging Read.
   quic_data1.AddWrite(SYNCHRONOUS,
@@ -2327,7 +2327,7 @@
 
   // Set up the second socket data provider that is used after migration.
   // The response to the earlier request is read on the new socket.
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   // Connectivity probe to be sent on the new path.
   quic_data2.AddWrite(SYNCHRONOUS,
                       client_maker_.MakeConnectivityProbingPacket(3, true));
@@ -2489,7 +2489,7 @@
   scoped_mock_network_change_notifier_->mock_network_change_notifier()
       ->QueueNetworkMadeDefault(kDefaultNetworkForTests);
 
-  MockQuicData quic_data1;
+  MockQuicData quic_data1(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging Read.
   quic_data1.AddWrite(SYNCHRONOUS,
@@ -2502,7 +2502,7 @@
 
   // Set up the second socket data provider that is used after migration.
   // The response to the earlier request is read on the new socket.
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   // First connectivity probe to be sent on the new path.
   quic_data2.AddWrite(SYNCHRONOUS,
                       client_maker_.MakeConnectivityProbingPacket(3, true));
@@ -2667,7 +2667,7 @@
   auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
   QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -2750,7 +2750,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   if (!migrate_idle_sessions) {
@@ -2767,7 +2767,7 @@
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
   // Set up the second socket data provider that is used for probing.
-  MockQuicData quic_data1;
+  MockQuicData quic_data1(version_);
   // Connectivity probe to be sent on the new path.
   quic_data1.AddWrite(SYNCHRONOUS,
                       client_maker_.MakeConnectivityProbingPacket(2, true));
@@ -2845,7 +2845,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddWrite(
@@ -2916,8 +2916,8 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData failed_socket_data;
-  MockQuicData socket_data;
+  MockQuicData failed_socket_data(version_);
+  MockQuicData socket_data(version_);
   if (migrate_idle_sessions) {
     quic::QuicStreamOffset header_stream_offset = 0;
     failed_socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
@@ -3001,7 +3001,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddWrite(
@@ -3070,7 +3070,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   if (!migrate_idle_sessions) {
@@ -3082,7 +3082,7 @@
   }
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData quic_data1;
+  MockQuicData quic_data1(version_);
   if (migrate_idle_sessions) {
     // Set up the second socket data provider that is used for probing.
     // Connectivity probe to be sent on the new path.
@@ -3151,12 +3151,12 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData default_socket_data;
+  MockQuicData default_socket_data(version_);
   default_socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   default_socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   default_socket_data.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData alternate_socket_data;
+  MockQuicData alternate_socket_data(version_);
   if (migrate_idle_sessions) {
     // Set up second socket data provider that is used after migration.
     alternate_socket_data.AddRead(SYNCHRONOUS,
@@ -3223,7 +3223,7 @@
   QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), runner_.get());
 
   int packet_number = 1;
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(
@@ -3277,7 +3277,7 @@
 
   // Set up second socket data provider that is used after migration.
   // The response to the earlier request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddWrite(
       SYNCHRONOUS,
       client_maker_.MakePingPacket(packet_number++, /*include_version=*/true));
@@ -3348,7 +3348,7 @@
   // Use the test task runner.
   QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), runner_.get());
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(
@@ -3404,7 +3404,7 @@
 
   // Set up second socket data provider that is used after migration.
   // The response to the earlier request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddWrite(
       SYNCHRONOUS, client_maker_.MakePingPacket(3, /*include_version=*/true));
   socket_data1.AddRead(
@@ -3478,7 +3478,7 @@
       ->QueueNetworkMadeDefault(kDefaultNetworkForTests);
 
   int packet_number = 1;
-  MockQuicData quic_data1;
+  MockQuicData quic_data1(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging Read.
   quic_data1.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(
@@ -3492,7 +3492,7 @@
 
   // Set up the second socket data provider that is used for probing on the
   // alternate network.
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   // Connectivity probe to be sent on the new path.
   quic_data2.AddWrite(SYNCHRONOUS, client_maker_.MakeConnectivityProbingPacket(
                                        packet_number++, true));
@@ -3655,7 +3655,7 @@
       ->QueueNetworkMadeDefault(kDefaultNetworkForTests);
 
   int packet_number = 1;
-  MockQuicData quic_data1;
+  MockQuicData quic_data1(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging Read.
   quic_data1.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(
@@ -3673,7 +3673,7 @@
 
   // Set up the second socket data provider that is used after migration.
   // The response to the earlier request is read on the new socket.
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   // Connectivity probe to be sent on the new path.
   quic_data2.AddWrite(SYNCHRONOUS, client_maker_.MakeConnectivityProbingPacket(
                                        packet_number++, true));
@@ -3811,7 +3811,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData quic_data1;
+  MockQuicData quic_data1(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   quic_data1.AddWrite(SYNCHRONOUS,
                       ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -3827,7 +3827,7 @@
   quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   quic_data1.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   quic::QuicStreamOffset header_stream_offset2 = 0;
   quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   quic_data2.AddWrite(
@@ -3925,7 +3925,7 @@
   scoped_mock_network_change_notifier_->mock_network_change_notifier()
       ->QueueNetworkMadeDefault(kDefaultNetworkForTests);
 
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   quic_data.AddWrite(SYNCHRONOUS,
                      ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -4044,7 +4044,7 @@
       ->QueueNetworkMadeDefault(kDefaultNetworkForTests);
 
   int packet_number = 1;
-  MockQuicData quic_data1;
+  MockQuicData quic_data1(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   quic_data1.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(
                                        packet_number++, &header_stream_offset));
@@ -4068,7 +4068,7 @@
   quic_data1.AddSocketDataToFactory(socket_factory_.get());
 
   // Set up the second socket data provider that is used after migration.
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   // Connectivity probe to be sent on the new path.
   quic_data2.AddWrite(SYNCHRONOUS, client_maker_.MakeConnectivityProbingPacket(
                                        packet_number++, false));
@@ -4215,7 +4215,7 @@
   scoped_mock_network_change_notifier_->mock_network_change_notifier()
       ->QueueNetworkMadeDefault(kDefaultNetworkForTests);
 
-  MockQuicData quic_data1;
+  MockQuicData quic_data1(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging Read.
   quic_data1.AddWrite(SYNCHRONOUS,
@@ -4228,7 +4228,7 @@
 
   // Set up the second socket data provider that is used after migration.
   // The response to the earlier request is read on the new socket.
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   // Connectivity probe to be sent on the new path.
   quic_data2.AddWrite(SYNCHRONOUS,
                       client_maker_.MakeConnectivityProbingPacket(3, true));
@@ -4369,12 +4369,12 @@
        MigrateMultipleSessionsToBadSocketsAfterDisconnected) {
   InitializeConnectionMigrationV2Test({kDefaultNetworkForTests});
 
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data1.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data1.AddWrite(ASYNC, OK);
   socket_data1.AddSocketDataToFactory(socket_factory_.get());
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data2.AddWrite(ASYNC, OK);
@@ -4494,7 +4494,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   quic_data.AddWrite(SYNCHRONOUS,
                      ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -4592,7 +4592,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   if (!migrate_idle_sessions) {
@@ -4608,7 +4608,7 @@
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
   // Set up the second socket data provider that is used for probing.
-  MockQuicData quic_data1;
+  MockQuicData quic_data1(version_);
   // Connectivity probe to be sent on the new path.
   quic_data1.AddWrite(SYNCHRONOUS,
                       client_maker_.MakeConnectivityProbingPacket(2, true));
@@ -4687,7 +4687,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddWrite(
@@ -4760,7 +4760,7 @@
   auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
   QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(
@@ -4771,7 +4771,7 @@
   // Set up second socket data provider that is used after
   // migration. The request is rewritten to this new socket, and the
   // response to the request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddWrite(
       SYNCHRONOUS, ConstructGetRequestPacket(
                        2, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -4912,7 +4912,7 @@
   auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
   QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(
@@ -4923,7 +4923,7 @@
   // Set up second socket data provider that is used after
   // migration. The request is rewritten to this new socket, and the
   // response to the request is read on this new socket.
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   quic_data2.AddWrite(
       SYNCHRONOUS, ConstructGetRequestPacket(
                        2, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -4995,7 +4995,7 @@
   EXPECT_EQ(200, response.headers->response_code());
 
   // Set up the third socket data provider for migrate back to default network.
-  MockQuicData quic_data3;
+  MockQuicData quic_data3(version_);
   // Connectivity probe to be sent on the new path.
   quic_data3.AddWrite(SYNCHRONOUS,
                       client_maker_.MakeConnectivityProbingPacket(3, false));
@@ -5052,7 +5052,7 @@
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::COLD_START_WITH_CHLO_SENT);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(ASYNC, client_maker_.MakeDummyCHLOPacket(1));
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -5113,7 +5113,7 @@
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::COLD_START_WITH_CHLO_SENT);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(ASYNC, client_maker_.MakeDummyCHLOPacket(1));
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -5204,13 +5204,13 @@
       MockCryptoClientStream::COLD_START_WITH_CHLO_SENT);
 
   // Socket data for connection on the default network.
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(ASYNC, client_maker_.MakeDummyCHLOPacket(1));
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
   // Socket data for connection on the alternate network.
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data2.AddWrite(SYNCHRONOUS, client_maker_.MakeDummyCHLOPacket(1));
   socket_data2.AddRead(ASYNC, ERR_IO_PENDING);  // Pause.
@@ -5234,7 +5234,7 @@
   socket_data2.AddSocketDataToFactory(socket_factory_.get());
 
   // Socket data for probing on the default network.
-  MockQuicData probing_data;
+  MockQuicData probing_data(version_);
   probing_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   probing_data.AddWrite(SYNCHRONOUS,
                         client_maker_.MakeConnectivityProbingPacket(4, false));
@@ -5351,7 +5351,7 @@
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::COLD_START_WITH_CHLO_SENT);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   // Trigger PACKET_WRITE_ERROR when sending packets in crypto connect.
   socket_data.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
@@ -5374,7 +5374,7 @@
       MockCryptoClientStream::COLD_START);
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data2.AddSocketDataToFactory(socket_factory_.get());
@@ -5422,14 +5422,14 @@
       MockCryptoClientStream::COLD_START_WITH_CHLO_SENT);
 
   // Socket data for connection on the default network.
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   // Trigger PACKET_WRITE_ERROR when sending packets in crypto connect.
   socket_data.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
   // Socket data for connection on the alternate network.
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data2.AddWrite(SYNCHRONOUS, client_maker_.MakeDummyCHLOPacket(1));
   socket_data2.AddRead(ASYNC, ERR_IO_PENDING);  // Pause.
@@ -5514,7 +5514,7 @@
 
   auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(
@@ -5551,7 +5551,7 @@
   // Set up second socket data provider that is used after
   // migration. The request is rewritten to this new socket, and the
   // response to the request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddWrite(
       SYNCHRONOUS, ConstructGetRequestPacket(
                        2, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -5613,7 +5613,7 @@
   // Use the test task runner, to force the migration alarm timeout later.
   QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), runner_.get());
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddWrite(write_error_mode, ERR_ADDRESS_UNREACHABLE);
@@ -5723,7 +5723,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(
@@ -5734,7 +5734,7 @@
   // Set up second socket data provider that is used after
   // migration. The request is rewritten to this new socket, and the
   // response to the request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddWrite(
       SYNCHRONOUS, ConstructGetRequestPacket(
                        2, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -5856,7 +5856,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   int packet_number = 1;
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(
@@ -5868,7 +5868,7 @@
   // Set up second socket data provider that is used after
   // migration. The request is rewritten to this new socket, and the
   // response to the request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddWrite(
       SYNCHRONOUS,
       ConstructGetRequestPacket(packet_number++,
@@ -5995,7 +5995,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   int packet_number = 1;
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(
@@ -6008,7 +6008,7 @@
   // Set up second socket data provider that is used after
   // migration. The request is rewritten to this new socket, and the
   // response to the request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   // The packet triggered writer error will be sent anyway even if the stream
   // will be cancelled later.
   socket_data1.AddWrite(
@@ -6139,8 +6139,8 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData failed_socket_data;
-  MockQuicData socket_data;
+  MockQuicData failed_socket_data(version_);
+  MockQuicData socket_data(version_);
   if (migrate_idle_sessions) {
     quic::QuicStreamOffset header_stream_offset = 0;
     // The socket data provider for the original socket before migration.
@@ -6253,7 +6253,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddWrite(write_error_mode, ERR_ADDRESS_UNREACHABLE);
@@ -6343,7 +6343,7 @@
 
   // Set up the socket data used by the original network, which encounters a
   // write erorr.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data1.AddWrite(
@@ -6354,14 +6354,14 @@
 
   // Set up the socket data used by the alternate network, which also
   // encounters a write error.
-  MockQuicData failed_quic_data2;
+  MockQuicData failed_quic_data2(version_);
   failed_quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   failed_quic_data2.AddWrite(write_error_mode_on_new_network, ERR_FAILED);
   failed_quic_data2.AddSocketDataToFactory(socket_factory_.get());
 
   // Set up the third socket data used by original network, which encounters a
   // write error again.
-  MockQuicData failed_quic_data1;
+  MockQuicData failed_quic_data1(version_);
   failed_quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   failed_quic_data1.AddWrite(write_error_mode_on_old_network, ERR_FAILED);
   failed_quic_data1.AddSocketDataToFactory(socket_factory_.get());
@@ -6369,7 +6369,7 @@
   // Set up the last socket data used by the alternate network, which will
   // finish migration successfully. The request is rewritten to this new socket,
   // and the response to the request is read on this socket.
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   socket_data2.AddWrite(
       SYNCHRONOUS, ConstructGetRequestPacket(
                        2, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -6475,7 +6475,7 @@
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::COLD_START_WITH_CHLO_SENT);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(ASYNC, client_maker_.MakeDummyCHLOPacket(1));
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -6511,7 +6511,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(
@@ -6548,7 +6548,7 @@
   // Set up second socket data provider that is used after
   // migration. The request is rewritten to this new socket, and the
   // response to the request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddWrite(
       SYNCHRONOUS, ConstructGetRequestPacket(
                        2, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -6645,7 +6645,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(
@@ -6682,7 +6682,7 @@
   // Set up second socket data provider that is used after
   // migration. The request is rewritten to this new socket, and the
   // response to the request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddWrite(
       SYNCHRONOUS, ConstructGetRequestPacket(
                        2, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -6785,7 +6785,7 @@
   // Use the test task runner.
   QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), runner_.get());
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   socket_data.AddWrite(
@@ -6833,7 +6833,7 @@
 
   // Set up second socket data provider that is used after migration.
   // The response to the earlier request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddWrite(
       SYNCHRONOUS, ConstructGetRequestPacket(
                        2, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -6915,7 +6915,7 @@
   auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
   QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -6952,7 +6952,7 @@
 
   // Set up second socket data provider that is used after
   // migration. The response to the request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddWrite(
       SYNCHRONOUS, client_maker_.MakePingPacket(3, /*include_version=*/true));
   socket_data1.AddRead(
@@ -7020,7 +7020,7 @@
   auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
   QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -7057,7 +7057,7 @@
   // Set up second socket data provider that is used after
   // migration. The request is written to this new socket, and the
   // response to the request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddWrite(
       SYNCHRONOUS, client_maker_.MakePingPacket(2, /*include_version=*/true));
   socket_data1.AddWrite(
@@ -7134,7 +7134,7 @@
   auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
   QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -7171,7 +7171,7 @@
   // Set up second socket data provider that is used after
   // migration. The request is written to this new socket, and the
   // response to the request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddWrite(
       SYNCHRONOUS, client_maker_.MakePingPacket(2, /*include_version=*/true));
   socket_data1.AddWrite(
@@ -7250,7 +7250,7 @@
       factory_.get(),
       std::make_unique<QuicChromiumAlarmFactory>(task_runner.get(), &clock_));
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -7261,7 +7261,7 @@
   // Set up second socket data provider that is used after
   // migration. The request is written to this new socket, and the
   // response to the request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   // The PING packet sent post migration.
   socket_data1.AddWrite(SYNCHRONOUS, client_maker_.MakePingPacket(2, true));
   socket_data1.AddWrite(
@@ -7410,7 +7410,7 @@
       factory_.get(),
       std::make_unique<QuicChromiumAlarmFactory>(task_runner.get(), &clock_));
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -7421,7 +7421,7 @@
   // Set up second socket data provider that is used after
   // migration. The request is written to this new socket, and the
   // response to the request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   // The PING packet sent post migration.
   socket_data1.AddWrite(SYNCHRONOUS, client_maker_.MakePingPacket(2, true));
   socket_data1.AddWrite(
@@ -7569,7 +7569,7 @@
       factory_.get(),
       std::make_unique<QuicChromiumAlarmFactory>(task_runner.get(), &clock_));
 
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data1.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -7707,7 +7707,7 @@
       factory_.get(),
       std::make_unique<QuicChromiumAlarmFactory>(task_runner.get(), &clock_));
 
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data1.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -7845,7 +7845,7 @@
       factory_.get(),
       std::make_unique<QuicChromiumAlarmFactory>(task_runner.get(), &clock_));
 
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data1.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -7985,7 +7985,7 @@
       factory_.get(),
       std::make_unique<QuicChromiumAlarmFactory>(task_runner.get(), &clock_));
 
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data1.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -8117,7 +8117,7 @@
   auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
   QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
@@ -8154,7 +8154,7 @@
   // Set up second socket data provider that is used after
   // migration. The request is written to this new socket, and the
   // response to the request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddWrite(
       SYNCHRONOUS, ConstructGetRequestPacket(
                        2, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -8262,7 +8262,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(
@@ -8316,7 +8316,7 @@
   // Set up second socket data provider that is used after
   // migration. The request is rewritten to this new socket, and the
   // response to the request is read on this new socket.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddWrite(
       SYNCHRONOUS, ConstructGetRequestPacket(
                        2, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -8405,13 +8405,13 @@
   QuicStreamFactoryPeer::SetTickClock(factory_.get(),
                                       task_runner->GetMockTickClock());
 
-  MockQuicData default_socket_data;
+  MockQuicData default_socket_data(version_);
   default_socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   default_socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   default_socket_data.AddSocketDataToFactory(socket_factory_.get());
 
   // Set up second socket data provider that is used after migration.
-  MockQuicData alternate_socket_data;
+  MockQuicData alternate_socket_data(version_);
   alternate_socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   // Ping packet to send after migration.
   alternate_socket_data.AddWrite(
@@ -8419,32 +8419,32 @@
   alternate_socket_data.AddSocketDataToFactory(socket_factory_.get());
 
   // Set up probing socket for migrating back to the default network.
-  MockQuicData quic_data;                          // retry count: 0.
+  MockQuicData quic_data(version_);                // retry count: 0.
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   quic_data.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
   quic_data.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData quic_data1;                          // retry count: 1
+  MockQuicData quic_data1(version_);                // retry count: 1
   quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   quic_data1.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
   quic_data1.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData quic_data2;                          // retry count: 2
+  MockQuicData quic_data2(version_);                // retry count: 2
   quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   quic_data2.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
   quic_data2.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData quic_data3;                          // retry count: 3
+  MockQuicData quic_data3(version_);                // retry count: 3
   quic_data3.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   quic_data3.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
   quic_data3.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData quic_data4;                          // retry count: 4
+  MockQuicData quic_data4(version_);                // retry count: 4
   quic_data4.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   quic_data4.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
   quic_data4.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData quic_data5;                          // retry count: 5
+  MockQuicData quic_data5(version_);                // retry count: 5
   quic_data5.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   quic_data5.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
   quic_data5.AddSocketDataToFactory(socket_factory_.get());
@@ -8524,13 +8524,13 @@
   QuicStreamFactoryPeer::SetTickClock(factory_.get(),
                                       task_runner->GetMockTickClock());
 
-  MockQuicData default_socket_data;
+  MockQuicData default_socket_data(version_);
   default_socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   default_socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   default_socket_data.AddSocketDataToFactory(socket_factory_.get());
 
   // Set up second socket data provider that is used after migration.
-  MockQuicData alternate_socket_data;
+  MockQuicData alternate_socket_data(version_);
   alternate_socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   // Ping packet to send after migration.
   alternate_socket_data.AddWrite(
@@ -8538,27 +8538,27 @@
   alternate_socket_data.AddSocketDataToFactory(socket_factory_.get());
 
   // Set up probing socket for migrating back to the default network.
-  MockQuicData quic_data;                          // retry count: 0.
+  MockQuicData quic_data(version_);                // retry count: 0.
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   quic_data.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
   quic_data.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData quic_data1;                          // retry count: 1
+  MockQuicData quic_data1(version_);                // retry count: 1
   quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   quic_data1.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
   quic_data1.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData quic_data2;                          // retry count: 2
+  MockQuicData quic_data2(version_);                // retry count: 2
   quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   quic_data2.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
   quic_data2.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData quic_data3;                          // retry count: 3
+  MockQuicData quic_data3(version_);                // retry count: 3
   quic_data3.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   quic_data3.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
   quic_data3.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData quic_data4;                          // retry count: 4
+  MockQuicData quic_data4(version_);                // retry count: 4
   quic_data4.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
   quic_data4.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
   quic_data4.AddSocketDataToFactory(socket_factory_.get());
@@ -8630,7 +8630,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   quic::QuicStreamOffset header_stream_offset = 0;
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data1.AddWrite(
@@ -8681,7 +8681,7 @@
   // Set up second socket data provider that is used after
   // migration. The request is rewritten to this new socket, and the
   // response to the request is read on this new socket.
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   socket_data2.AddWrite(
       SYNCHRONOUS, client_maker_.MakePingPacket(3, /*include_version=*/true));
   socket_data2.AddRead(
@@ -8780,7 +8780,7 @@
   crypto_client_stream_factory_.SetConfig(config);
 
   // Set up only socket data provider.
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data1.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data1.AddWrite(
@@ -8837,12 +8837,12 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data2.AddWrite(SYNCHRONOUS,
                         ConstructInitialSettingsPacket(1, nullptr));
@@ -8981,7 +8981,7 @@
 
   QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), runner_.get());
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
@@ -9018,12 +9018,12 @@
 
   QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), runner_.get());
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data2.AddWrite(SYNCHRONOUS,
                         ConstructInitialSettingsPacket(1, nullptr));
@@ -9118,7 +9118,7 @@
 TEST_P(QuicStreamFactoryTest, StartCertVerifyJob) {
   Initialize();
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -9184,7 +9184,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   QuicStreamFactoryPeer::SetYieldAfterPackets(factory_.get(), 0);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ConstructClientConnectionClosePacket(1));
   socket_data.AddRead(ASYNC, OK);
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -9233,7 +9233,7 @@
   QuicStreamFactoryPeer::SetYieldAfterDuration(
       factory_.get(), quic::QuicTime::Delta::FromMilliseconds(-1));
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ConstructClientConnectionClosePacket(1));
   socket_data.AddRead(ASYNC, OK);
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -9279,7 +9279,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -9324,7 +9324,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data1;
+  MockQuicData socket_data1(version_);
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data1.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data1.AddWrite(
@@ -9333,7 +9333,7 @@
                        quic::QUIC_STREAM_CANCELLED));
   socket_data1.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data2.AddSocketDataToFactory(socket_factory_.get());
@@ -9399,7 +9399,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -9855,7 +9855,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -9883,7 +9883,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -9939,7 +9939,7 @@
 
   host_resolver_->set_ondemand_mode(true);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_FAILED);
   socket_data.AddWrite(SYNCHRONOUS, ERR_FAILED);
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -9992,7 +9992,7 @@
       MockCryptoClientStream::ZERO_RTT);
   factory_->set_require_confirmation(true);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(ASYNC, ERR_IO_PENDING);  // Pause
   socket_data.AddRead(ASYNC, ERR_FAILED);
   socket_data.AddWrite(ASYNC, ERR_FAILED);
@@ -10044,7 +10044,7 @@
 
   host_resolver_->set_synchronous_mode(true);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_FAILED);
   socket_data.AddWrite(SYNCHRONOUS, ERR_FAILED);
   socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -10081,7 +10081,7 @@
       MockCryptoClientStream::ZERO_RTT);
   factory_->set_require_confirmation(true);
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(ASYNC, ERR_IO_PENDING);  // Pause
   socket_data.AddRead(ASYNC, ERR_FAILED);
   socket_data.AddWrite(ASYNC, ERR_FAILED);
@@ -10196,7 +10196,7 @@
   // Expire the cache
   cache->Invalidate();
 
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   quic_data.AddSocketDataToFactory(socket_factory_.get());
@@ -10231,7 +10231,7 @@
   host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
                                             kNonCachedIPAddress, "");
 
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   quic_data.AddSocketDataToFactory(socket_factory_.get());
@@ -10289,7 +10289,7 @@
   // Expire the cache
   cache->Invalidate();
 
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   quic_data.AddSocketDataToFactory(socket_factory_.get());
@@ -10350,7 +10350,7 @@
   // Expire the cache
   cache->Invalidate();
 
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   quic_data.AddSocketDataToFactory(socket_factory_.get());
@@ -10417,7 +10417,7 @@
   // Expire the cache
   cache->Invalidate();
 
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   quic_data.AddSocketDataToFactory(socket_factory_.get());
@@ -10478,7 +10478,7 @@
   cache->Invalidate();
 
   // Socket for the stale connection which will invoke connection closure.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   quic_data.AddWrite(
@@ -10488,7 +10488,7 @@
   quic_data.AddSocketDataToFactory(socket_factory_.get());
 
   // Socket for the new connection.
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   quic_data2.AddSocketDataToFactory(socket_factory_.get());
@@ -10550,7 +10550,7 @@
   // Expire the cache
   cache->Invalidate();
 
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   quic_data.AddWrite(
@@ -10559,7 +10559,7 @@
           2, true, quic::QUIC_STALE_CONNECTION_CANCELLED, "net error"));
   quic_data.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic_data2.AddSocketDataToFactory(socket_factory_.get());
 
@@ -10622,7 +10622,7 @@
   // Expire the cache
   cache->Invalidate();
 
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
   quic_data.AddWrite(
@@ -10631,7 +10631,7 @@
           1, true, quic::QUIC_STALE_CONNECTION_CANCELLED, "net error"));
   quic_data.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE);
   quic_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
@@ -10676,7 +10676,7 @@
   host_resolver_->set_synchronous_mode(true);
   host_resolver_->rules()->AddSimulatedFailure(host_port_pair_.host());
 
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddSocketDataToFactory(socket_factory_.get());
   QuicStreamRequest request(factory_.get());
 
@@ -10701,7 +10701,7 @@
   host_resolver_->set_ondemand_mode(true);
   host_resolver_->rules()->AddSimulatedFailure(host_port_pair_.host());
 
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddSocketDataToFactory(socket_factory_.get());
   QuicStreamRequest request(factory_.get());
 
@@ -10742,7 +10742,7 @@
   cache->Invalidate();
 
   // Socket for the stale connection which is supposed to disconnect.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   quic_data.AddWrite(
@@ -10798,11 +10798,11 @@
   cache->Invalidate();
 
   // Simulate synchronous connect failure.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddConnect(SYNCHRONOUS, ERR_ADDRESS_IN_USE);
   quic_data.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   quic_data2.AddConnect(SYNCHRONOUS, ERR_ADDRESS_IN_USE);
   quic_data2.AddSocketDataToFactory(socket_factory_.get());
 
@@ -10846,11 +10846,11 @@
   cache->Invalidate();
 
   // Add failure for the stale connection.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddConnect(SYNCHRONOUS, ERR_ADDRESS_IN_USE);
   quic_data.AddSocketDataToFactory(socket_factory_.get());
 
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   quic_data2.AddSocketDataToFactory(socket_factory_.get());
@@ -10908,12 +10908,12 @@
   cache->Invalidate();
 
   // Add failure for stale connection.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddConnect(SYNCHRONOUS, ERR_ADDRESS_IN_USE);
   quic_data.AddSocketDataToFactory(socket_factory_.get());
 
   // Add failure for resolved dns connection.
-  MockQuicData quic_data2;
+  MockQuicData quic_data2(version_);
   quic_data2.AddConnect(SYNCHRONOUS, ERR_ADDRESS_IN_USE);
   quic_data2.AddSocketDataToFactory(socket_factory_.get());
 
@@ -10962,7 +10962,7 @@
   cache->Invalidate();
 
   // Socket data for stale connection which is supposed to disconnect.
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
   quic_data.AddWrite(
@@ -11015,7 +11015,7 @@
   // Expire the cache
   cache->Invalidate();
 
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
   quic_data.AddWrite(
@@ -11054,7 +11054,7 @@
   host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
                                             kNonCachedIPAddress, "");
 
-  MockQuicData quic_data;
+  MockQuicData quic_data(version_);
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   quic_data.AddSocketDataToFactory(socket_factory_.get());
@@ -11103,7 +11103,7 @@
       factory_.get(),
       std::make_unique<QuicChromiumAlarmFactory>(task_runner.get(), &clock_));
 
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(ASYNC, client_maker_.MakeDummyCHLOPacket(1));
   socket_data.AddWrite(ASYNC, client_maker_.MakeDummyCHLOPacket(2));
@@ -11160,11 +11160,11 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   // Prepare to establish two QUIC sessions.
-  MockQuicData socket_data;
+  MockQuicData socket_data(version_);
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
-  MockQuicData socket_data2;
+  MockQuicData socket_data2(version_);
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data2.AddSocketDataToFactory(socket_factory_.get());
diff --git a/net/quic/quic_test_packet_printer.cc b/net/quic/quic_test_packet_printer.cc
new file mode 100644
index 0000000..6db02a56
--- /dev/null
+++ b/net/quic/quic_test_packet_printer.cc
@@ -0,0 +1,216 @@
+// 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 "net/quic/quic_test_packet_printer.h"
+
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+class QuicPacketPrinter : public QuicFramerVisitorInterface {
+ public:
+  explicit QuicPacketPrinter(QuicFramer* framer, std::ostream* output)
+      : framer_(framer), output_(output) {}
+
+  // QuicFramerVisitorInterface implementation.
+  void OnError(QuicFramer* framer) override {
+    *output_ << "OnError: " << QuicErrorCodeToString(framer->error())
+             << " detail: " << framer->detailed_error() << "\n";
+  }
+  bool OnProtocolVersionMismatch(ParsedQuicVersion received_version,
+                                 PacketHeaderFormat form) override {
+    framer_->set_version(received_version);
+    *output_ << "OnProtocolVersionMismatch: "
+             << ParsedQuicVersionToString(received_version) << "\n";
+    return true;
+  }
+  void OnPacket() override { *output_ << "OnPacket\n"; }
+  void OnPublicResetPacket(const QuicPublicResetPacket& packet) override {
+    *output_ << "OnPublicResetPacket\n";
+  }
+  void OnVersionNegotiationPacket(
+      const QuicVersionNegotiationPacket& packet) override {
+    *output_ << "OnVersionNegotiationPacket\n";
+  }
+  void OnRetryPacket(QuicConnectionId original_connection_id,
+                     QuicConnectionId new_connection_id,
+                     QuicStringPiece retry_token) override {
+    *output_ << "OnRetryPacket\n";
+  }
+  bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override {
+    *output_ << "OnUnauthenticatedPublicHeader: " << header << "\n";
+    return true;
+  }
+  bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override {
+    *output_ << "OnUnauthenticatedHeader: " << header;
+    return true;
+  }
+  void OnDecryptedPacket(EncryptionLevel level) override {
+    *output_ << "OnDecryptedPacket\n";
+  }
+  bool OnPacketHeader(const QuicPacketHeader& header) override {
+    *output_ << "OnPacketHeader\n";
+    return true;
+  }
+  void OnCoalescedPacket(const QuicEncryptedPacket& packet) override {
+    *output_ << "OnCoalescedPacket\n";
+  }
+  bool OnStreamFrame(const QuicStreamFrame& frame) override {
+    *output_ << "OnStreamFrame: " << frame;
+    *output_ << "         data: { "
+             << QuicTextUtils::HexEncode(frame.data_buffer, frame.data_length)
+             << " }\n";
+    return true;
+  }
+  bool OnCryptoFrame(const QuicCryptoFrame& frame) override {
+    *output_ << "OnCryptoFrame: " << frame;
+    *output_ << "         data: { "
+             << QuicTextUtils::HexEncode(frame.data_buffer, frame.data_length)
+             << " }\n";
+    return true;
+  }
+  bool OnAckFrameStart(QuicPacketNumber largest_acked,
+                       QuicTime::Delta /*ack_delay_time*/) override {
+    *output_ << "OnAckFrameStart, largest_acked: " << largest_acked;
+    return true;
+  }
+  bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override {
+    *output_ << "OnAckRange: [" << start << ", " << end << ")";
+    return true;
+  }
+  bool OnAckTimestamp(QuicPacketNumber packet_number,
+                      QuicTime timestamp) override {
+    *output_ << "OnAckTimestamp: [" << packet_number << ", "
+             << timestamp.ToDebuggingValue() << ")";
+    return true;
+  }
+  bool OnAckFrameEnd(QuicPacketNumber start) override {
+    *output_ << "OnAckFrameEnd, start: " << start;
+    return true;
+  }
+  bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override {
+    *output_ << "OnStopWaitingFrame: " << frame;
+    return true;
+  }
+  bool OnPaddingFrame(const QuicPaddingFrame& frame) override {
+    *output_ << "OnPaddingFrame: " << frame;
+    return true;
+  }
+  bool OnPingFrame(const QuicPingFrame& frame) override {
+    *output_ << "OnPingFrame\n";
+    return true;
+  }
+  bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override {
+    *output_ << "OnRstStreamFrame: " << frame;
+    return true;
+  }
+  bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override {
+    // The frame printout will indicate whether it's a Google QUIC
+    // CONNECTION_CLOSE, IETF QUIC CONNECTION_CLOSE/Transport, or IETF QUIC
+    // CONNECTION_CLOSE/Application frame.
+    *output_ << "OnConnectionCloseFrame: " << frame;
+    return true;
+  }
+  bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override {
+    *output_ << "OnNewConnectionIdFrame: " << frame;
+    return true;
+  }
+  bool OnRetireConnectionIdFrame(
+      const QuicRetireConnectionIdFrame& frame) override {
+    *output_ << "OnRetireConnectionIdFrame: " << frame;
+    return true;
+  }
+  bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override {
+    *output_ << "OnNewTokenFrame: " << frame;
+    return true;
+  }
+  bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override {
+    *output_ << "OnStopSendingFrame: " << frame;
+    return true;
+  }
+  bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override {
+    *output_ << "OnPathChallengeFrame: " << frame;
+    return true;
+  }
+  bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override {
+    *output_ << "OnPathResponseFrame: " << frame;
+    return true;
+  }
+  bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override {
+    *output_ << "OnGoAwayFrame: " << frame;
+    return true;
+  }
+  bool OnMaxStreamsFrame(const QuicMaxStreamsFrame& frame) override {
+    *output_ << "OnMaxStreamsFrame: " << frame;
+    return true;
+  }
+  bool OnStreamsBlockedFrame(const QuicStreamsBlockedFrame& frame) override {
+    *output_ << "OnStreamsBlockedFrame: " << frame;
+    return true;
+  }
+  bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override {
+    *output_ << "OnWindowUpdateFrame: " << frame;
+    return true;
+  }
+  bool OnBlockedFrame(const QuicBlockedFrame& frame) override {
+    *output_ << "OnBlockedFrame: " << frame;
+    return true;
+  }
+  bool OnMessageFrame(const QuicMessageFrame& frame) override {
+    *output_ << "OnMessageFrame: " << frame;
+    return true;
+  }
+  void OnPacketComplete() override { *output_ << "OnPacketComplete\n"; }
+  bool IsValidStatelessResetToken(QuicUint128 token) const override {
+    *output_ << "IsValidStatelessResetToken\n";
+    return false;
+  }
+  void OnAuthenticatedIetfStatelessResetPacket(
+      const QuicIetfStatelessResetPacket& packet) override {
+    *output_ << "OnAuthenticatedIetfStatelessResetPacket\n";
+  }
+
+ private:
+  QuicFramer* framer_;  // Unowned.
+  mutable std::ostream* output_;
+};
+
+}  // namespace quic
+
+namespace net {
+
+std::string QuicPacketPrinter::PrintWrite(const std::string& data) {
+  quic::ParsedQuicVersionVector versions = {version_};
+  // Fake a time since we're not actually generating acks.
+  quic::QuicTime start(quic::QuicTime::Zero());
+  // Construct a server framer as this will be processing packets from
+  // the client.
+  quic::QuicFramer framer(versions, start, quic::Perspective::IS_SERVER,
+                          quic::kQuicDefaultConnectionIdLength);
+  std::ostringstream stream;
+  quic::QuicPacketPrinter visitor(&framer, &stream);
+  framer.set_visitor(&visitor);
+
+  if (version_.KnowsWhichDecrypterToUse()) {
+    framer.InstallDecrypter(
+        quic::ENCRYPTION_FORWARD_SECURE,
+        std::make_unique<quic::NullDecrypter>(quic::Perspective::IS_SERVER));
+  } else {
+    framer.SetDecrypter(
+        quic::ENCRYPTION_FORWARD_SECURE,
+        std::make_unique<quic::NullDecrypter>(quic::Perspective::IS_SERVER));
+  }
+
+  quic::QuicEncryptedPacket encrypted(data.c_str(), data.length());
+  framer.ProcessPacket(encrypted);
+  return stream.str() + "\n\n";
+}
+
+}  // namespace net
diff --git a/net/quic/quic_test_packet_printer.h b/net/quic/quic_test_packet_printer.h
new file mode 100644
index 0000000..d1d1bd5
--- /dev/null
+++ b/net/quic/quic_test_packet_printer.h
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_TEST_PACKET_PRINTER_H_
+#define NET_QUIC_QUIC_TEST_PACKET_PRINTER_H_
+
+#include <string>
+
+#include "net/socket/socket_test_util.h"
+
+namespace net {
+
+class QuicPacketPrinter : public SocketDataPrinter {
+ public:
+  explicit QuicPacketPrinter(quic::ParsedQuicVersion version)
+      : version_(version) {}
+  QuicPacketPrinter(const QuicPacketPrinter&) = delete;
+  QuicPacketPrinter& operator=(const QuicPacketPrinter&) = delete;
+
+  ~QuicPacketPrinter() = default;
+
+  std::string PrintWrite(const std::string& data) override;
+
+ private:
+  quic::ParsedQuicVersion version_;
+};
+
+}  // namespace net
+
+#endif  // NET_QUIC_QUIC_TEST_PACKET_PRINTER_H_
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index bb05c92..71fe6593 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -220,7 +220,8 @@
   write_index_ = 0;
 }
 
-bool StaticSocketDataHelper::VerifyWriteData(const std::string& data) {
+bool StaticSocketDataHelper::VerifyWriteData(const std::string& data,
+                                             SocketDataPrinter* printer) {
   CHECK(!AllWriteDataConsumed());
   // Check that the actual data matches the expectations, skipping over any
   // pause events.
@@ -240,6 +241,12 @@
   EXPECT_TRUE(actual_data == expected_data)
       << "Actual write data:\n" << HexDump(data)
       << "Expected write data:\n" << HexDump(expected_data);
+  if (printer) {
+    EXPECT_TRUE(actual_data == expected_data)
+        << "Actual write data:\n"
+        << printer->PrintWrite(data) << "Expected write data:\n"
+        << printer->PrintWrite(expected_data);
+  }
   return expected_data == actual_data;
 }
 
@@ -295,7 +302,7 @@
 
   // Check that what we are writing matches the expectation.
   // Then give the mocked return value.
-  if (!helper_.VerifyWriteData(data))
+  if (!helper_.VerifyWriteData(data, printer_))
     return MockWriteResult(SYNCHRONOUS, ERR_UNEXPECTED);
 
   const MockWrite& next_write = helper_.AdvanceWrite();
@@ -487,7 +494,7 @@
   NET_TRACE(1, " *** ") << "next_write: " << next_write.sequence_number;
   CHECK_GE(next_write.sequence_number, sequence_number_);
 
-  if (!helper_.VerifyWriteData(data))
+  if (!helper_.VerifyWriteData(data, printer_))
     return MockWriteResult(SYNCHRONOUS, ERR_UNEXPECTED);
 
   if (next_write.sequence_number <= sequence_number_) {
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 05d4c51e..d4ae682 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -355,6 +355,15 @@
   virtual void OnDataProviderDestroyed() = 0;
 };
 
+class SocketDataPrinter {
+ public:
+  ~SocketDataPrinter() = default;
+
+  // Prints the write in |data| using some sort of protocol-specific
+  // format.
+  virtual std::string PrintWrite(const std::string& data) = 0;
+};
+
 // StaticSocketDataHelper manages a list of reads and writes.
 class StaticSocketDataHelper {
  public:
@@ -377,7 +386,7 @@
   // Returns true if |data| is valid data for the next write. In order
   // to support short writes, the next write may be longer than |data|
   // in which case this method will still return true.
-  bool VerifyWriteData(const std::string& data);
+  bool VerifyWriteData(const std::string& data, SocketDataPrinter* printer);
 
   size_t read_index() const { return read_index_; }
   size_t write_index() const { return write_index_; }
@@ -424,11 +433,14 @@
   size_t read_count() const { return helper_.read_count(); }
   size_t write_count() const { return helper_.write_count(); }
 
+  void set_printer(SocketDataPrinter* printer) { printer_ = printer; }
+
  private:
   // From SocketDataProvider:
   void Reset() override;
 
   StaticSocketDataHelper helper_;
+  SocketDataPrinter* printer_ = nullptr;
   bool paused_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(StaticSocketDataProvider);
@@ -542,6 +554,8 @@
     busy_before_sync_reads_ = busy_before_sync_reads;
   }
 
+  void set_printer(SocketDataPrinter* printer) { printer_ = printer; }
+
  private:
   // Defines the state for the read or write path.
   enum IoState {
@@ -562,6 +576,7 @@
   void MaybePostWriteCompleteTask();
 
   StaticSocketDataHelper helper_;
+  SocketDataPrinter* printer_ = nullptr;
   int sequence_number_;
   IoState read_state_;
   IoState write_state_;
diff --git a/net/websockets/websocket_channel.cc b/net/websockets/websocket_channel.cc
index d9224258..d3fe375 100644
--- a/net/websockets/websocket_channel.cc
+++ b/net/websockets/websocket_channel.cc
@@ -571,7 +571,7 @@
   // TODO(ricea): Get flow control information from the WebSocketStream once we
   // have a multiplexing WebSocketStream.
   current_send_quota_ = send_quota_high_water_mark_;
-  event_interface_->OnFlowControl(send_quota_high_water_mark_);
+  event_interface_->OnSendFlowControlQuotaAdded(send_quota_high_water_mark_);
 
   // |stream_request_| is not used once the connection has succeeded.
   stream_request_.reset();
@@ -669,7 +669,7 @@
           // server, if the protocol in use supports quota.
           int fresh_quota = send_quota_high_water_mark_ - current_send_quota_;
           current_send_quota_ += fresh_quota;
-          event_interface_->OnFlowControl(fresh_quota);
+          event_interface_->OnSendFlowControlQuotaAdded(fresh_quota);
           return CHANNEL_ALIVE;
         }
       }
diff --git a/net/websockets/websocket_channel_test.cc b/net/websockets/websocket_channel_test.cc
index 981a1c2..a55e266f 100644
--- a/net/websockets/websocket_channel_test.cc
+++ b/net/websockets/websocket_channel_test.cc
@@ -179,7 +179,7 @@
                void(bool,
                     WebSocketMessageType,
                     const std::vector<char>&));           // NOLINT
-  MOCK_METHOD1(OnFlowControl, void(int64_t));             // NOLINT
+  MOCK_METHOD1(OnSendFlowControlQuotaAdded, void(int64_t));  // NOLINT
   MOCK_METHOD0(OnClosingHandshake, void(void));           // NOLINT
   MOCK_METHOD1(OnFailChannel, void(const std::string&));  // NOLINT
   MOCK_METHOD3(OnDropChannel,
@@ -235,7 +235,7 @@
                    WebSocketMessageType type,
                    scoped_refptr<IOBuffer> data,
                    size_t data_size) override {}
-  void OnFlowControl(int64_t quota) override {}
+  void OnSendFlowControlQuotaAdded(int64_t quota) override {}
   void OnClosingHandshake() override {}
   void OnFailChannel(const std::string& message) override {}
   void OnDropChannel(bool was_clean,
@@ -926,7 +926,7 @@
     // whether these methods are called or not.
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _))
         .Times(AnyNumber());
-    EXPECT_CALL(*event_interface_, OnFlowControl(_))
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_))
         .Times(AnyNumber());
   }
 };
@@ -990,9 +990,9 @@
 TEST_F(WebSocketChannelEventInterfaceTest, ConnectSuccessReported) {
   // false means success.
   EXPECT_CALL(*event_interface_, OnAddChannelResponse("", ""));
-  // OnFlowControl is always called immediately after connect to provide initial
-  // quota to the renderer.
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  // OnSendFlowControlQuotaAdded is always called immediately after connect to
+  // provide initial quota to the renderer.
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
 
   CreateChannelAndConnect();
 
@@ -1015,7 +1015,7 @@
 
 TEST_F(WebSocketChannelEventInterfaceTest, ProtocolPassed) {
   EXPECT_CALL(*event_interface_, OnAddChannelResponse("Bob", ""));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
 
   CreateChannelAndConnect();
 
@@ -1026,7 +1026,7 @@
 TEST_F(WebSocketChannelEventInterfaceTest, ExtensionsPassed) {
   EXPECT_CALL(*event_interface_,
               OnAddChannelResponse("", "extension1, extension2"));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
 
   CreateChannelAndConnect();
 
@@ -1046,7 +1046,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(*event_interface_,
                 OnDataFrameVector(true, WebSocketFrameHeader::kOpCodeText,
                                   AsVector("HELLO")));
@@ -1069,7 +1069,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(*event_interface_, OnClosingHandshake());
     EXPECT_CALL(
         *event_interface_,
@@ -1090,7 +1090,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(*event_interface_,
                 OnDropChannel(false, kWebSocketErrorAbnormalClosure, _));
   }
@@ -1110,7 +1110,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(checkpoint, Call(1));
     EXPECT_CALL(*event_interface_,
                 OnDataFrameVector(true, WebSocketFrameHeader::kOpCodeText,
@@ -1138,7 +1138,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(*event_interface_,
                 OnDataFrameVector(true, WebSocketFrameHeader::kOpCodeText,
                                   AsVector("HELLO")));
@@ -1177,7 +1177,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(*event_interface_,
                 OnDataFrameVector(false, WebSocketFrameHeader::kOpCodeText,
                                   AsVector("THREE")));
@@ -1211,7 +1211,7 @@
   stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
   set_stream(std::move(stream));
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
   EXPECT_CALL(
       *event_interface_,
       OnDataFrameVector(true, WebSocketFrameHeader::kOpCodeText, AsVector("")));
@@ -1227,7 +1227,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(*event_interface_,
                 OnDropChannel(false, kWebSocketErrorAbnormalClosure, _));
   }
@@ -1245,7 +1245,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(*event_interface_,
                 OnDropChannel(false, kWebSocketErrorAbnormalClosure, _));
   }
@@ -1265,7 +1265,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(
         *event_interface_,
         OnFailChannel(
@@ -1287,7 +1287,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(*event_interface_,
                 OnFailChannel("Unrecognized frame opcode: 4"));
   }
@@ -1317,7 +1317,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(*event_interface_,
                 OnDataFrameVector(false, WebSocketFrameHeader::kOpCodeText,
                                   AsVector("SPLIT ")));
@@ -1340,7 +1340,7 @@
   stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames);
   set_stream(std::move(stream));
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
 
   CreateChannelAndConnectSuccessfully();
   base::RunLoop().RunUntilIdle();
@@ -1359,7 +1359,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(
         *event_interface_,
         OnFailChannel(
@@ -1377,7 +1377,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
   }
 
   CreateChannelAndConnectSuccessfully();
@@ -1395,9 +1395,9 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(checkpoint, Call(1));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(checkpoint, Call(2));
   }
 
@@ -1416,13 +1416,13 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(checkpoint, Call(1));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(checkpoint, Call(2));
     // If quota was not really refreshed, we would get an OnDropChannel()
     // message.
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(checkpoint, Call(3));
   }
 
@@ -1446,7 +1446,8 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(kDefaultInitialQuota));
+    EXPECT_CALL(*event_interface_,
+                OnSendFlowControlQuotaAdded(kDefaultInitialQuota));
     EXPECT_CALL(*event_interface_, OnFailChannel("Send quota exceeded"));
   }
 
@@ -1463,7 +1464,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(checkpoint, Call(1));
     EXPECT_CALL(*event_interface_,
                 OnDropChannel(false, kWebSocketErrorAbnormalClosure, _));
@@ -1484,7 +1485,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(*event_interface_,
                 OnDropChannel(true, kWebSocketNormalClosure, "Fred"));
   }
@@ -1512,7 +1513,7 @@
 TEST_F(WebSocketChannelEventInterfaceTest, OnDropChannelCalledOnce) {
   set_stream(std::make_unique<ResetOnWriteFakeWebSocketStream>());
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
 
   EXPECT_CALL(*event_interface_,
               OnDropChannel(false, kWebSocketErrorAbnormalClosure, ""))
@@ -1536,7 +1537,7 @@
                                  ERR_CONNECTION_CLOSED);
   set_stream(std::move(stream));
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
   EXPECT_CALL(*event_interface_, OnClosingHandshake());
   EXPECT_CALL(*event_interface_,
               OnDropChannel(true, kWebSocketErrorNoStatusReceived, _));
@@ -1555,7 +1556,7 @@
                                  ERR_CONNECTION_CLOSED);
   set_stream(std::move(stream));
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
   EXPECT_CALL(*event_interface_, OnClosingHandshake());
   EXPECT_CALL(*event_interface_,
               OnDropChannel(true, kWebSocketErrorNoStatusReceived, _));
@@ -1571,7 +1572,7 @@
                                  ERR_WS_PROTOCOL_ERROR);
   set_stream(std::move(stream));
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
 
   EXPECT_CALL(*event_interface_, OnFailChannel("Invalid frame header"));
 
@@ -1585,7 +1586,7 @@
                                  ERR_WS_PROTOCOL_ERROR);
   set_stream(std::move(stream));
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
 
   EXPECT_CALL(*event_interface_, OnFailChannel("Invalid frame header"));
 
@@ -1597,7 +1598,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(*event_interface_, OnStartOpeningHandshakeCalled());
   }
 
@@ -1615,7 +1616,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(*event_interface_, OnFinishOpeningHandshakeCalled());
   }
 
@@ -1668,7 +1669,7 @@
   stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
   set_stream(std::move(stream));
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
 
   {
     InSequence s;
@@ -1689,7 +1690,7 @@
   stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
   set_stream(std::move(stream));
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
   EXPECT_CALL(
       *event_interface_,
       OnFailChannel(
@@ -1708,7 +1709,7 @@
   stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
   set_stream(std::move(stream));
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
   EXPECT_CALL(
       *event_interface_,
       OnFailChannel(
@@ -1727,7 +1728,7 @@
   stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
   set_stream(std::move(stream));
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
   EXPECT_CALL(
       *event_interface_,
       OnFailChannel(
@@ -1752,7 +1753,7 @@
                                std::move(raw_frames));
   set_stream(std::move(stream));
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
   EXPECT_CALL(*event_interface_,
               OnFailChannel(
                   "One or more reserved bits are on: reserved1 = 1, "
@@ -1770,7 +1771,7 @@
                                  ERR_IO_PENDING);
   set_stream(std::move(stream));
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
   // This checkpoint object verifies that the OnDropChannel message comes after
   // the timeout.
   Checkpoint checkpoint;
@@ -1807,7 +1808,7 @@
   stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames);
   set_stream(std::move(stream));
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
   Checkpoint checkpoint;
   TestClosure completion;
   {
@@ -1957,7 +1958,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(*event_interface_,
                 OnDataFrameVector(false, WebSocketFrameHeader::kOpCodeText,
                                   AsVector("FO")));
@@ -1988,7 +1989,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(checkpoint, Call(1));
     EXPECT_CALL(*event_interface_,
                 OnDataFrameVector(false, WebSocketFrameHeader::kOpCodeText,
@@ -2033,7 +2034,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(*event_interface_,
                 OnDataFrameVector(false, WebSocketFrameHeader::kOpCodeText,
                                   AsVector("FIRST FRAME IS")));
@@ -2074,7 +2075,7 @@
   {
     InSequence s;
     EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-    EXPECT_CALL(*event_interface_, OnFlowControl(_));
+    EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
     EXPECT_CALL(*event_interface_,
                 OnDataFrameVector(false, WebSocketFrameHeader::kOpCodeText,
                                   AsVector("FIRST ")));
@@ -2112,7 +2113,7 @@
   Checkpoint checkpoint;
   InSequence s;
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
   EXPECT_CALL(*event_interface_,
               OnDataFrameVector(false, WebSocketFrameHeader::kOpCodeText,
                                 AsVector("FIRST ")));
@@ -2160,7 +2161,7 @@
   Checkpoint checkpoint;
   InSequence s;
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
   EXPECT_CALL(*event_interface_,
               OnDataFrameVector(false, WebSocketFrameHeader::kOpCodeText,
                                 AsVector("FIRST ")));
@@ -2592,7 +2593,7 @@
                                std::move(frames));
   set_stream(std::move(stream));
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(_));
+  EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
   EXPECT_CALL(
       *event_interface_,
       OnDataFrameVector(
@@ -2708,7 +2709,8 @@
   set_stream(std::move(stream));
 
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(kDefaultInitialQuota));
+  EXPECT_CALL(*event_interface_,
+              OnSendFlowControlQuotaAdded(kDefaultInitialQuota));
   EXPECT_CALL(*event_interface_,
               OnFailChannel("Could not decode a text frame as UTF-8."));
 
@@ -2899,7 +2901,8 @@
   set_stream(std::move(stream));
 
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(kDefaultInitialQuota));
+  EXPECT_CALL(*event_interface_,
+              OnSendFlowControlQuotaAdded(kDefaultInitialQuota));
   EXPECT_CALL(*event_interface_,
               OnDataFrameVector(false, WebSocketFrameHeader::kOpCodeBinary,
                                 AsVector("frame1")));
@@ -2921,7 +2924,8 @@
   set_stream(std::move(stream));
 
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(kDefaultInitialQuota));
+  EXPECT_CALL(*event_interface_,
+              OnSendFlowControlQuotaAdded(kDefaultInitialQuota));
   EXPECT_CALL(*event_interface_,
               OnFailChannel("Received unexpected continuation frame."));
 
@@ -2941,7 +2945,8 @@
   set_stream(std::move(stream));
 
   EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _));
-  EXPECT_CALL(*event_interface_, OnFlowControl(kDefaultInitialQuota));
+  EXPECT_CALL(*event_interface_,
+              OnSendFlowControlQuotaAdded(kDefaultInitialQuota));
   EXPECT_CALL(
       *event_interface_,
       OnDataFrameVector(true, WebSocketFrameHeader::kOpCodeText, AsVector("")));
diff --git a/net/websockets/websocket_end_to_end_test.cc b/net/websockets/websocket_end_to_end_test.cc
index 7edcbb0..3082311 100644
--- a/net/websockets/websocket_end_to_end_test.cc
+++ b/net/websockets/websocket_end_to_end_test.cc
@@ -104,7 +104,7 @@
                    scoped_refptr<IOBuffer> data,
                    size_t data_size) override;
 
-  void OnFlowControl(int64_t quota) override;
+  void OnSendFlowControlQuotaAdded(int64_t quota) override;
 
   void OnClosingHandshake() override;
 
@@ -178,7 +178,7 @@
                                                scoped_refptr<IOBuffer> data,
                                                size_t data_size) {}
 
-void ConnectTestingEventInterface::OnFlowControl(int64_t quota) {}
+void ConnectTestingEventInterface::OnSendFlowControlQuotaAdded(int64_t quota) {}
 
 void ConnectTestingEventInterface::OnClosingHandshake() {}
 
diff --git a/net/websockets/websocket_event_interface.h b/net/websockets/websocket_event_interface.h
index 0edf249..d154ea69 100644
--- a/net/websockets/websocket_event_interface.h
+++ b/net/websockets/websocket_event_interface.h
@@ -56,9 +56,8 @@
                            size_t buffer_size) = 0;
 
   // Called to provide more send quota for this channel to the renderer
-  // process. Currently the quota units are always bytes of message body
-  // data. In future it might depend on the type of multiplexing in use.
-  virtual void OnFlowControl(int64_t quota) = 0;
+  // process.
+  virtual void OnSendFlowControlQuotaAdded(int64_t quota) = 0;
 
   // Called when the remote server has Started the WebSocket Closing
   // Handshake. The client should not attempt to send any more messages after
diff --git a/net/websockets/websocket_stream.cc b/net/websockets/websocket_stream.cc
index 1ed8337..fb78883 100644
--- a/net/websockets/websocket_stream.cc
+++ b/net/websockets/websocket_stream.cc
@@ -123,7 +123,6 @@
                                             &delegate_,
                                             kTrafficAnnotation)),
         connect_delegate_(std::move(connect_delegate)),
-        perform_upgrade_has_been_called_(false),
         api_delegate_(std::move(api_delegate)) {
     HttpRequestHeaders headers = additional_headers;
     headers.SetHeader(websockets::kUpgrade, websockets::kWebSocketLowercase);
@@ -189,17 +188,11 @@
 
   void PerformUpgrade() {
     DCHECK(timer_);
-    CHECK(!perform_upgrade_has_been_called_);
-    // TODO(bnc): Change to DCHECK after https://crbug.com/850183 is fixed.
-    CHECK(connect_delegate_);
-
-    perform_upgrade_has_been_called_ = true;
+    DCHECK(connect_delegate_);
 
     timer_->Stop();
 
     if (!handshake_stream_) {
-      // TODO(https://crbug.com/850183):
-      // Find out why this can happen and make it stop.
       ReportFailureWithMessage(
           "No handshake stream has been created "
           "or handshake stream is already destroyed.");
@@ -209,9 +202,7 @@
     std::unique_ptr<URLRequest> url_request = std::move(url_request_);
     WebSocketHandshakeStreamBase* handshake_stream = handshake_stream_.get();
     handshake_stream_.reset();
-    // TODO(bnc): Combine into one line after https://crbug.com/850183 is fixed.
-    std::unique_ptr<WebSocketStream> stream = handshake_stream->Upgrade();
-    connect_delegate_->OnSuccess(std::move(stream));
+    connect_delegate_->OnSuccess(handshake_stream->Upgrade());
 
     // This is safe even if |this| has already been deleted.
     url_request->CancelWithError(ERR_WS_UPGRADE);
@@ -275,8 +266,7 @@
  private:
   void OnHandshakeStreamCreated(
       WebSocketHandshakeStreamBase* handshake_stream) {
-    // TODO(bnc): Change to DCHECK after https://crbug.com/850183 is fixed.
-    CHECK(handshake_stream);
+    DCHECK(handshake_stream);
 
     handshake_stream_ = handshake_stream->GetWeakPtr();
   }
@@ -299,9 +289,6 @@
   // succeeded.
   base::WeakPtr<WebSocketHandshakeStreamBase> handshake_stream_;
 
-  // TODO(bnc): Remove after https://crbug.com/850183 is fixed.
-  bool perform_upgrade_has_been_called_;
-
   // The failure message supplied by WebSocketBasicHandshakeStream, if any.
   std::string failure_message_;
 
diff --git a/ppapi/api/dev/pp_cursor_type_dev.idl b/ppapi/api/dev/pp_cursor_type_dev.idl
index 08abec3b..6d02bc73 100644
--- a/ppapi/api/dev/pp_cursor_type_dev.idl
+++ b/ppapi/api/dev/pp_cursor_type_dev.idl
@@ -51,6 +51,8 @@
   PP_CURSORTYPE_ZOOMIN = 39,
   PP_CURSORTYPE_ZOOMOUT = 40,
   PP_CURSORTYPE_GRAB = 41,
-  PP_CURSORTYPE_GRABBING = 42
+  PP_CURSORTYPE_GRABBING = 42,
+  PP_CURSORTYPE_MIDDLEPANNINGVERTICAL = 43,
+  PP_CURSORTYPE_MIDDLEPANNINGHORIZONTAL = 44
 };
 
diff --git a/ppapi/api/ppb_mouse_cursor.idl b/ppapi/api/ppb_mouse_cursor.idl
index 9fba254..2dd7681 100644
--- a/ppapi/api/ppb_mouse_cursor.idl
+++ b/ppapi/api/ppb_mouse_cursor.idl
@@ -63,7 +63,9 @@
   PP_MOUSECURSOR_TYPE_ZOOMIN = 39,
   PP_MOUSECURSOR_TYPE_ZOOMOUT = 40,
   PP_MOUSECURSOR_TYPE_GRAB = 41,
-  PP_MOUSECURSOR_TYPE_GRABBING = 42
+  PP_MOUSECURSOR_TYPE_GRABBING = 42,
+  PP_MOUSECURSOR_TYPE_MIDDLEPANNINGVERTICAL = 43,
+  PP_MOUSECURSOR_TYPE_MIDDLEPANNINGHORIZONTAL = 44
 };
 
 /**
diff --git a/ppapi/c/dev/pp_cursor_type_dev.h b/ppapi/c/dev/pp_cursor_type_dev.h
index 82967a7..68ca41c 100644
--- a/ppapi/c/dev/pp_cursor_type_dev.h
+++ b/ppapi/c/dev/pp_cursor_type_dev.h
@@ -64,7 +64,9 @@
   PP_CURSORTYPE_ZOOMIN = 39,
   PP_CURSORTYPE_ZOOMOUT = 40,
   PP_CURSORTYPE_GRAB = 41,
-  PP_CURSORTYPE_GRABBING = 42
+  PP_CURSORTYPE_GRABBING = 42,
+  PP_CURSORTYPE_MIDDLEPANNINGVERTICAL = 43,
+  PP_CURSORTYPE_MIDDLEPANNINGHORIZONTAL = 44
 };
 PP_COMPILE_ASSERT_ENUM_SIZE_IN_BYTES(PP_CursorType_Dev, 4);
 /**
diff --git a/ppapi/c/ppb_mouse_cursor.h b/ppapi/c/ppb_mouse_cursor.h
index bc6a5f5..e07bea3 100644
--- a/ppapi/c/ppb_mouse_cursor.h
+++ b/ppapi/c/ppb_mouse_cursor.h
@@ -77,7 +77,9 @@
   PP_MOUSECURSOR_TYPE_ZOOMIN = 39,
   PP_MOUSECURSOR_TYPE_ZOOMOUT = 40,
   PP_MOUSECURSOR_TYPE_GRAB = 41,
-  PP_MOUSECURSOR_TYPE_GRABBING = 42
+  PP_MOUSECURSOR_TYPE_GRABBING = 42,
+  PP_MOUSECURSOR_TYPE_MIDDLEPANNINGVERTICAL = 43,
+  PP_MOUSECURSOR_TYPE_MIDDLEPANNINGHORIZONTAL = 44
 };
 PP_COMPILE_ASSERT_ENUM_SIZE_IN_BYTES(PP_MouseCursor_Type, 4);
 /**
diff --git a/ppapi/nacl_irt/ppapi_dispatcher.cc b/ppapi/nacl_irt/ppapi_dispatcher.cc
index f70ca06..3bf22ff 100644
--- a/ppapi/nacl_irt/ppapi_dispatcher.cc
+++ b/ppapi/nacl_irt/ppapi_dispatcher.cc
@@ -16,7 +16,6 @@
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/waitable_event.h"
 #include "build/build_config.h"
-#include "components/tracing/child/child_trace_message_filter.h"
 #include "ipc/ipc_channel_handle.h"
 #include "ipc/ipc_logging.h"
 #include "ipc/ipc_message.h"
@@ -55,7 +54,6 @@
   channel_->AddFilter(plugin_filter.get());
   globals->RegisterResourceMessageFilters(plugin_filter.get());
 
-  channel_->AddFilter(new tracing::ChildTraceMessageFilter(task_runner_.get()));
   channel_->Init(browser_ipc_handle, IPC::Channel::MODE_SERVER, true);
 }
 
diff --git a/remoting/host/installer/mac/Scripts/remoting_postflight.sh b/remoting/host/installer/mac/Scripts/remoting_postflight.sh
index e28bdf6..ede1c04 100755
--- a/remoting/host/installer/mac/Scripts/remoting_postflight.sh
+++ b/remoting/host/installer/mac/Scripts/remoting_postflight.sh
@@ -93,7 +93,7 @@
 fi
 
 # Run the config-upgrade tool.
-"$HOST_EXE" --upgrade-token --host-config="$CONFIG_FILE"
+"$HOST_EXE" --upgrade-token --host-config="$CONFIG_FILE" || true
 
 # Load the service for each user for whom the service was unloaded in the
 # preflight script (this includes the root user, in case only the login screen
diff --git a/services/audio/output_controller_unittest.cc b/services/audio/output_controller_unittest.cc
index f3da456..d837b60 100644
--- a/services/audio/output_controller_unittest.cc
+++ b/services/audio/output_controller_unittest.cc
@@ -22,6 +22,7 @@
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_piece.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/test_message_loop.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
@@ -32,7 +33,6 @@
 #include "media/audio/test_audio_thread.h"
 #include "media/base/audio_bus.h"
 #include "media/base/audio_parameters.h"
-#include "media/base/gmock_callback_support.h"
 #include "services/audio/loopback_group_member.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -49,8 +49,9 @@
 using media::AudioManager;
 using media::AudioOutputStream;
 using media::AudioParameters;
-using media::RunClosure;
-using media::RunOnceClosure;
+
+using base::test::RunClosure;
+using base::test::RunOnceClosure;
 
 namespace audio {
 namespace {
diff --git a/services/data_decoder/OWNERS b/services/data_decoder/OWNERS
index 78112f0..cd1f44c 100644
--- a/services/data_decoder/OWNERS
+++ b/services/data_decoder/OWNERS
@@ -1,3 +1,2 @@
-
-jcivelli@chromium.org
+palmer@chromium.org
 rsesek@chromium.org
diff --git a/services/device/generic_sensor/platform_sensor.cc b/services/device/generic_sensor/platform_sensor.cc
index 09b91426..0a749da0 100644
--- a/services/device/generic_sensor/platform_sensor.cc
+++ b/services/device/generic_sensor/platform_sensor.cc
@@ -102,12 +102,7 @@
 }
 
 bool PlatformSensor::GetLatestReading(SensorReading* result) {
-  if (!shared_buffer_reader_) {
-    shared_buffer_reader_ =
-        std::make_unique<SensorReadingSharedBufferReader>(reading_buffer_);
-  }
-
-  return shared_buffer_reader_->GetReading(result);
+  return SensorReadingSharedBufferReader::GetReading(reading_buffer_, result);
 }
 
 void PlatformSensor::UpdateSharedBufferAndNotifyClients(
diff --git a/services/device/generic_sensor/platform_sensor.h b/services/device/generic_sensor/platform_sensor.h
index 600d6072..c45f6a5f 100644
--- a/services/device/generic_sensor/platform_sensor.h
+++ b/services/device/generic_sensor/platform_sensor.h
@@ -22,7 +22,6 @@
 
 class PlatformSensorProvider;
 class PlatformSensorConfiguration;
-class SensorReadingSharedBufferReader;
 
 // Base class for the sensors provided by the platform. Concrete instances of
 // this class are created by platform specific PlatformSensorProvider.
@@ -107,7 +106,6 @@
  private:
   friend class base::RefCountedThreadSafe<PlatformSensor>;
   SensorReadingSharedBuffer* reading_buffer_;  // NOTE: Owned by |provider_|.
-  std::unique_ptr<SensorReadingSharedBufferReader> shared_buffer_reader_;
   mojom::SensorType type_;
   ConfigMap config_map_;
   PlatformSensorProvider* provider_;
diff --git a/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java b/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java
index a5c8c1c4..7911525 100644
--- a/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java
+++ b/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java
@@ -23,6 +23,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
+import org.chromium.device.mojom.NdefCompatibility;
 import org.chromium.device.mojom.NdefMessage;
 import org.chromium.device.mojom.Nfc;
 import org.chromium.device.mojom.NfcClient;
@@ -30,8 +31,7 @@
 import org.chromium.device.mojom.NfcErrorType;
 import org.chromium.device.mojom.NfcPushOptions;
 import org.chromium.device.mojom.NfcPushTarget;
-import org.chromium.device.mojom.NfcWatchMode;
-import org.chromium.device.mojom.NfcWatchOptions;
+import org.chromium.device.mojom.NfcReaderOptions;
 import org.chromium.mojo.bindings.Callbacks;
 import org.chromium.mojo.system.MojoException;
 
@@ -103,12 +103,12 @@
     private int mWatcherId;
 
     /**
-     * Map of watchId <-> NfcWatchOptions. All NfcWatchOptions are matched against tag that is in
+     * Map of watchId <-> NfcReaderOptions. All NfcReaderOptions are matched against tag that is in
      * proximity, when match algorithm (@see #matchesWatchOptions) returns true, watcher with
      * corresponding ID would be notified using NfcClient interface.
      * @see NfcClient#onWatch(int[] id, NdefMessage message)
      */
-    private final SparseArray<NfcWatchOptions> mWatchers = new SparseArray<>();
+    private final SparseArray<NfcReaderOptions> mWatchers = new SparseArray<>();
 
     /**
      * Handler that runs delayed push timeout task.
@@ -163,8 +163,8 @@
 
     /**
      * Sets NfcClient. NfcClient interface is used to notify mojo NFC service client when NFC
-     * device is in proximity and has NdefMessage that matches NfcWatchOptions criteria.
-     * @see Nfc#watch(NfcWatchOptions options, WatchResponse callback)
+     * device is in proximity and has NdefMessage that matches NfcReaderOptions criteria.
+     * @see Nfc#watch(NfcReaderOptions options, WatchResponse callback)
      *
      * @param client @see NfcClient
      */
@@ -238,15 +238,15 @@
     /**
      * Watch method allows to set filtering criteria for NdefMessages that are found when NFC device
      * is within proximity. On success, watch ID is returned to caller through WatchResponse
-     * callback. When NdefMessage that matches NfcWatchOptions is found, it is passed to NfcClient
+     * callback. When NdefMessage that matches NfcReaderOptions is found, it is passed to NfcClient
      * interface together with corresponding watch ID.
      * @see NfcClient#onWatch(int[] id, NdefMessage message)
      *
-     * @param options used to filter NdefMessages, @see NfcWatchOptions.
+     * @param options used to filter NdefMessages, @see NfcReaderOptions.
      * @param callback that is used to notify caller when watch() is completed and return watch ID.
      */
     @Override
-    public void watch(NfcWatchOptions options, WatchResponse callback) {
+    public void watch(NfcReaderOptions options, WatchResponse callback) {
         if (!checkIfReady(callback)) return;
         int watcherId = ++mWatcherId;
         mWatchers.put(watcherId, options);
@@ -544,20 +544,21 @@
             Log.w(TAG, "Cannot read data from NFC tag. IO_ERROR.");
         }
 
-        if (message != null) notifyMatchingWatchers(message);
+        if (message != null) notifyMatchingWatchers(message, mTagHandler.compatibility());
     }
 
     /**
-     * Iterates through active watchers and if any of those match NfcWatchOptions criteria,
+     * Iterates through active watchers and if any of those match NfcReaderOptions criteria,
      * delivers NdefMessage to the client.
      */
-    private void notifyMatchingWatchers(android.nfc.NdefMessage message) {
+    private void notifyMatchingWatchers(android.nfc.NdefMessage message, int compatibility) {
         try {
             NdefMessage ndefMessage = NfcTypeConverter.toNdefMessage(message);
             List<Integer> watchIds = new ArrayList<Integer>();
             for (int i = 0; i < mWatchers.size(); i++) {
-                NfcWatchOptions options = mWatchers.valueAt(i);
-                if (matchesWatchOptions(ndefMessage, options)) watchIds.add(mWatchers.keyAt(i));
+                NfcReaderOptions options = mWatchers.valueAt(i);
+                if (matchesWatchOptions(ndefMessage, compatibility, options))
+                    watchIds.add(mWatchers.keyAt(i));
             }
 
             if (watchIds.size() != 0) {
@@ -575,10 +576,12 @@
     /**
      * Implements matching algorithm.
      */
-    private boolean matchesWatchOptions(NdefMessage message, NfcWatchOptions options) {
-        // Valid WebNFC message must have non-empty url.
-        if (options.mode == NfcWatchMode.WEBNFC_ONLY
-                && (message.url == null || message.url.isEmpty())) {
+    private boolean matchesWatchOptions(
+            NdefMessage message, int compatibility, NfcReaderOptions options) {
+        // 'nfc-forum' option can only read messages from NFC standard devices and 'vendor' option
+        // can only read from vendor specific ones.
+        if (options.compatibility != NdefCompatibility.ANY
+                && options.compatibility != compatibility) {
             return false;
         }
 
diff --git a/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcTagHandler.java b/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcTagHandler.java
index 9e8b9c46..77f5167 100644
--- a/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcTagHandler.java
+++ b/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcTagHandler.java
@@ -12,12 +12,15 @@
 import android.nfc.tech.NdefFormatable;
 import android.nfc.tech.TagTechnology;
 
+import org.chromium.device.mojom.NdefCompatibility;
+
 import java.io.IOException;
 
 /**
  * Utility class that provides I/O operations for NFC tags.
  */
 public class NfcTagHandler {
+    private final int mCompatibility;
     private final TagTechnology mTech;
     private final TagTechnologyHandler mTechHandler;
     private boolean mWasConnected;
@@ -32,11 +35,20 @@
         if (tag == null) return null;
 
         Ndef ndef = Ndef.get(tag);
-        if (ndef != null) return new NfcTagHandler(ndef, new NdefHandler(ndef));
+        if (ndef != null) {
+            int compatibility = NdefCompatibility.VENDOR;
+            String type = ndef.getType();
+            if (type.equals(Ndef.NFC_FORUM_TYPE_1) || type.equals(Ndef.NFC_FORUM_TYPE_2)
+                    || type.equals(Ndef.NFC_FORUM_TYPE_3) || type.equals(Ndef.NFC_FORUM_TYPE_4)) {
+                compatibility = NdefCompatibility.NFC_FORUM;
+            }
+            return new NfcTagHandler(compatibility, ndef, new NdefHandler(ndef));
+        }
 
         NdefFormatable formattable = NdefFormatable.get(tag);
         if (formattable != null) {
-            return new NfcTagHandler(formattable, new NdefFormattableHandler(formattable));
+            return new NfcTagHandler(
+                    NdefCompatibility.VENDOR, formattable, new NdefFormattableHandler(formattable));
         }
 
         return null;
@@ -100,7 +112,8 @@
         }
     }
 
-    protected NfcTagHandler(TagTechnology tech, TagTechnologyHandler handler) {
+    protected NfcTagHandler(int compatibility, TagTechnology tech, TagTechnologyHandler handler) {
+        mCompatibility = compatibility;
         mTech = tech;
         mTechHandler = handler;
     }
@@ -154,4 +167,12 @@
         }
         return false;
     }
+
+    /**
+     * Returns NdefCompatibility.NFC_FORUM if the tag has a NFC standard type, otherwise returns
+     * NdefCompatibility.VENDOR.
+     */
+    public int compatibility() {
+        return mCompatibility;
+    }
 }
diff --git a/services/device/nfc/android/junit/src/org/chromium/device/nfc/NFCTest.java b/services/device/nfc/android/junit/src/org/chromium/device/nfc/NFCTest.java
index 5044c56..b154b84 100644
--- a/services/device/nfc/android/junit/src/org/chromium/device/nfc/NFCTest.java
+++ b/services/device/nfc/android/junit/src/org/chromium/device/nfc/NFCTest.java
@@ -42,6 +42,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.util.Feature;
+import org.chromium.device.mojom.NdefCompatibility;
 import org.chromium.device.mojom.NdefMessage;
 import org.chromium.device.mojom.NdefRecord;
 import org.chromium.device.mojom.NdefRecordType;
@@ -56,8 +57,7 @@
 import org.chromium.device.mojom.NfcErrorType;
 import org.chromium.device.mojom.NfcPushOptions;
 import org.chromium.device.mojom.NfcPushTarget;
-import org.chromium.device.mojom.NfcWatchMode;
-import org.chromium.device.mojom.NfcWatchOptions;
+import org.chromium.device.mojom.NfcReaderOptions;
 import org.chromium.testing.local.LocalRobolectricTestRunner;
 
 import java.io.IOException;
@@ -202,7 +202,7 @@
         TestNfcImpl nfc = new TestNfcImpl(mContext, mDelegate);
         mDelegate.invokeCallback();
         WatchResponse mockCallback = mock(WatchResponse.class);
-        nfc.watch(createNfcWatchOptions(), mockCallback);
+        nfc.watch(createNfcReaderOptions(), mockCallback);
         verify(mockCallback).call(anyInt(), mErrorCaptor.capture());
         assertNull(mErrorCaptor.getValue());
     }
@@ -385,7 +385,7 @@
         mDelegate.invokeCallback();
         nfc.setClient(mNfcClient);
         WatchResponse mockCallback = mock(WatchResponse.class);
-        nfc.watch(createNfcWatchOptions(), mockCallback);
+        nfc.watch(createNfcReaderOptions(), mockCallback);
         nfc.suspendNfcOperations();
         verify(mNfcAdapter, times(1)).disableReaderMode(mActivity);
         nfc.resumeNfcOperations();
@@ -452,7 +452,7 @@
         mDelegate.invokeCallback();
         nfc.setClient(mNfcClient);
         WatchResponse mockWatchCallback1 = mock(WatchResponse.class);
-        nfc.watch(createNfcWatchOptions(), mockWatchCallback1);
+        nfc.watch(createNfcReaderOptions(), mockWatchCallback1);
 
         // Check that watch requests were completed successfully.
         verify(mockWatchCallback1).call(mWatchCaptor.capture(), mErrorCaptor.capture());
@@ -460,7 +460,7 @@
         int watchId1 = mWatchCaptor.getValue().intValue();
 
         WatchResponse mockWatchCallback2 = mock(WatchResponse.class);
-        nfc.watch(createNfcWatchOptions(), mockWatchCallback2);
+        nfc.watch(createNfcReaderOptions(), mockWatchCallback2);
         verify(mockWatchCallback2).call(mWatchCaptor.capture(), mErrorCaptor.capture());
         assertNull(mErrorCaptor.getValue());
         int watchId2 = mWatchCaptor.getValue().intValue();
@@ -488,8 +488,8 @@
         nfc.setClient(mNfcClient);
 
         // Should match by WebNFC Id (exact match).
-        NfcWatchOptions options1 = createNfcWatchOptions();
-        options1.mode = NfcWatchMode.WEBNFC_ONLY;
+        NfcReaderOptions options1 = createNfcReaderOptions();
+        options1.compatibility = NdefCompatibility.NFC_FORUM;
         options1.url = TEST_URL;
         WatchResponse mockWatchCallback1 = mock(WatchResponse.class);
         nfc.watch(options1, mockWatchCallback1);
@@ -498,8 +498,8 @@
         int watchId1 = mWatchCaptor.getValue().intValue();
 
         // Should match by media type.
-        NfcWatchOptions options2 = createNfcWatchOptions();
-        options2.mode = NfcWatchMode.ANY;
+        NfcReaderOptions options2 = createNfcReaderOptions();
+        options2.compatibility = NdefCompatibility.ANY;
         options2.mediaType = TEXT_MIME;
         WatchResponse mockWatchCallback2 = mock(WatchResponse.class);
         nfc.watch(options2, mockWatchCallback2);
@@ -508,8 +508,8 @@
         int watchId2 = mWatchCaptor.getValue().intValue();
 
         // Should match by record type.
-        NfcWatchOptions options3 = createNfcWatchOptions();
-        options3.mode = NfcWatchMode.ANY;
+        NfcReaderOptions options3 = createNfcReaderOptions();
+        options3.compatibility = NdefCompatibility.ANY;
         NdefRecordTypeFilter typeFilter = new NdefRecordTypeFilter();
         typeFilter.recordType = NdefRecordType.URL;
         options3.recordFilter = typeFilter;
@@ -520,8 +520,8 @@
         int watchId3 = mWatchCaptor.getValue().intValue();
 
         // Should not match
-        NfcWatchOptions options4 = createNfcWatchOptions();
-        options4.mode = NfcWatchMode.WEBNFC_ONLY;
+        NfcReaderOptions options4 = createNfcReaderOptions();
+        options4.compatibility = NdefCompatibility.NFC_FORUM;
         options4.url = DOMAIN;
         WatchResponse mockWatchCallback4 = mock(WatchResponse.class);
         nfc.watch(options4, mockWatchCallback4);
@@ -549,7 +549,7 @@
         TestNfcImpl nfc = new TestNfcImpl(mContext, mDelegate);
         mDelegate.invokeCallback();
         WatchResponse mockWatchCallback = mock(WatchResponse.class);
-        nfc.watch(createNfcWatchOptions(), mockWatchCallback);
+        nfc.watch(createNfcReaderOptions(), mockWatchCallback);
 
         verify(mockWatchCallback).call(mWatchCaptor.capture(), mErrorCaptor.capture());
         assertNull(mErrorCaptor.getValue());
@@ -576,11 +576,11 @@
         mDelegate.invokeCallback();
         WatchResponse mockWatchCallback1 = mock(WatchResponse.class);
         WatchResponse mockWatchCallback2 = mock(WatchResponse.class);
-        nfc.watch(createNfcWatchOptions(), mockWatchCallback1);
+        nfc.watch(createNfcReaderOptions(), mockWatchCallback1);
         verify(mockWatchCallback1).call(mWatchCaptor.capture(), mErrorCaptor.capture());
         assertNull(mErrorCaptor.getValue());
 
-        nfc.watch(createNfcWatchOptions(), mockWatchCallback2);
+        nfc.watch(createNfcReaderOptions(), mockWatchCallback2);
         verify(mockWatchCallback2).call(mWatchCaptor.capture(), mErrorCaptor.capture());
         assertNull(mErrorCaptor.getValue());
 
@@ -601,7 +601,7 @@
         TestNfcImpl nfc = new TestNfcImpl(mContext, mDelegate);
         mDelegate.invokeCallback();
         WatchResponse mockWatchCallback = mock(WatchResponse.class);
-        nfc.watch(createNfcWatchOptions(), mockWatchCallback);
+        nfc.watch(createNfcReaderOptions(), mockWatchCallback);
 
         verify(mockWatchCallback).call(mWatchCaptor.capture(), mErrorCaptor.capture());
         assertNull(mErrorCaptor.getValue());
@@ -639,7 +639,7 @@
         mDelegate.invokeCallback();
         nfc.setClient(mNfcClient);
         WatchResponse mockWatchCallback = mock(WatchResponse.class);
-        nfc.watch(createNfcWatchOptions(), mockWatchCallback);
+        nfc.watch(createNfcReaderOptions(), mockWatchCallback);
 
         // Force read operation to fail
         doThrow(IllegalStateException.class).when(mNfcTagHandler).read();
@@ -849,7 +849,7 @@
         TestNfcImpl nfc = new TestNfcImpl(mContext, mDelegate);
         mDelegate.invokeCallback();
         WatchResponse mockWatchCallback = mock(WatchResponse.class);
-        nfc.watch(createNfcWatchOptions(), mockWatchCallback);
+        nfc.watch(createNfcReaderOptions(), mockWatchCallback);
 
         PushResponse mockPushCallback = mock(PushResponse.class);
         // Should be cancelled with TIMER_EXPIRED.
@@ -907,6 +907,124 @@
     }
 
     /**
+     * Test that 'nfc-forum' tag messages can only be read by Nfc.watch() with 'nfc-forum' or 'any'
+     * option.
+     */
+    @Test
+    @Feature({"NFCTest"})
+    public void testWatchCompatibilityMatchingNfcForumTag() {
+        TestNfcImpl nfc = new TestNfcImpl(mContext, mDelegate);
+        mDelegate.invokeCallback();
+        nfc.setClient(mNfcClient);
+
+        // Should match.
+        {
+            NfcReaderOptions options = createNfcReaderOptions();
+            options.compatibility = NdefCompatibility.NFC_FORUM;
+            options.url = TEST_URL;
+            WatchResponse mockWatchCallback = mock(WatchResponse.class);
+            nfc.watch(options, mockWatchCallback);
+            verify(mockWatchCallback).call(mWatchCaptor.capture(), mErrorCaptor.capture());
+            assertNull(mErrorCaptor.getValue());
+        }
+        int watchId1 = mWatchCaptor.getValue().intValue();
+
+        // Should not match.
+        {
+            NfcReaderOptions options = createNfcReaderOptions();
+            options.compatibility = NdefCompatibility.VENDOR;
+            options.url = TEST_URL;
+            WatchResponse mockWatchCallback = mock(WatchResponse.class);
+            nfc.watch(options, mockWatchCallback);
+            verify(mockWatchCallback).call(mWatchCaptor.capture(), mErrorCaptor.capture());
+            assertNull(mErrorCaptor.getValue());
+        }
+        int watchId2 = mWatchCaptor.getValue().intValue();
+
+        // Should match.
+        {
+            NfcReaderOptions options = createNfcReaderOptions();
+            options.compatibility = NdefCompatibility.ANY;
+            options.url = TEST_URL;
+            WatchResponse mockWatchCallback = mock(WatchResponse.class);
+            nfc.watch(options, mockWatchCallback);
+            verify(mockWatchCallback).call(mWatchCaptor.capture(), mErrorCaptor.capture());
+            assertNull(mErrorCaptor.getValue());
+        }
+        int watchId3 = mWatchCaptor.getValue().intValue();
+
+        doReturn(NdefCompatibility.NFC_FORUM).when(mNfcTagHandler).compatibility();
+        nfc.processPendingOperationsForTesting(mNfcTagHandler);
+
+        // Check that client was notified and watch with 'nfc-forum' or 'any' compatibility was
+        // triggered.
+        verify(mNfcClient, times(1))
+                .onWatch(mOnWatchCallbackCaptor.capture(), any(NdefMessage.class));
+        assertEquals(2, mOnWatchCallbackCaptor.getValue().length);
+        assertEquals(watchId1, mOnWatchCallbackCaptor.getValue()[0]);
+        assertEquals(watchId3, mOnWatchCallbackCaptor.getValue()[1]);
+    }
+
+    /**
+     * Test that 'vendor' tag messages can only be read by Nfc.watch() with 'vendor' or 'any'
+     * option.
+     */
+    @Test
+    @Feature({"NFCTest"})
+    public void testWatchCompatibilityMatchingVendorTag() {
+        TestNfcImpl nfc = new TestNfcImpl(mContext, mDelegate);
+        mDelegate.invokeCallback();
+        nfc.setClient(mNfcClient);
+
+        // Should not match.
+        {
+            NfcReaderOptions options = createNfcReaderOptions();
+            options.compatibility = NdefCompatibility.NFC_FORUM;
+            options.url = TEST_URL;
+            WatchResponse mockWatchCallback = mock(WatchResponse.class);
+            nfc.watch(options, mockWatchCallback);
+            verify(mockWatchCallback).call(mWatchCaptor.capture(), mErrorCaptor.capture());
+            assertNull(mErrorCaptor.getValue());
+        }
+        int watchId1 = mWatchCaptor.getValue().intValue();
+
+        // Should match.
+        {
+            NfcReaderOptions options = createNfcReaderOptions();
+            options.compatibility = NdefCompatibility.VENDOR;
+            options.url = TEST_URL;
+            WatchResponse mockWatchCallback = mock(WatchResponse.class);
+            nfc.watch(options, mockWatchCallback);
+            verify(mockWatchCallback).call(mWatchCaptor.capture(), mErrorCaptor.capture());
+            assertNull(mErrorCaptor.getValue());
+        }
+        int watchId2 = mWatchCaptor.getValue().intValue();
+
+        // Should match.
+        {
+            NfcReaderOptions options = createNfcReaderOptions();
+            options.compatibility = NdefCompatibility.ANY;
+            options.url = TEST_URL;
+            WatchResponse mockWatchCallback = mock(WatchResponse.class);
+            nfc.watch(options, mockWatchCallback);
+            verify(mockWatchCallback).call(mWatchCaptor.capture(), mErrorCaptor.capture());
+            assertNull(mErrorCaptor.getValue());
+        }
+        int watchId3 = mWatchCaptor.getValue().intValue();
+
+        doReturn(NdefCompatibility.VENDOR).when(mNfcTagHandler).compatibility();
+        nfc.processPendingOperationsForTesting(mNfcTagHandler);
+
+        // Check that client was notified and watch with 'vendor' or 'any' compatibility was
+        // triggered.
+        verify(mNfcClient, times(1))
+                .onWatch(mOnWatchCallbackCaptor.capture(), any(NdefMessage.class));
+        assertEquals(2, mOnWatchCallbackCaptor.getValue().length);
+        assertEquals(watchId2, mOnWatchCallbackCaptor.getValue()[0]);
+        assertEquals(watchId3, mOnWatchCallbackCaptor.getValue()[1]);
+    }
+
+    /**
      * Test that Nfc.watch() WebNFC Id pattern matching works correctly.
      */
     @Test
@@ -918,8 +1036,8 @@
 
         // Should match.
         {
-            NfcWatchOptions options = createNfcWatchOptions();
-            options.mode = NfcWatchMode.WEBNFC_ONLY;
+            NfcReaderOptions options = createNfcReaderOptions();
+            options.compatibility = NdefCompatibility.NFC_FORUM;
             options.url = "https://test.com/*";
             WatchResponse mockWatchCallback = mock(WatchResponse.class);
             nfc.watch(options, mockWatchCallback);
@@ -930,8 +1048,8 @@
 
         // Should match.
         {
-            NfcWatchOptions options = createNfcWatchOptions();
-            options.mode = NfcWatchMode.WEBNFC_ONLY;
+            NfcReaderOptions options = createNfcReaderOptions();
+            options.compatibility = NdefCompatibility.NFC_FORUM;
             options.url = "https://test.com/contact/42";
             WatchResponse mockWatchCallback = mock(WatchResponse.class);
             nfc.watch(options, mockWatchCallback);
@@ -942,8 +1060,8 @@
 
         // Should match.
         {
-            NfcWatchOptions options = createNfcWatchOptions();
-            options.mode = NfcWatchMode.WEBNFC_ONLY;
+            NfcReaderOptions options = createNfcReaderOptions();
+            options.compatibility = NdefCompatibility.NFC_FORUM;
             options.url = "https://subdomain.test.com/*";
             WatchResponse mockWatchCallback = mock(WatchResponse.class);
             nfc.watch(options, mockWatchCallback);
@@ -954,8 +1072,8 @@
 
         // Should match.
         {
-            NfcWatchOptions options = createNfcWatchOptions();
-            options.mode = NfcWatchMode.WEBNFC_ONLY;
+            NfcReaderOptions options = createNfcReaderOptions();
+            options.compatibility = NdefCompatibility.NFC_FORUM;
             options.url = "https://subdomain.test.com/contact";
             WatchResponse mockWatchCallback = mock(WatchResponse.class);
             nfc.watch(options, mockWatchCallback);
@@ -966,8 +1084,8 @@
 
         // Should not match.
         {
-            NfcWatchOptions options = createNfcWatchOptions();
-            options.mode = NfcWatchMode.WEBNFC_ONLY;
+            NfcReaderOptions options = createNfcReaderOptions();
+            options.compatibility = NdefCompatibility.NFC_FORUM;
             options.url = "https://www.test.com/*";
             WatchResponse mockWatchCallback = mock(WatchResponse.class);
             nfc.watch(options, mockWatchCallback);
@@ -977,8 +1095,8 @@
 
         // Should not match.
         {
-            NfcWatchOptions options = createNfcWatchOptions();
-            options.mode = NfcWatchMode.WEBNFC_ONLY;
+            NfcReaderOptions options = createNfcReaderOptions();
+            options.compatibility = NdefCompatibility.NFC_FORUM;
             options.url = "http://test.com/*";
             WatchResponse mockWatchCallback = mock(WatchResponse.class);
             nfc.watch(options, mockWatchCallback);
@@ -988,8 +1106,8 @@
 
         // Should not match.
         {
-            NfcWatchOptions options = createNfcWatchOptions();
-            options.mode = NfcWatchMode.WEBNFC_ONLY;
+            NfcReaderOptions options = createNfcReaderOptions();
+            options.compatibility = NdefCompatibility.NFC_FORUM;
             options.url = "invalid pattern url";
             WatchResponse mockWatchCallback = mock(WatchResponse.class);
             nfc.watch(options, mockWatchCallback);
@@ -1027,8 +1145,8 @@
         nfc.setClient(mNfcClient);
 
         // Should not match when invalid WebNFC Id is received.
-        NfcWatchOptions options = createNfcWatchOptions();
-        options.mode = NfcWatchMode.WEBNFC_ONLY;
+        NfcReaderOptions options = createNfcReaderOptions();
+        options.compatibility = NdefCompatibility.NFC_FORUM;
         options.url = "https://test.com/*";
         WatchResponse mockWatchCallback = mock(WatchResponse.class);
         nfc.watch(options, mockWatchCallback);
@@ -1095,11 +1213,11 @@
         return pushOptions;
     }
 
-    private NfcWatchOptions createNfcWatchOptions() {
-        NfcWatchOptions options = new NfcWatchOptions();
+    private NfcReaderOptions createNfcReaderOptions() {
+        NfcReaderOptions options = new NfcReaderOptions();
         options.url = "";
         options.mediaType = "";
-        options.mode = NfcWatchMode.ANY;
+        options.compatibility = NdefCompatibility.ANY;
         options.recordFilter = null;
         return options;
     }
diff --git a/services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.cc b/services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.cc
index 836234c4..9d18139 100644
--- a/services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.cc
+++ b/services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.cc
@@ -4,7 +4,9 @@
 
 #include "services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h"
 
+#include "base/memory/ptr_util.h"
 #include "device/base/synchronization/shared_memory_seqlock_buffer.h"
+#include "services/device/public/cpp/generic_sensor/sensor_reading.h"
 
 namespace {
 
@@ -15,14 +17,50 @@
 namespace device {
 
 SensorReadingSharedBufferReader::SensorReadingSharedBufferReader(
-    const SensorReadingSharedBuffer* buffer)
-    : buffer_(buffer) {}
+    mojo::ScopedSharedBufferHandle shared_buffer_handle,
+    mojo::ScopedSharedBufferMapping shared_buffer)
+    : shared_buffer_handle_(std::move(shared_buffer_handle)),
+      shared_buffer_(std::move(shared_buffer)) {}
 
 SensorReadingSharedBufferReader::~SensorReadingSharedBufferReader() = default;
 
+// static
+std::unique_ptr<SensorReadingSharedBufferReader>
+SensorReadingSharedBufferReader::Create(
+    mojo::ScopedSharedBufferHandle reading_buffer_handle,
+    uint64_t reading_buffer_offset) {
+  const size_t kReadBufferSize = sizeof(SensorReadingSharedBuffer);
+  DCHECK_EQ(0u, reading_buffer_offset % kReadBufferSize);
+
+  mojo::ScopedSharedBufferMapping shared_buffer =
+      reading_buffer_handle->MapAtOffset(kReadBufferSize,
+                                         reading_buffer_offset);
+
+  if (!shared_buffer)
+    return nullptr;
+
+  return base::WrapUnique(new SensorReadingSharedBufferReader(
+      std::move(reading_buffer_handle), std::move(shared_buffer)));
+}
+
 bool SensorReadingSharedBufferReader::GetReading(SensorReading* result) {
+  if (!shared_buffer_handle_->is_valid())
+    return false;
+
+  const auto* buffer = static_cast<const device::SensorReadingSharedBuffer*>(
+      shared_buffer_.get());
+
+  return GetReading(buffer, result);
+}
+
+// static
+bool SensorReadingSharedBufferReader::GetReading(
+    const SensorReadingSharedBuffer* buffer,
+    SensorReading* result) {
+  DCHECK(buffer);
+
   int read_attempts = 0;
-  while (!TryReadFromBuffer(result)) {
+  while (!TryReadFromBuffer(buffer, result)) {
     // Only try to read this many times before failing to avoid waiting here
     // very long in case of contention with the writer.
     if (++read_attempts == kMaxReadAttemptsCount) {
@@ -36,12 +74,17 @@
   return true;
 }
 
-bool SensorReadingSharedBufferReader::TryReadFromBuffer(SensorReading* result) {
-  auto version = buffer_->seqlock.value().ReadBegin();
-  temp_reading_data_ = buffer_->reading;
-  if (buffer_->seqlock.value().ReadRetry(version))
+// static
+bool SensorReadingSharedBufferReader::TryReadFromBuffer(
+    const SensorReadingSharedBuffer* buffer,
+    SensorReading* result) {
+  DCHECK(buffer);
+
+  auto version = buffer->seqlock.value().ReadBegin();
+  SensorReading temp_reading_data = buffer->reading;
+  if (buffer->seqlock.value().ReadRetry(version))
     return false;
-  *result = temp_reading_data_;
+  *result = temp_reading_data;
   return true;
 }
 
diff --git a/services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h b/services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h
index 354f8a3..b91dd996 100644
--- a/services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h
+++ b/services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h
@@ -5,28 +5,41 @@
 #ifndef SERVICES_DEVICE_PUBLIC_CPP_GENERIC_SENSOR_SENSOR_READING_SHARED_BUFFER_READER_H_
 #define SERVICES_DEVICE_PUBLIC_CPP_GENERIC_SENSOR_SENSOR_READING_SHARED_BUFFER_READER_H_
 
+#include <memory>
+
 #include "base/macros.h"
-#include "services/device/public/cpp/generic_sensor/sensor_reading.h"
+#include "mojo/public/cpp/system/buffer.h"
 
 namespace device {
 
+union SensorReading;
+struct SensorReadingSharedBuffer;
+
 class SensorReadingSharedBufferReader {
  public:
-  // TODO(juncai): Maybe pass a mojo::ScopedSharedBufferHandle to the
-  // constructor.
-  // https://crbug.com/742408
-  explicit SensorReadingSharedBufferReader(
-      const SensorReadingSharedBuffer* buffer);
+  // Creates a new SensorReadingSharedBufferReader instance that reads
+  // sensor readings from the shared buffer.
+  static std::unique_ptr<SensorReadingSharedBufferReader> Create(
+      mojo::ScopedSharedBufferHandle reading_buffer_handle,
+      uint64_t reading_buffer_offset);
+
   ~SensorReadingSharedBufferReader();
 
   // Get sensor reading from shared buffer.
   bool GetReading(SensorReading* result);
+  static bool GetReading(const SensorReadingSharedBuffer* buffer,
+                         SensorReading* result);
 
  private:
-  bool TryReadFromBuffer(SensorReading* result);
+  explicit SensorReadingSharedBufferReader(
+      mojo::ScopedSharedBufferHandle shared_buffer_handle,
+      mojo::ScopedSharedBufferMapping shared_buffer);
 
-  const SensorReadingSharedBuffer* buffer_;
-  SensorReading temp_reading_data_;
+  static bool TryReadFromBuffer(const SensorReadingSharedBuffer* buffer,
+                                SensorReading* result);
+
+  mojo::ScopedSharedBufferHandle shared_buffer_handle_;
+  mojo::ScopedSharedBufferMapping shared_buffer_;
 
   DISALLOW_COPY_AND_ASSIGN(SensorReadingSharedBufferReader);
 };
diff --git a/services/device/public/mojom/nfc.mojom b/services/device/public/mojom/nfc.mojom
index 18e724e79..daad8e53 100644
--- a/services/device/public/mojom/nfc.mojom
+++ b/services/device/public/mojom/nfc.mojom
@@ -33,12 +33,13 @@
   ANY
 };
 
-enum NFCWatchMode {
-  // Restricts scope of the watch operation. Only Web NFC messages must be
-  // used by matching algorithm.
-  WEBNFC_ONLY,
-  // Allows performing watch operation for all NFC messages. For example, NFC
-  // tags with valid NDEF messages.
+enum NDEFCompatibility {
+  // Allows all active and passive NFC devices, supported by the NFC standard.
+  NFC_FORUM,
+  // Allows vendor specific NFC tags (passive device) that require specific
+  // reader chips.
+  VENDOR,
+  // Allows all NDEF compatible devices that the reader chip can read.
   ANY
 };
 
@@ -63,7 +64,7 @@
 
   // The |url| field is an ASCII serialized origin, optionally followed by a URL
   // path. It represents Web NFC id, that can be used for matching Web NFC
-  // content with the filter specified by |url| field in NFCWatchOptions.
+  // content with the filter specified by |url| field in NFCReaderOptions.
   string? url;
 
   // Maximum size of NFC message that can be sent over IPC is 32KB.
@@ -86,7 +87,7 @@
   NDEFRecordType record_type;
 };
 
-struct NFCWatchOptions {
+struct NFCReaderOptions {
   // Defines filtering constraint for NFC messages with specified |url|.
   string? url;
 
@@ -96,8 +97,8 @@
   // Defines media type filtering constraint.
   string? media_type;
 
-  // Defines mode of watch operation.
-  NFCWatchMode mode;
+  // Defines the accepted kind of NFC devices.
+  NDEFCompatibility compatibility;
 };
 
 interface NFC {
@@ -115,8 +116,8 @@
   CancelPush(NFCPushTarget target) => (NFCError? error);
 
   // Starts watching for nearby NFC devices with data that matches
-  // NFCWatchOptions filtering criteria. On success, watch id is returned.
-  Watch(NFCWatchOptions options) => (uint32 id, NFCError? error);
+  // NFCReaderOptions filtering criteria. On success, watch id is returned.
+  Watch(NFCReaderOptions options) => (uint32 id, NFCError? error);
 
   // Cancels watch operation with provided id.
   CancelWatch (uint32 id) => (NFCError? error);
diff --git a/services/identity/public/cpp/identity_manager_unittest.cc b/services/identity/public/cpp/identity_manager_unittest.cc
index dd9c50a..e8f2803 100644
--- a/services/identity/public/cpp/identity_manager_unittest.cc
+++ b/services/identity/public/cpp/identity_manager_unittest.cc
@@ -89,7 +89,7 @@
 
  private:
   // OAuth2TokenService:
-  void InvalidateAccessTokenImpl(const std::string& account_id,
+  void InvalidateAccessTokenImpl(const CoreAccountId& account_id,
                                  const std::string& client_id,
                                  const ScopeSet& scopes,
                                  const std::string& access_token) override {
diff --git a/services/network/conditional_cache_deletion_helper.cc b/services/network/conditional_cache_deletion_helper.cc
index da5fefde..c460650 100644
--- a/services/network/conditional_cache_deletion_helper.cc
+++ b/services/network/conditional_cache_deletion_helper.cc
@@ -9,6 +9,8 @@
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "net/http/http_cache.h"
+#include "net/http/http_util.h"
 
 namespace {
 
@@ -17,9 +19,11 @@
     const base::Time& begin_time,
     const base::Time& end_time,
     const disk_cache::Entry* entry) {
+  std::string entry_key(entry->GetKey());
+  std::string url_string(
+      net::HttpCache::GetResourceURLFromHttpCacheKey(entry_key));
   return (entry->GetLastUsed() >= begin_time &&
-          entry->GetLastUsed() < end_time &&
-          url_matcher.Run(GURL(entry->GetKey())));
+          entry->GetLastUsed() < end_time && url_matcher.Run(GURL(url_string)));
 }
 
 }  // namespace
diff --git a/services/network/cross_origin_read_blocking.cc b/services/network/cross_origin_read_blocking.cc
index 30999c0..60a03f67 100644
--- a/services/network/cross_origin_read_blocking.cc
+++ b/services/network/cross_origin_read_blocking.cc
@@ -211,7 +211,7 @@
 // confirmation sniffing because images, scripts, etc. are frequently
 // mislabelled by http servers as HTML/JSON/XML).
 base::flat_set<std::string>& GetNeverSniffedMimeTypes() {
-  static base::NoDestructor<base::flat_set<std::string>> s_types({
+  static base::NoDestructor<base::flat_set<std::string>> s_types{{
       // The list below has been populated based on most commonly used content
       // types according to HTTP Archive - see:
       // https://github.com/whatwg/fetch/issues/860#issuecomment-457330454
@@ -224,7 +224,7 @@
       "application/x-www-form-urlencoded",
       "application/zip",
       "text/event-stream",
-  });
+  }};
 
   // All items need to be lower-case, to support case-insensitive comparisons
   // later.
diff --git a/services/network/http_cache_data_remover_unittest.cc b/services/network/http_cache_data_remover_unittest.cc
index be140cf..cdc9689 100644
--- a/services/network/http_cache_data_remover_unittest.cc
+++ b/services/network/http_cache_data_remover_unittest.cc
@@ -11,10 +11,12 @@
 #include "base/location.h"
 #include "base/run_loop.h"
 #include "base/stl_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "net/base/cache_type.h"
+#include "net/base/features.h"
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
 #include "net/disk_cache/disk_cache.h"
@@ -76,13 +78,13 @@
   void SetUp() override {
     InitNetworkContext();
 
-    net::HttpCache* cache = network_context_->url_request_context()
-                                ->http_transaction_factory()
-                                ->GetCache();
-    ASSERT_TRUE(cache);
+    cache_ = network_context_->url_request_context()
+                 ->http_transaction_factory()
+                 ->GetCache();
+    ASSERT_TRUE(cache_);
     {
       net::TestCompletionCallback callback;
-      int rv = cache->GetBackend(&backend_, callback.callback());
+      int rv = cache_->GetBackend(&backend_, callback.callback());
       ASSERT_EQ(net::OK, callback.GetResult(rv));
       ASSERT_TRUE(backend_);
     }
@@ -91,8 +93,9 @@
     for (const CacheTestEntry& test_entry : kCacheEntries) {
       disk_cache::Entry* entry = nullptr;
       net::TestCompletionCallback callback;
-      int rv = backend_->CreateEntry(test_entry.url, net::HIGHEST, &entry,
-                                     callback.callback());
+      std::string key = ComputeCacheKey(test_entry.url);
+      int rv =
+          backend_->CreateEntry(key, net::HIGHEST, &entry, callback.callback());
       ASSERT_EQ(net::OK, callback.GetResult(rv));
       ASSERT_TRUE(entry);
       base::Time time;
@@ -105,6 +108,16 @@
               static_cast<size_t>(backend_->GetEntryCount()));
   }
 
+  std::string ComputeCacheKey(const std::string& url_string) {
+    GURL url(url_string);
+    net::HttpRequestInfo request_info;
+    request_info.url = url;
+    request_info.method = "GET";
+    request_info.network_isolation_key =
+        net::NetworkIsolationKey(url::Origin::Create(url));
+    return cache_->GenerateCacheKeyForTest(&request_info);
+  }
+
   void RemoveData(mojom::ClearDataFilterPtr filter,
                   base::Time start_time,
                   base::Time end_time) {
@@ -117,7 +130,8 @@
     run_loop.Run();
   }
 
-  bool HasEntry(const std::string& key) {
+  bool HasEntry(const std::string& url_string) {
+    std::string key = ComputeCacheKey(url_string);
     disk_cache::Entry* entry = nullptr;
     base::RunLoop run_loop;
     net::TestCompletionCallback callback;
@@ -147,6 +161,21 @@
   // Stores the NetworkContextPtr of the most recently created NetworkContext.
   mojom::NetworkContextPtr network_context_ptr_;
   disk_cache::Backend* backend_ = nullptr;
+
+ private:
+  net::HttpCache* cache_;
+};
+
+class HttpCacheDataRemoverSplitCacheTest : public HttpCacheDataRemoverTest {
+ protected:
+  void SetUp() override {
+    feature_list_.InitAndEnableFeature(
+        net::features::kSplitCacheByTopFrameOrigin);
+    HttpCacheDataRemoverTest::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
 };
 
 TEST_F(HttpCacheDataRemoverTest, ClearAll) {
@@ -333,6 +362,54 @@
   RemoveData(/*filter=*/nullptr, base::Time(), base::Time());
 }
 
+TEST_F(HttpCacheDataRemoverSplitCacheTest, FilterDeleteByDomain) {
+  mojom::ClearDataFilterPtr filter = mojom::ClearDataFilter::New();
+  filter->type = mojom::ClearDataFilter_Type::DELETE_MATCHES;
+  filter->domains.push_back("wikipedia.com");
+  filter->domains.push_back("google.com");
+  RemoveData(std::move(filter), base::Time(), base::Time());
+  EXPECT_FALSE(HasEntry(kCacheEntries[0].url));
+  EXPECT_FALSE(HasEntry(kCacheEntries[1].url));
+  EXPECT_FALSE(HasEntry(kCacheEntries[2].url));
+  EXPECT_FALSE(HasEntry(kCacheEntries[3].url));
+  EXPECT_EQ(4, backend_->GetEntryCount());
+}
+
+TEST_F(HttpCacheDataRemoverSplitCacheTest, FilterKeepByDomain) {
+  mojom::ClearDataFilterPtr filter = mojom::ClearDataFilter::New();
+  filter->type = mojom::ClearDataFilter_Type::KEEP_MATCHES;
+  filter->domains.push_back("wikipedia.com");
+  filter->domains.push_back("google.com");
+  RemoveData(std::move(filter), base::Time(), base::Time());
+  EXPECT_TRUE(HasEntry(kCacheEntries[0].url));
+  EXPECT_TRUE(HasEntry(kCacheEntries[1].url));
+  EXPECT_TRUE(HasEntry(kCacheEntries[2].url));
+  EXPECT_TRUE(HasEntry(kCacheEntries[3].url));
+  EXPECT_EQ(4, backend_->GetEntryCount());
+}
+
+TEST_F(HttpCacheDataRemoverSplitCacheTest, FilterDeleteByOrigin) {
+  mojom::ClearDataFilterPtr filter = mojom::ClearDataFilter::New();
+  filter->type = mojom::ClearDataFilter_Type::DELETE_MATCHES;
+  filter->origins.push_back(url::Origin::Create(GURL("http://www.google.com")));
+  filter->origins.push_back(url::Origin::Create(GURL("http://localhost:1234")));
+  RemoveData(std::move(filter), base::Time(), base::Time());
+  EXPECT_FALSE(HasEntry(kCacheEntries[0].url));
+  EXPECT_FALSE(HasEntry(kCacheEntries[4].url));
+  EXPECT_EQ(6, backend_->GetEntryCount());
+}
+
+TEST_F(HttpCacheDataRemoverSplitCacheTest, FilterKeepByOrigin) {
+  mojom::ClearDataFilterPtr filter = mojom::ClearDataFilter::New();
+  filter->type = mojom::ClearDataFilter_Type::KEEP_MATCHES;
+  filter->origins.push_back(url::Origin::Create(GURL("http://www.google.com")));
+  filter->origins.push_back(url::Origin::Create(GURL("http://localhost:1234")));
+  RemoveData(std::move(filter), base::Time(), base::Time());
+  EXPECT_TRUE(HasEntry(kCacheEntries[0].url));
+  EXPECT_TRUE(HasEntry(kCacheEntries[4].url));
+  EXPECT_EQ(2, backend_->GetEntryCount());
+}
+
 }  // namespace
 
 }  // namespace network
diff --git a/services/network/public/mojom/websocket.mojom b/services/network/public/mojom/websocket.mojom
index 3ffcbdd8..a568244e 100644
--- a/services/network/public/mojom/websocket.mojom
+++ b/services/network/public/mojom/websocket.mojom
@@ -77,13 +77,11 @@
   //   UTF-8. If |fin| is not set, |data| must be non-empty.
   OnDataFrame(bool fin, WebSocketMessageType type, array<uint8> data);
 
-  // Add |quota| tokens of send quota for the channel. |quota| must be a
-  // positive integer. Both the browser and the renderer set send quota for the
-  // other side, and check that quota has not been exceeded when receiving
-  // messages.  Both sides start a new channel with a quota of 0, and must wait
-  // for a FlowControl message before calling SendFrame. The total available
-  // quota on one side must never exceed 0x7FFFFFFFFFFFFFFF tokens.
-  OnFlowControl(int64 quota);
+  // Add |quota| bytes of send quota. |quota| must be positive. Initial quota
+  // is 0. The renderer will wait for an AddSendFlowControlQuota() message
+  // before forwarding any messages to the browser. Total quota must never
+  // exceed 0x7FFFFFFFFFFFFFFF bytes.
+  AddSendFlowControlQuota(int64 quota);
 
   // Drop the channel.
   //
diff --git a/services/network/throttling/throttling_controller.h b/services/network/throttling/throttling_controller.h
index 43751c4..3c6f87b 100644
--- a/services/network/throttling/throttling_controller.h
+++ b/services/network/throttling/throttling_controller.h
@@ -38,7 +38,7 @@
 
   // TODO(https://crbug.com/960874): Debugging code to try and shed some light
   // on why the owned maps are invalid.
-  enum class Liveness : int32_t {
+  enum class Liveness : uint32_t {
     kAlive = 0xCA11AB13,
     kDead = 0xDEADBEEF,
   };
diff --git a/services/network/websocket.cc b/services/network/websocket.cc
index 9d99806..b863f9b 100644
--- a/services/network/websocket.cc
+++ b/services/network/websocket.cc
@@ -88,7 +88,7 @@
                    scoped_refptr<net::IOBuffer> buffer,
                    size_t buffer_size) override;
   void OnClosingHandshake() override;
-  void OnFlowControl(int64_t quota) override;
+  void OnSendFlowControlQuotaAdded(int64_t quota) override;
   void OnDropChannel(bool was_clean,
                      uint16_t code,
                      const std::string& reason) override;
@@ -176,11 +176,12 @@
   impl_->client_->OnClosingHandshake();
 }
 
-void WebSocket::WebSocketEventHandler::OnFlowControl(int64_t quota) {
-  DVLOG(3) << "WebSocketEventHandler::OnFlowControl @"
+void WebSocket::WebSocketEventHandler::OnSendFlowControlQuotaAdded(
+    int64_t quota) {
+  DVLOG(3) << "WebSocketEventHandler::OnSendFlowControlQuotaAdded @"
            << reinterpret_cast<void*>(this) << " quota=" << quota;
 
-  impl_->client_->OnFlowControl(quota);
+  impl_->client_->AddSendFlowControlQuota(quota);
 }
 
 void WebSocket::WebSocketEventHandler::OnDropChannel(
diff --git a/storage/browser/fileapi/sandbox_file_stream_writer.cc b/storage/browser/fileapi/sandbox_file_stream_writer.cc
index 9cf34451..981d8ef 100644
--- a/storage/browser/fileapi/sandbox_file_stream_writer.cc
+++ b/storage/browser/fileapi/sandbox_file_stream_writer.cc
@@ -136,10 +136,9 @@
   }
   file_size_ = file_info.size;
   if (initial_offset_ > file_size_) {
-    LOG(ERROR) << initial_offset_ << ", " << file_size_;
-    // This shouldn't happen as long as we check offset in the renderer.
-    NOTREACHED();
-    initial_offset_ = file_size_;
+    // We should not be writing pass the end of the file.
+    std::move(callback).Run(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
+    return;
   }
   DCHECK(!file_writer_.get());
 
diff --git a/testing/buildbot/chromium.chrome.json b/testing/buildbot/chromium.chrome.json
index 8e7b299..a344dc3 100644
--- a/testing/buildbot/chromium.chrome.json
+++ b/testing/buildbot/chromium.chrome.json
@@ -2003,7 +2003,9 @@
         "test": "ui_touch_selection_unittests"
       },
       {
-        "experiment_percentage": 100,
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.unit_tests.filter"
+        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 13c3ffa2..7cfff9b 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -645,7 +645,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -660,7 +660,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -675,7 +675,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -690,7 +690,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -705,7 +705,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -720,7 +720,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -735,7 +735,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -750,7 +750,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -765,7 +765,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -780,7 +780,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -795,7 +795,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -810,7 +810,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -826,7 +826,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -841,7 +841,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -856,7 +856,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -871,7 +871,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 21
@@ -891,7 +891,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 20
@@ -912,7 +912,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -927,7 +927,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -945,7 +945,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -960,7 +960,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -975,7 +975,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -990,7 +990,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1005,7 +1005,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1020,7 +1020,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1035,7 +1035,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1050,7 +1050,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1065,7 +1065,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1080,7 +1080,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1095,7 +1095,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 6
@@ -1116,7 +1116,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1135,7 +1135,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 10
@@ -1151,7 +1151,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1170,7 +1170,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1185,7 +1185,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1200,7 +1200,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1215,7 +1215,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1230,7 +1230,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1245,7 +1245,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1260,7 +1260,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1275,7 +1275,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1290,7 +1290,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1305,7 +1305,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1320,7 +1320,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1335,7 +1335,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1350,7 +1350,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1365,7 +1365,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1380,7 +1380,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1395,7 +1395,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1410,7 +1410,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 3
@@ -1431,7 +1431,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1446,7 +1446,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1461,7 +1461,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1476,7 +1476,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1491,7 +1491,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1506,7 +1506,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1521,7 +1521,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1536,7 +1536,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1551,7 +1551,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1566,7 +1566,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1581,7 +1581,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1596,7 +1596,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1611,7 +1611,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1626,7 +1626,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1641,7 +1641,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1656,7 +1656,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1671,7 +1671,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1686,7 +1686,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 2
@@ -1705,7 +1705,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1720,7 +1720,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1735,7 +1735,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1750,7 +1750,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1765,7 +1765,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1780,7 +1780,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1795,7 +1795,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1810,7 +1810,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1825,7 +1825,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1840,7 +1840,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1855,7 +1855,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1870,7 +1870,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1885,7 +1885,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1900,7 +1900,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1915,7 +1915,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1930,7 +1930,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1945,7 +1945,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1960,7 +1960,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1975,7 +1975,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1990,7 +1990,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2005,7 +2005,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2020,7 +2020,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2035,7 +2035,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2051,7 +2051,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2066,7 +2066,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2081,7 +2081,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2096,7 +2096,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2111,7 +2111,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 18808a4..aaea37c 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -3833,7 +3833,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3848,7 +3848,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3863,7 +3863,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3878,7 +3878,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3893,7 +3893,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3908,7 +3908,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3923,7 +3923,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3938,7 +3938,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3953,7 +3953,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3968,7 +3968,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3984,7 +3984,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -3999,7 +3999,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4014,7 +4014,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4029,7 +4029,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 10
@@ -4050,7 +4050,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4065,7 +4065,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4083,7 +4083,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4098,7 +4098,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4113,7 +4113,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4128,7 +4128,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4143,7 +4143,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4158,7 +4158,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4173,7 +4173,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4188,7 +4188,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4203,7 +4203,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 6
@@ -4224,7 +4224,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4244,7 +4244,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4259,7 +4259,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4274,7 +4274,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4289,7 +4289,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4304,7 +4304,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4319,7 +4319,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4334,7 +4334,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4349,7 +4349,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4364,7 +4364,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4379,7 +4379,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4394,7 +4394,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4409,7 +4409,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4424,7 +4424,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4439,7 +4439,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4454,7 +4454,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4469,7 +4469,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4484,7 +4484,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4499,7 +4499,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4514,7 +4514,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4529,7 +4529,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 3
@@ -4550,7 +4550,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4565,7 +4565,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4580,7 +4580,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4595,7 +4595,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4610,7 +4610,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4625,7 +4625,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4640,7 +4640,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4655,7 +4655,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4670,7 +4670,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4685,7 +4685,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4700,7 +4700,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4715,7 +4715,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4730,7 +4730,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4745,7 +4745,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4760,7 +4760,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4775,7 +4775,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4790,7 +4790,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 2
@@ -4806,7 +4806,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4821,7 +4821,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4836,7 +4836,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4851,7 +4851,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4866,7 +4866,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4881,7 +4881,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4896,7 +4896,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4911,7 +4911,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4926,7 +4926,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4941,7 +4941,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4956,7 +4956,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4971,7 +4971,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4986,7 +4986,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5001,7 +5001,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5016,7 +5016,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5031,7 +5031,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5046,7 +5046,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5061,7 +5061,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5076,7 +5076,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5091,7 +5091,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5106,7 +5106,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5121,7 +5121,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5136,7 +5136,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5151,7 +5151,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5166,7 +5166,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5182,7 +5182,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5201,7 +5201,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -5217,7 +5217,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -5233,7 +5233,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -5249,7 +5249,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -5265,7 +5265,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -5281,7 +5281,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -5297,7 +5297,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -5321,7 +5321,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 20
@@ -5338,7 +5338,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "idempotent": false
@@ -5359,7 +5359,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "idempotent": false,
@@ -5381,7 +5381,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "idempotent": false,
@@ -5404,7 +5404,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -5420,7 +5420,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -5443,7 +5443,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 20
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 7cd5178..d9065cb 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -1970,9 +1970,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
+              "gpu": "8086:5912",
               "os": "Windows-10",
-              "pool": "chrome.tests.perf",
-              "synthetic_product_name": "OptiPlex 7050 (Dell Inc.)"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 7200,
@@ -2007,9 +2007,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
+              "gpu": "8086:5912",
               "os": "Windows-10",
-              "pool": "chrome.tests.perf",
-              "synthetic_product_name": "OptiPlex 7050 (Dell Inc.)"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 7200,
@@ -2044,9 +2044,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
+              "gpu": "8086:5912",
               "os": "Windows-10",
-              "pool": "chrome.tests.perf",
-              "synthetic_product_name": "OptiPlex 7050 (Dell Inc.)"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 7200,
@@ -2081,9 +2081,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
+              "gpu": "8086:5912",
               "os": "Windows-10",
-              "pool": "chrome.tests.perf",
-              "synthetic_product_name": "OptiPlex 7050 (Dell Inc.)"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 7200,
@@ -2118,9 +2118,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
+              "gpu": "8086:5912",
               "os": "Windows-10",
-              "pool": "chrome.tests.perf",
-              "synthetic_product_name": "OptiPlex 7050 (Dell Inc.)"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 7200,
@@ -2159,9 +2159,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
+              "gpu": "8086:5912",
               "os": "Windows-10",
-              "pool": "chrome.tests.perf",
-              "synthetic_product_name": "OptiPlex 7050 (Dell Inc.)"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 7200,
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index 335afe2..0be4941 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -38,6 +38,7 @@
     "//testing/buildbot/filters/chromeos.base_unittests.filter",
     "//testing/buildbot/filters/chromeos.media_unittests.filter",
     "//testing/buildbot/filters/chromeos.services_unittests.filter",
+    "//testing/buildbot/filters/chromeos.unit_tests.filter",
   ]
 }
 
diff --git a/testing/buildbot/filters/chromeos.unit_tests.filter b/testing/buildbot/filters/chromeos.unit_tests.filter
new file mode 100644
index 0000000..53fd6a2
--- /dev/null
+++ b/testing/buildbot/filters/chromeos.unit_tests.filter
@@ -0,0 +1,9 @@
+# TODO(crbug.com/970790): Enable this.
+-InputMethodManagerImplTest.TestEnableLayouts
+-InputMethodManagerImplTest.TestEnableLayoutsNonUsHardwareKeyboard
+-InputMethodManagerImplTest.TestEnableMultipleHardwareKeyboardLayout
+-InputMethodManagerImplTest.TestLastUsedInputMethod
+-InputMethodManagerImplTest.TestNextInputMethod
+-InputMethodManagerImplTest.TestObserver
+# TODO(crbug.com/970806): Enable this.
+-KeyboardShortcutViewerMetadataTest.ModifyAcceleratorShouldUpdateMetadata
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 28d00d33..2e14e87 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1476,9 +1476,9 @@
         },
       },
       'linux-chromeos-google-rel': {
-        # TODO(https://crbug.com/932269): Promote out of experiment when the
-        # tests are green.
-        'experiment_percentage': 100,
+        'args': [
+          '--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.unit_tests.filter',
+        ],
       },
     },
   },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 4e975d1..5a046ba 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -706,7 +706,7 @@
       },
       'linux-chromeos-dbg': {
         'mixins': [
-          'linux-trusty',
+          'linux-xenial',
         ],
         'test_suites': {
           'gtest_tests': 'linux_chromeos_gtests',
@@ -3425,7 +3425,7 @@
       },
       'Linux Tests (dbg)(1)': {
         'mixins': [
-          'linux-trusty',
+          'linux-xenial',
         ],
         'test_suites': {
           'gtest_tests': 'chromium_linux_gtests',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 04a3d5546..4d2bdf0 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -597,24 +597,6 @@
             ]
         }
     ],
-    "AutofillDropdownLayout": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AutofillDropdownLayout"
-                    ]
-                }
-            ]
-        }
-    ],
     "AutofillDynamicForms": [
         {
             "platforms": [
@@ -810,39 +792,6 @@
             ]
         }
     ],
-    "AutofillPreviewStyleExperiment": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_BlackOnGoogleBlue050_V2",
-                    "params": {
-                        "bg_color": "#E8F0FE",
-                        "color": "#000000"
-                    },
-                    "enable_features": [
-                        "AutofillPreviewStyleExperiment"
-                    ]
-                },
-                {
-                    "name": "Enabled_BlackOnGoogleYellow050_V2",
-                    "params": {
-                        "bg_color": "#E6F4EA",
-                        "color": "#000000"
-                    },
-                    "enable_features": [
-                        "AutofillPreviewStyleExperiment"
-                    ]
-                }
-            ]
-        }
-    ],
     "AutofillProfileValidation": [
         {
             "platforms": [
@@ -1766,6 +1715,24 @@
             ]
         }
     ],
+    "DialMediaRouteProvider": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "DialMediaRouteProvider"
+                    ]
+                }
+            ]
+        }
+    ],
     "DirectCompositionGpuVSync": [
         {
             "platforms": [
@@ -3027,6 +2994,24 @@
             ]
         }
     ],
+    "MirroringService": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "MirroringService"
+                    ]
+                }
+            ]
+        }
+    ],
     "ModernMediaControls": [
         {
             "platforms": [
@@ -3852,25 +3837,6 @@
             ]
         }
     ],
-    "PaymentResponseRetry": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "PaymentResponseRetry"
-                    ]
-                }
-            ]
-        }
-    ],
     "PdfIsolation": [
         {
             "platforms": [
diff --git a/third_party/blink/common/mediastream/media_stream_controls.cc b/third_party/blink/common/mediastream/media_stream_controls.cc
index 9637125..b992bd65 100644
--- a/third_party/blink/common/mediastream/media_stream_controls.cc
+++ b/third_party/blink/common/mediastream/media_stream_controls.cc
@@ -8,7 +8,7 @@
 
 TrackControls::TrackControls() {}
 
-TrackControls::TrackControls(bool request, MediaStreamType type)
+TrackControls::TrackControls(bool request, mojom::MediaStreamType type)
     : requested(request), stream_type(type) {}
 
 TrackControls::TrackControls(const TrackControls& other) = default;
@@ -19,9 +19,11 @@
 
 StreamControls::StreamControls(bool request_audio, bool request_video)
     : audio(request_audio,
-            request_audio ? MEDIA_DEVICE_AUDIO_CAPTURE : MEDIA_NO_SERVICE),
+            request_audio ? mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE
+                          : mojom::MediaStreamType::NO_SERVICE),
       video(request_video,
-            request_video ? MEDIA_DEVICE_VIDEO_CAPTURE : MEDIA_NO_SERVICE) {}
+            request_video ? mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE
+                          : mojom::MediaStreamType::NO_SERVICE) {}
 
 StreamControls::~StreamControls() {}
 
diff --git a/third_party/blink/common/mediastream/media_stream_mojom_traits.cc b/third_party/blink/common/mediastream/media_stream_mojom_traits.cc
index 3ef4409..d022978 100644
--- a/third_party/blink/common/mediastream/media_stream_mojom_traits.cc
+++ b/third_party/blink/common/mediastream/media_stream_mojom_traits.cc
@@ -12,76 +12,6 @@
 namespace mojo {
 
 // static
-blink::mojom::MediaStreamType
-EnumTraits<blink::mojom::MediaStreamType, blink::MediaStreamType>::ToMojom(
-    blink::MediaStreamType type) {
-  switch (type) {
-    case blink::MediaStreamType::MEDIA_NO_SERVICE:
-      return blink::mojom::MediaStreamType::MEDIA_NO_SERVICE;
-    case blink::MediaStreamType::MEDIA_DEVICE_AUDIO_CAPTURE:
-      return blink::mojom::MediaStreamType::MEDIA_DEVICE_AUDIO_CAPTURE;
-    case blink::MediaStreamType::MEDIA_DEVICE_VIDEO_CAPTURE:
-      return blink::mojom::MediaStreamType::MEDIA_DEVICE_VIDEO_CAPTURE;
-    case blink::MediaStreamType::MEDIA_GUM_TAB_AUDIO_CAPTURE:
-      return blink::mojom::MediaStreamType::MEDIA_GUM_TAB_AUDIO_CAPTURE;
-    case blink::MediaStreamType::MEDIA_GUM_TAB_VIDEO_CAPTURE:
-      return blink::mojom::MediaStreamType::MEDIA_GUM_TAB_VIDEO_CAPTURE;
-    case blink::MediaStreamType::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE:
-      return blink::mojom::MediaStreamType::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE;
-    case blink::MediaStreamType::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE:
-      return blink::mojom::MediaStreamType::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE;
-    case blink::MediaStreamType::MEDIA_DISPLAY_VIDEO_CAPTURE:
-      return blink::mojom::MediaStreamType::MEDIA_DISPLAY_VIDEO_CAPTURE;
-    case blink::MediaStreamType::MEDIA_DISPLAY_AUDIO_CAPTURE:
-      return blink::mojom::MediaStreamType::MEDIA_DISPLAY_AUDIO_CAPTURE;
-    case blink::MediaStreamType::NUM_MEDIA_TYPES:
-      return blink::mojom::MediaStreamType::NUM_MEDIA_TYPES;
-  }
-  NOTREACHED();
-  return blink::mojom::MediaStreamType::MEDIA_NO_SERVICE;
-}
-
-// static
-bool EnumTraits<blink::mojom::MediaStreamType, blink::MediaStreamType>::
-    FromMojom(blink::mojom::MediaStreamType input,
-              blink::MediaStreamType* out) {
-  switch (input) {
-    case blink::mojom::MediaStreamType::MEDIA_NO_SERVICE:
-      *out = blink::MediaStreamType::MEDIA_NO_SERVICE;
-      return true;
-    case blink::mojom::MediaStreamType::MEDIA_DEVICE_AUDIO_CAPTURE:
-      *out = blink::MediaStreamType::MEDIA_DEVICE_AUDIO_CAPTURE;
-      return true;
-    case blink::mojom::MediaStreamType::MEDIA_DEVICE_VIDEO_CAPTURE:
-      *out = blink::MediaStreamType::MEDIA_DEVICE_VIDEO_CAPTURE;
-      return true;
-    case blink::mojom::MediaStreamType::MEDIA_GUM_TAB_AUDIO_CAPTURE:
-      *out = blink::MediaStreamType::MEDIA_GUM_TAB_AUDIO_CAPTURE;
-      return true;
-    case blink::mojom::MediaStreamType::MEDIA_GUM_TAB_VIDEO_CAPTURE:
-      *out = blink::MediaStreamType::MEDIA_GUM_TAB_VIDEO_CAPTURE;
-      return true;
-    case blink::mojom::MediaStreamType::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE:
-      *out = blink::MediaStreamType::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE;
-      return true;
-    case blink::mojom::MediaStreamType::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE:
-      *out = blink::MediaStreamType::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE;
-      return true;
-    case blink::mojom::MediaStreamType::MEDIA_DISPLAY_VIDEO_CAPTURE:
-      *out = blink::MediaStreamType::MEDIA_DISPLAY_VIDEO_CAPTURE;
-      return true;
-    case blink::mojom::MediaStreamType::MEDIA_DISPLAY_AUDIO_CAPTURE:
-      *out = blink::MediaStreamType::MEDIA_DISPLAY_AUDIO_CAPTURE;
-      return true;
-    case blink::mojom::MediaStreamType::NUM_MEDIA_TYPES:
-      *out = blink::MediaStreamType::NUM_MEDIA_TYPES;
-      return true;
-  }
-  NOTREACHED();
-  return false;
-}
-
-// static
 bool StructTraits<blink::mojom::MediaStreamDeviceDataView,
                   blink::MediaStreamDevice>::
     Read(blink::mojom::MediaStreamDeviceDataView input,
diff --git a/third_party/blink/common/mediastream/media_stream_request.cc b/third_party/blink/common/mediastream/media_stream_request.cc
index 4b0c692..76fb7c2 100644
--- a/third_party/blink/common/mediastream/media_stream_request.cc
+++ b/third_party/blink/common/mediastream/media_stream_request.cc
@@ -9,56 +9,57 @@
 
 namespace blink {
 
-bool IsAudioInputMediaType(MediaStreamType type) {
-  return (type == MEDIA_DEVICE_AUDIO_CAPTURE ||
-          type == MEDIA_GUM_TAB_AUDIO_CAPTURE ||
-          type == MEDIA_GUM_DESKTOP_AUDIO_CAPTURE ||
-          type == MEDIA_DISPLAY_AUDIO_CAPTURE);
+bool IsAudioInputMediaType(mojom::MediaStreamType type) {
+  return (type == mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE ||
+          type == mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE ||
+          type == mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE ||
+          type == mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE);
 }
 
-bool IsVideoInputMediaType(MediaStreamType type) {
-  return (type == MEDIA_DEVICE_VIDEO_CAPTURE ||
-          type == MEDIA_GUM_TAB_VIDEO_CAPTURE ||
-          type == MEDIA_GUM_DESKTOP_VIDEO_CAPTURE ||
-          type == MEDIA_DISPLAY_VIDEO_CAPTURE);
+bool IsVideoInputMediaType(mojom::MediaStreamType type) {
+  return (type == mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE ||
+          type == mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE ||
+          type == mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE ||
+          type == mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE);
 }
 
-bool IsScreenCaptureMediaType(MediaStreamType type) {
+bool IsScreenCaptureMediaType(mojom::MediaStreamType type) {
   return IsDesktopCaptureMediaType(type) || IsTabCaptureMediaType(type);
 }
 
-bool IsVideoScreenCaptureMediaType(MediaStreamType type) {
+bool IsVideoScreenCaptureMediaType(mojom::MediaStreamType type) {
   return IsVideoDesktopCaptureMediaType(type) ||
-         type == MEDIA_GUM_TAB_VIDEO_CAPTURE;
+         type == mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE;
 }
 
-bool IsDesktopCaptureMediaType(MediaStreamType type) {
-  return (type == MEDIA_GUM_DESKTOP_AUDIO_CAPTURE ||
+bool IsDesktopCaptureMediaType(mojom::MediaStreamType type) {
+  return (type == mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE ||
           IsVideoDesktopCaptureMediaType(type));
 }
 
-bool IsVideoDesktopCaptureMediaType(MediaStreamType type) {
-  return (type == MEDIA_DISPLAY_VIDEO_CAPTURE ||
-          type == MEDIA_GUM_DESKTOP_VIDEO_CAPTURE);
+bool IsVideoDesktopCaptureMediaType(mojom::MediaStreamType type) {
+  return (type == mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE ||
+          type == mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE);
 }
 
-bool IsTabCaptureMediaType(MediaStreamType type) {
-  return (type == MEDIA_GUM_TAB_AUDIO_CAPTURE ||
-          type == MEDIA_GUM_TAB_VIDEO_CAPTURE);
+bool IsTabCaptureMediaType(mojom::MediaStreamType type) {
+  return (type == mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE ||
+          type == mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE);
 }
 
-bool IsDeviceMediaType(MediaStreamType type) {
-  return (type == MEDIA_DEVICE_AUDIO_CAPTURE ||
-          type == MEDIA_DEVICE_VIDEO_CAPTURE);
+bool IsDeviceMediaType(mojom::MediaStreamType type) {
+  return (type == mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE ||
+          type == mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
 }
 
 // static
 const int MediaStreamDevice::kNoId = -1;
 
 MediaStreamDevice::MediaStreamDevice()
-    : type(MEDIA_NO_SERVICE), video_facing(media::MEDIA_VIDEO_FACING_NONE) {}
+    : type(mojom::MediaStreamType::NO_SERVICE),
+      video_facing(media::MEDIA_VIDEO_FACING_NONE) {}
 
-MediaStreamDevice::MediaStreamDevice(MediaStreamType type,
+MediaStreamDevice::MediaStreamDevice(mojom::MediaStreamType type,
                                      const std::string& id,
                                      const std::string& name)
     : type(type),
@@ -67,7 +68,7 @@
       name(name) {}
 
 MediaStreamDevice::MediaStreamDevice(
-    MediaStreamType type,
+    mojom::MediaStreamType type,
     const std::string& id,
     const std::string& name,
     media::VideoFacingMode facing,
@@ -78,7 +79,7 @@
       group_id(group_id),
       name(name) {}
 
-MediaStreamDevice::MediaStreamDevice(MediaStreamType type,
+MediaStreamDevice::MediaStreamDevice(mojom::MediaStreamType type,
                                      const std::string& id,
                                      const std::string& name,
                                      int sample_rate,
diff --git a/third_party/blink/common/push_messaging/push_messaging_mojom_traits.cc b/third_party/blink/common/push_messaging/push_messaging_mojom_traits.cc
index 562c1b29..afa2c3e 100644
--- a/third_party/blink/common/push_messaging/push_messaging_mojom_traits.cc
+++ b/third_party/blink/common/push_messaging/push_messaging_mojom_traits.cc
@@ -8,47 +8,6 @@
 
 namespace mojo {
 
-// PushErrorType
-static_assert(blink::WebPushError::ErrorType::kErrorTypeAbort ==
-                  static_cast<blink::WebPushError::ErrorType>(
-                      blink::mojom::PushErrorType::ABORT),
-              "PushErrorType enums must match, ABORT");
-
-static_assert(blink::WebPushError::ErrorType::kErrorTypeNetwork ==
-                  static_cast<blink::WebPushError::ErrorType>(
-                      blink::mojom::PushErrorType::NETWORK),
-              "PushErrorType enums must match, NETWORK");
-
-static_assert(blink::WebPushError::ErrorType::kErrorTypeNone ==
-                  static_cast<blink::WebPushError::ErrorType>(
-                      blink::mojom::PushErrorType::NONE),
-              "PushErrorType enums must match, NONE");
-
-static_assert(blink::WebPushError::ErrorType::kErrorTypeNotAllowed ==
-                  static_cast<blink::WebPushError::ErrorType>(
-                      blink::mojom::PushErrorType::NOT_ALLOWED),
-              "PushErrorType enums must match, NOT_ALLOWED");
-
-static_assert(blink::WebPushError::ErrorType::kErrorTypeNotFound ==
-                  static_cast<blink::WebPushError::ErrorType>(
-                      blink::mojom::PushErrorType::NOT_FOUND),
-              "PushErrorType enums must match, NOT_FOUND");
-
-static_assert(blink::WebPushError::ErrorType::kErrorTypeNotSupported ==
-                  static_cast<blink::WebPushError::ErrorType>(
-                      blink::mojom::PushErrorType::NOT_SUPPORTED),
-              "PushErrorType enums must match, NOT_SUPPORTED");
-
-static_assert(blink::WebPushError::ErrorType::kErrorTypeInvalidState ==
-                  static_cast<blink::WebPushError::ErrorType>(
-                      blink::mojom::PushErrorType::INVALID_STATE),
-              "PushErrorType enums must match, INVALID_STATE");
-
-static_assert(blink::WebPushError::ErrorType::kErrorTypeLast ==
-                  static_cast<blink::WebPushError::ErrorType>(
-                      blink::mojom::PushErrorType::kMaxValue),
-              "PushErrorType enums must match, kMaxValue");
-
 // static
 bool StructTraits<blink::mojom::PushSubscriptionOptionsDataView,
                   blink::WebPushSubscriptionOptions>::
@@ -67,31 +26,4 @@
   return true;
 }
 
-// static
-blink::mojom::PushErrorType
-EnumTraits<blink::mojom::PushErrorType, blink::WebPushError::ErrorType>::
-    ToMojom(blink::WebPushError::ErrorType input) {
-  if (input >= blink::WebPushError::ErrorType::kErrorTypeAbort &&
-      input <= blink::WebPushError::ErrorType::kErrorTypeInvalidState) {
-    return static_cast<blink::mojom::PushErrorType>(input);
-  }
-
-  NOTREACHED();
-  return blink::mojom::PushErrorType::ABORT;
-}
-
-// static
-bool EnumTraits<blink::mojom::PushErrorType, blink::WebPushError::ErrorType>::
-    FromMojom(blink::mojom::PushErrorType input,
-              blink::WebPushError::ErrorType* output) {
-  if (input >= blink::mojom::PushErrorType::ABORT &&
-      input <= blink::mojom::PushErrorType::INVALID_STATE) {
-    *output = static_cast<blink::WebPushError::ErrorType>(input);
-    return true;
-  }
-
-  NOTREACHED();
-  return false;
-}
-
 }  // namespace mojo
diff --git a/third_party/blink/public/common/mediastream/media_stream.typemap b/third_party/blink/public/common/mediastream/media_stream.typemap
index 2fded1c0..5f7163f 100644
--- a/third_party/blink/public/common/mediastream/media_stream.typemap
+++ b/third_party/blink/public/common/mediastream/media_stream.typemap
@@ -16,7 +16,6 @@
 
 type_mappings = [
   "blink.mojom.MediaStreamDevice=blink::MediaStreamDevice",
-  "blink.mojom.MediaStreamType=blink::MediaStreamType",
   "blink.mojom.StreamControls=blink::StreamControls",
   "blink.mojom.TrackControls=blink::TrackControls",
 ]
diff --git a/third_party/blink/public/common/mediastream/media_stream_controls.h b/third_party/blink/public/common/mediastream/media_stream_controls.h
index 8a73b7ba..4b4551c 100644
--- a/third_party/blink/public/common/mediastream/media_stream_controls.h
+++ b/third_party/blink/public/common/mediastream/media_stream_controls.h
@@ -23,14 +23,14 @@
 
 struct BLINK_COMMON_EXPORT TrackControls {
   TrackControls();
-  explicit TrackControls(bool request, MediaStreamType type);
+  explicit TrackControls(bool request, mojom::MediaStreamType type);
   explicit TrackControls(const TrackControls& other);
   ~TrackControls();
 
   bool requested = false;
 
   // Represents the requested  stream type.
-  MediaStreamType stream_type = MEDIA_NO_SERVICE;
+  mojom::MediaStreamType stream_type = mojom::MediaStreamType::NO_SERVICE;
 
   // An empty string represents the default device.
   // A nonempty string represents a specific device.
diff --git a/third_party/blink/public/common/mediastream/media_stream_mojom_traits.h b/third_party/blink/public/common/mediastream/media_stream_mojom_traits.h
index 6815314..3533860 100644
--- a/third_party/blink/public/common/mediastream/media_stream_mojom_traits.h
+++ b/third_party/blink/public/common/mediastream/media_stream_mojom_traits.h
@@ -13,18 +13,9 @@
 namespace mojo {
 
 template <>
-struct BLINK_COMMON_EXPORT
-    EnumTraits<blink::mojom::MediaStreamType, blink::MediaStreamType> {
-  static blink::mojom::MediaStreamType ToMojom(blink::MediaStreamType type);
-
-  static bool FromMojom(blink::mojom::MediaStreamType input,
-                        blink::MediaStreamType* out);
-};
-
-template <>
 struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::MediaStreamDeviceDataView,
                                         blink::MediaStreamDevice> {
-  static const blink::MediaStreamType& type(
+  static const blink::mojom::MediaStreamType& type(
       const blink::MediaStreamDevice& device) {
     return device.type;
   }
@@ -77,7 +68,7 @@
     return controls.requested;
   }
 
-  static const blink::MediaStreamType& stream_type(
+  static const blink::mojom::MediaStreamType& stream_type(
       const blink::TrackControls& controls) {
     return controls.stream_type;
   }
diff --git a/third_party/blink/public/common/mediastream/media_stream_request.h b/third_party/blink/public/common/mediastream/media_stream_request.h
index 117db7b..d76b3837 100644
--- a/third_party/blink/public/common/mediastream/media_stream_request.h
+++ b/third_party/blink/public/common/mediastream/media_stream_request.h
@@ -18,36 +18,10 @@
 #include "media/capture/video/video_capture_device_descriptor.h"
 #include "media/mojo/interfaces/display_media_information.mojom.h"
 #include "third_party/blink/public/common/common_export.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 namespace blink {
 
-// Types of media streams. When updating this list, make sure to update the
-// predicates declared below, e.g. IsVideoScreenCaptureMediaType().
-enum MediaStreamType {
-  MEDIA_NO_SERVICE = 0,
-
-  // A device provided by the operating system (e.g., webcam input).
-  MEDIA_DEVICE_AUDIO_CAPTURE,
-  MEDIA_DEVICE_VIDEO_CAPTURE,
-
-  // Below MEDIA_GUM_* types represent the streams generated by
-  // getUserMedia() calls with constraints that are collected with legacy
-  // APIs for desktop and tab capture.
-  // Mirroring of a browser tab.
-  MEDIA_GUM_TAB_AUDIO_CAPTURE,
-  MEDIA_GUM_TAB_VIDEO_CAPTURE,
-  // Desktop media sources.
-  MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
-  // Capture system audio (post-mix loopback stream).
-  MEDIA_GUM_DESKTOP_AUDIO_CAPTURE,
-
-  // Enables the capture of a user's display, generated by getDisplayMedia()
-  // call.
-  MEDIA_DISPLAY_VIDEO_CAPTURE,
-  MEDIA_DISPLAY_AUDIO_CAPTURE,
-
-  NUM_MEDIA_TYPES
-};
 
 // Types of media stream requests that can be made to the media controller.
 enum MediaStreamRequestType {
@@ -59,15 +33,17 @@
 
 // Convenience predicates to determine whether the given type represents some
 // audio or some video device.
-BLINK_COMMON_EXPORT bool IsAudioInputMediaType(MediaStreamType type);
-BLINK_COMMON_EXPORT bool IsVideoInputMediaType(MediaStreamType type);
-BLINK_COMMON_EXPORT bool IsScreenCaptureMediaType(MediaStreamType type);
+BLINK_COMMON_EXPORT bool IsAudioInputMediaType(mojom::MediaStreamType type);
+BLINK_COMMON_EXPORT bool IsVideoInputMediaType(mojom::MediaStreamType type);
+BLINK_COMMON_EXPORT bool IsScreenCaptureMediaType(mojom::MediaStreamType type);
 // Whether the |type| captures anything on the screen.
-BLINK_COMMON_EXPORT bool IsVideoScreenCaptureMediaType(MediaStreamType type);
-BLINK_COMMON_EXPORT bool IsDesktopCaptureMediaType(MediaStreamType type);
-BLINK_COMMON_EXPORT bool IsVideoDesktopCaptureMediaType(MediaStreamType type);
-BLINK_COMMON_EXPORT bool IsTabCaptureMediaType(MediaStreamType type);
-BLINK_COMMON_EXPORT bool IsDeviceMediaType(MediaStreamType type);
+BLINK_COMMON_EXPORT bool IsVideoScreenCaptureMediaType(
+    mojom::MediaStreamType type);
+BLINK_COMMON_EXPORT bool IsDesktopCaptureMediaType(mojom::MediaStreamType type);
+BLINK_COMMON_EXPORT bool IsVideoDesktopCaptureMediaType(
+    mojom::MediaStreamType type);
+BLINK_COMMON_EXPORT bool IsTabCaptureMediaType(mojom::MediaStreamType type);
+BLINK_COMMON_EXPORT bool IsDeviceMediaType(mojom::MediaStreamType type);
 
 // TODO(xians): Change the structs to classes.
 // Represents one device in a request for media stream(s).
@@ -76,18 +52,18 @@
 
   MediaStreamDevice();
 
-  MediaStreamDevice(MediaStreamType type,
+  MediaStreamDevice(mojom::MediaStreamType type,
                     const std::string& id,
                     const std::string& name);
 
   MediaStreamDevice(
-      MediaStreamType type,
+      mojom::MediaStreamType type,
       const std::string& id,
       const std::string& name,
       media::VideoFacingMode facing,
       const base::Optional<std::string>& group_id = base::nullopt);
 
-  MediaStreamDevice(MediaStreamType type,
+  MediaStreamDevice(mojom::MediaStreamType type,
                     const std::string& id,
                     const std::string& name,
                     int sample_rate,
@@ -103,7 +79,7 @@
   bool IsSameDevice(const MediaStreamDevice& other_device) const;
 
   // The device's type.
-  MediaStreamType type;
+  mojom::MediaStreamType type;
 
   // The device's unique ID.
   std::string id;
diff --git a/third_party/blink/public/common/push_messaging/push_messaging.typemap b/third_party/blink/public/common/push_messaging/push_messaging.typemap
index 4f7a0e51..22349b9 100644
--- a/third_party/blink/public/common/push_messaging/push_messaging.typemap
+++ b/third_party/blink/public/common/push_messaging/push_messaging.typemap
@@ -3,15 +3,10 @@
 # found in the LICENSE file.
 
 mojom = "//third_party/blink/public/mojom/push_messaging/push_messaging.mojom"
-public_headers = [
-  "//third_party/blink/public/common/push_messaging/web_push_subscription_options.h",
-  "//third_party/blink/public/platform/modules/push_messaging/web_push_error.h",
-]
+public_headers = [ "//third_party/blink/public/common/push_messaging/web_push_subscription_options.h" ]
 traits_headers = [ "//third_party/blink/public/common/push_messaging/push_messaging_mojom_traits.h" ]
 sources = [
   "//third_party/blink/common/push_messaging/push_messaging_mojom_traits.cc",
 ]
-type_mappings = [
-  "blink.mojom.PushErrorType=blink::WebPushError::ErrorType",
-  "blink.mojom.PushSubscriptionOptions=blink::WebPushSubscriptionOptions",
-]
+type_mappings =
+    [ "blink.mojom.PushSubscriptionOptions=blink::WebPushSubscriptionOptions" ]
diff --git a/third_party/blink/public/common/push_messaging/push_messaging_mojom_traits.h b/third_party/blink/public/common/push_messaging/push_messaging_mojom_traits.h
index e974639..98639f6d 100644
--- a/third_party/blink/public/common/push_messaging/push_messaging_mojom_traits.h
+++ b/third_party/blink/public/common/push_messaging/push_messaging_mojom_traits.h
@@ -30,15 +30,6 @@
                    blink::WebPushSubscriptionOptions* out);
 };
 
-template <>
-struct BLINK_COMMON_EXPORT
-    EnumTraits<blink::mojom::PushErrorType, blink::WebPushError::ErrorType> {
-  static blink::mojom::PushErrorType ToMojom(
-      blink::WebPushError::ErrorType input);
-  static bool FromMojom(blink::mojom::PushErrorType input,
-                        blink::WebPushError::ErrorType* output);
-};
-
 }  // namespace mojo
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_PUSH_MESSAGING_PUSH_MESSAGING_MOJOM_TRAITS_H_
diff --git a/third_party/blink/public/mojom/mediastream/media_stream.mojom b/third_party/blink/public/mojom/mediastream/media_stream.mojom
index a3b25fd..224996a9 100644
--- a/third_party/blink/public/mojom/mediastream/media_stream.mojom
+++ b/third_party/blink/public/mojom/mediastream/media_stream.mojom
@@ -8,17 +8,35 @@
 import "media/mojo/interfaces/audio_parameters.mojom";
 import "media/mojo/interfaces/display_media_information.mojom";
 
-// Types of media streams (see public/common/media_stream_request.h).
+// Types of media streams. When updating this list, make sure to update the
+// predicates declared in public/common/media_stream_request.h,
+// e.g. IsVideoScreenCaptureMediaType().
 enum MediaStreamType {
-  MEDIA_NO_SERVICE,
-  MEDIA_DEVICE_AUDIO_CAPTURE,
-  MEDIA_DEVICE_VIDEO_CAPTURE,
-  MEDIA_GUM_TAB_AUDIO_CAPTURE,
-  MEDIA_GUM_TAB_VIDEO_CAPTURE,
-  MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
-  MEDIA_GUM_DESKTOP_AUDIO_CAPTURE,
-  MEDIA_DISPLAY_VIDEO_CAPTURE,
-  MEDIA_DISPLAY_AUDIO_CAPTURE,
+  NO_SERVICE,
+
+  // A device provided by the operating system (e.g., webcam input).
+  DEVICE_AUDIO_CAPTURE,
+  DEVICE_VIDEO_CAPTURE,
+
+  // Below GUM_* types represent the streams generated by
+  // getUserMedia() calls with constraints that are collected with legacy
+  // APIs for desktop and tab capture.
+  // Mirroring of a browser tab.
+  GUM_TAB_AUDIO_CAPTURE,
+  GUM_TAB_VIDEO_CAPTURE,
+
+  // Desktop media sources.
+  GUM_DESKTOP_VIDEO_CAPTURE,
+  // Capture system audio (post-mix loopback stream).
+  GUM_DESKTOP_AUDIO_CAPTURE,
+
+  // Enables the capture of a user's display, generated by getDisplayMedia()
+  // call.
+  DISPLAY_VIDEO_CAPTURE,
+  DISPLAY_AUDIO_CAPTURE,
+
+  // TODO(crbug.com/971228): Remove NUM_MEDIA_TYPES, as per the conventions
+  // in docs/security/mojo.md#do-not-define-placeholder-enumerator-values.
   NUM_MEDIA_TYPES
 };
 
diff --git a/third_party/blink/public/mojom/native_file_system/native_file_system_directory_handle.mojom b/third_party/blink/public/mojom/native_file_system/native_file_system_directory_handle.mojom
index e0778da..bec4a05 100644
--- a/third_party/blink/public/mojom/native_file_system/native_file_system_directory_handle.mojom
+++ b/third_party/blink/public/mojom/native_file_system/native_file_system_directory_handle.mojom
@@ -7,6 +7,7 @@
 import "third_party/blink/public/mojom/native_file_system/native_file_system_file_handle.mojom";
 import "third_party/blink/public/mojom/native_file_system/native_file_system_error.mojom";
 import "third_party/blink/public/mojom/native_file_system/native_file_system_transfer_token.mojom";
+import "third_party/blink/public/mojom/permissions/permission_status.mojom";
 
 // Union representing either a file or a directory handle. Used in APIs that
 // can return arbitrary handles.
@@ -30,6 +31,13 @@
 // fact that our paths really aren't base::FilePath, but instead are virtual
 // paths).
 interface NativeFileSystemDirectoryHandle {
+  // Queries the current permission status for this handle.
+  GetPermissionStatus(bool writable) => (PermissionStatus status);
+
+  // Requests read and/or write permission for this handle. Returns the new
+  // permission status for this handle.
+  RequestPermission(bool writable) => (PermissionStatus status);
+
   // Returns a file with the given |name| that is a child of this directory. If
   // no such file exists, and |create| is true, the file is first created.
   // Returns an error if the operation fails, or a handle to the newly created
@@ -50,22 +58,6 @@
   // until all entries have been retrieved.
   GetEntries() => (NativeFileSystemError result, array<NativeFileSystemEntry> entries);
 
-  // Creates a new file or directory, or overwrites an existing one in this
-  // directory by moving the file or directory represented by |source|. The
-  // new file or directory will be named |name|.
-  // Returns an error if the operation fails, or a handle to the newly created
-  // file or directory if the operation succeeds.
-  MoveFrom(NativeFileSystemTransferToken source, string name) =>
-      (NativeFileSystemError result, NativeFileSystemEntry? entry);
-
-  // Creates a new file or directory, or overwrites an existing one in this
-  // directory by copying the file or directory represented by |source|. The
-  // new file or directory will be named |name|.
-  // Returns an error if the operation fails, or a handle to the newly created
-  // file or directory if the operation succeeds.
-  CopyFrom(NativeFileSystemTransferToken source, string name) =>
-      (NativeFileSystemError result, NativeFileSystemEntry? entry);
-
   // Deletes this directory. To delete recursively, set |recursive| to true.
   Remove(bool recurse) => (NativeFileSystemError result);
 
diff --git a/third_party/blink/public/mojom/native_file_system/native_file_system_file_handle.mojom b/third_party/blink/public/mojom/native_file_system/native_file_system_file_handle.mojom
index 49d07793b..e08476a0 100644
--- a/third_party/blink/public/mojom/native_file_system/native_file_system_file_handle.mojom
+++ b/third_party/blink/public/mojom/native_file_system/native_file_system_file_handle.mojom
@@ -8,10 +8,18 @@
 import "third_party/blink/public/mojom/blob/serialized_blob.mojom";
 import "third_party/blink/public/mojom/native_file_system/native_file_system_error.mojom";
 import "third_party/blink/public/mojom/native_file_system/native_file_system_transfer_token.mojom";
+import "third_party/blink/public/mojom/permissions/permission_status.mojom";
 
 // This interface represents a handle to a directory in the Native File System
 // API.
 interface NativeFileSystemFileHandle {
+  // Queries the current permission status for this handle.
+  GetPermissionStatus(bool writable) => (PermissionStatus status);
+
+  // Requests read and/or write permission for this handle. Returns the new
+  // permission status for this handle.
+  RequestPermission(bool writable) => (PermissionStatus status);
+
   // Returns a blob representing the current state of this file.
   AsBlob() => (NativeFileSystemError result, SerializedBlob? blob);
 
diff --git a/third_party/blink/public/platform/web_cursor_info.h b/third_party/blink/public/platform/web_cursor_info.h
index b3197b2..b93b78f 100644
--- a/third_party/blink/public/platform/web_cursor_info.h
+++ b/third_party/blink/public/platform/web_cursor_info.h
@@ -91,6 +91,8 @@
     kTypeZoomOut,
     kTypeGrab,
     kTypeGrabbing,
+    kTypeMiddlePanningVertical,
+    kTypeMiddlePanningHorizontal,
     kTypeCustom,
     kTypeMaxValue = kTypeCustom
   };
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index 6ad3c27..f1d73e93 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -151,7 +151,6 @@
   BLINK_PLATFORM_EXPORT static void EnablePaymentRequest(bool);
   BLINK_PLATFORM_EXPORT static void EnablePaymentRequestHasEnrolledInstrument(
       bool);
-  BLINK_PLATFORM_EXPORT static void EnablePaymentRetry(bool);
   BLINK_PLATFORM_EXPORT static void EnablePerformanceManagerInstrumentation(
       bool);
   BLINK_PLATFORM_EXPORT static void EnablePeriodicBackgroundSync(bool);
diff --git a/third_party/blink/public/platform/web_thread_type.h b/third_party/blink/public/platform/web_thread_type.h
index b7d8ec51..207e87d 100644
--- a/third_party/blink/public/platform/web_thread_type.h
+++ b/third_party/blink/public/platform/web_thread_type.h
@@ -26,8 +26,9 @@
   kReverbConvolutionBackgroundThread = 13,
   kHRTFDatabaseLoaderThread = 14,
   kTestThread = 15,
+  kAudioEncoderThread = 16,
 
-  kCount = 16
+  kCount = 17
 };
 
 BLINK_PLATFORM_EXPORT const char* GetNameForThreadType(WebThreadType);
diff --git a/third_party/blink/public/web/DEPS b/third_party/blink/public/web/DEPS
index 92c8477b..cc4aa40 100644
--- a/third_party/blink/public/web/DEPS
+++ b/third_party/blink/public/web/DEPS
@@ -8,6 +8,7 @@
     "+base/memory/scoped_refptr.h",
     "+base/strings",
     "+base/time/time.h",
+    "+base/threading/thread_checker.h",
     "+cc/input/browser_controls_state.h",
     "+cc/input/overscroll_behavior.h",
     "+cc/input/layer_selection_bound.h",
diff --git a/content/renderer/media_recorder/audio_track_recorder.h b/third_party/blink/public/web/modules/mediarecorder/audio_track_recorder.h
similarity index 67%
rename from content/renderer/media_recorder/audio_track_recorder.h
rename to third_party/blink/public/web/modules/mediarecorder/audio_track_recorder.h
index 65458c5..2ee36d9d2 100644
--- a/content/renderer/media_recorder/audio_track_recorder.h
+++ b/third_party/blink/public/web/modules/mediarecorder/audio_track_recorder.h
@@ -2,20 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_MEDIA_RECORDER_AUDIO_TRACK_RECORDER_H_
-#define CONTENT_RENDERER_MEDIA_RECORDER_AUDIO_TRACK_RECORDER_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIARECORDER_AUDIO_TRACK_RECORDER_H_
+#define THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIARECORDER_AUDIO_TRACK_RECORDER_H_
 
 #include <memory>
 
-#include "base/callback.h"
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/threading/thread.h"
 #include "base/threading/thread_checker.h"
-#include "base/time/time.h"
-#include "content/common/content_export.h"
-#include "content/renderer/media_recorder/audio_track_encoder.h"
 #include "third_party/blink/public/platform/modules/mediastream/web_media_stream_audio_sink.h"
+#include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
 
 namespace media {
@@ -23,17 +18,19 @@
 class AudioParameters;
 }  // namespace media
 
-namespace content {
+namespace blink {
+
+class AudioTrackEncoder;
+class Thread;
 
 // AudioTrackRecorder is a MediaStreamAudioSink that encodes the audio buses
 // received from a Stream Audio Track. The class is constructed on a
-// single thread (the main Render thread) but can recieve MediaStreamAudioSink-
+// single thread (the main Render thread) but can receive MediaStreamAudioSink-
 // related calls on a different "live audio" thread (referred to internally as
 // the "capture thread"). It owns an internal thread to use for encoding, on
 // which lives an AudioTrackEncoder with its own threading subtleties, see the
 // implementation file.
-class CONTENT_EXPORT AudioTrackRecorder
-    : public blink::WebMediaStreamAudioSink {
+class BLINK_EXPORT AudioTrackRecorder : public WebMediaStreamAudioSink {
  public:
   enum class CodecId {
     // Do not change the order of codecs. Add new ones right before LAST.
@@ -43,14 +40,14 @@
   };
 
   using OnEncodedAudioCB =
-      base::Callback<void(const media::AudioParameters& params,
-                          std::unique_ptr<std::string> encoded_data,
-                          base::TimeTicks capture_time)>;
+      base::RepeatingCallback<void(const media::AudioParameters& params,
+                                   std::unique_ptr<std::string> encoded_data,
+                                   base::TimeTicks capture_time)>;
 
   static CodecId GetPreferredCodecId();
 
   AudioTrackRecorder(CodecId codec,
-                     const blink::WebMediaStreamTrack& track,
+                     const WebMediaStreamTrack& track,
                      OnEncodedAudioCB on_encoded_audio_cb,
                      int32_t bits_per_second);
   ~AudioTrackRecorder() override;
@@ -66,20 +63,17 @@
  private:
   // Creates an audio encoder from |codec|. Returns nullptr if the codec is
   // invalid.
-  static AudioTrackEncoder* CreateAudioEncoder(
+  static scoped_refptr<AudioTrackEncoder> CreateAudioEncoder(
       CodecId codec,
       OnEncodedAudioCB on_encoded_audio_cb,
       int32_t bits_per_second);
 
-  // Used to check that we are destroyed on the same thread we were created on.
-  base::ThreadChecker main_render_thread_checker_;
-
   // Used to check that MediaStreamAudioSink's methods are called on the
   // capture audio thread.
-  base::ThreadChecker capture_thread_checker_;
+  THREAD_CHECKER(capture_thread_checker_);
 
   // We need to hold on to the Blink track to remove ourselves on destruction.
-  const blink::WebMediaStreamTrack track_;
+  const WebMediaStreamTrack track_;
 
   // Thin wrapper around OpusEncoder.
   // |encoder_| should be initialized before |encoder_thread_| such that
@@ -90,11 +84,13 @@
   const scoped_refptr<AudioTrackEncoder> encoder_;
 
   // The thread on which |encoder_| works.
-  base::Thread encoder_thread_;
+  std::unique_ptr<Thread> encoder_thread_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(AudioTrackRecorder);
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_MEDIA_RECORDER_AUDIO_TRACK_RECORDER_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIARECORDER_AUDIO_TRACK_RECORDER_H_
diff --git a/third_party/blink/public/web/web_ax_object.h b/third_party/blink/public/web/web_ax_object.h
index 8d07410..5b53e37 100644
--- a/third_party/blink/public/web/web_ax_object.h
+++ b/third_party/blink/public/web/web_ax_object.h
@@ -265,6 +265,7 @@
   BLINK_EXPORT bool HasComputedStyle() const;
   BLINK_EXPORT WebString ComputedStyleDisplay() const;
   BLINK_EXPORT bool AccessibilityIsIgnored() const;
+  BLINK_EXPORT bool AccessibilityIsIncludedInTree() const;
   BLINK_EXPORT void Markers(WebVector<ax::mojom::MarkerType>& types,
                             WebVector<int>& starts,
                             WebVector<int>& ends) const;
diff --git a/third_party/blink/renderer/bindings/bindings.gni b/third_party/blink/renderer/bindings/bindings.gni
index 070bdbd..d7c4229 100644
--- a/third_party/blink/renderer/bindings/bindings.gni
+++ b/third_party/blink/renderer/bindings/bindings.gni
@@ -59,6 +59,8 @@
                     "core/v8/module_record.h",
                     "core/v8/native_value_traits.h",
                     "core/v8/native_value_traits_impl.h",
+                    "core/v8/profiler_trace_builder.cc",
+                    "core/v8/profiler_trace_builder.h",
                     "core/v8/referrer_script_info.cc",
                     "core/v8/referrer_script_info.h",
                     "core/v8/rejected_promises.cc",
diff --git a/third_party/blink/renderer/bindings/core/v8/isolated_world_csp.cc b/third_party/blink/renderer/bindings/core/v8/isolated_world_csp.cc
index 473b6ba0..9404ad9 100644
--- a/third_party/blink/renderer/bindings/core/v8/isolated_world_csp.cc
+++ b/third_party/blink/renderer/bindings/core/v8/isolated_world_csp.cc
@@ -51,7 +51,7 @@
     // https://w3c.github.io/webappsec-csp/#violation-url.
     // TODO(crbug.com/916885): Figure out if we want to support violation
     // reporting for isolated world CSPs.
-    DEFINE_STATIC_LOCAL(KURL, g_empty_url, ());
+    DEFINE_STATIC_LOCAL(const KURL, g_empty_url, ());
     return g_empty_url;
   }
 
diff --git a/third_party/blink/renderer/bindings/core/v8/profiler_trace_builder.cc b/third_party/blink/renderer/bindings/core/v8/profiler_trace_builder.cc
new file mode 100644
index 0000000..e7bb44c
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/profiler_trace_builder.cc
@@ -0,0 +1,185 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/bindings/core/v8/profiler_trace_builder.h"
+
+#include "base/time/time.h"
+#include "third_party/blink/renderer/core/timing/performance.h"
+#include "third_party/blink/renderer/core/timing/profiler_frame.h"
+#include "third_party/blink/renderer/core/timing/profiler_sample.h"
+#include "third_party/blink/renderer/core/timing/profiler_stack.h"
+#include "third_party/blink/renderer/core/timing/profiler_trace.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+ProfilerTrace* ProfilerTraceBuilder::FromProfile(
+    ScriptState* script_state,
+    const v8::CpuProfile* profile,
+    const SecurityOrigin* allowed_origin,
+    base::TimeTicks time_origin) {
+  TRACE_EVENT0("blink", "ProfilerTraceBuilder::FromProfile");
+  ProfilerTraceBuilder* builder = MakeGarbageCollected<ProfilerTraceBuilder>(
+      script_state, allowed_origin, time_origin);
+  for (int i = 0; i < profile->GetSamplesCount(); i++) {
+    const auto* node = profile->GetSample(i);
+    auto timestamp = base::TimeTicks() + base::TimeDelta::FromMicroseconds(
+                                             profile->GetSampleTimestamp(i));
+    builder->AddSample(node, timestamp);
+  }
+  return builder->GetTrace();
+}
+
+ProfilerTraceBuilder::ProfilerTraceBuilder(ScriptState* script_state,
+                                           const SecurityOrigin* allowed_origin,
+                                           base::TimeTicks time_origin)
+    : script_state_(script_state),
+      allowed_origin_(allowed_origin),
+      time_origin_(time_origin) {}
+
+void ProfilerTraceBuilder::Trace(blink::Visitor* visitor) {
+  visitor->Trace(script_state_);
+  visitor->Trace(frames_);
+  visitor->Trace(stacks_);
+  visitor->Trace(samples_);
+}
+
+void ProfilerTraceBuilder::AddSample(const v8::CpuProfileNode* node,
+                                     base::TimeTicks timestamp) {
+  auto* sample = ProfilerSample::Create();
+  auto relative_timestamp = Performance::MonotonicTimeToDOMHighResTimeStamp(
+      time_origin_, timestamp, true);
+
+  sample->setTimestamp(relative_timestamp);
+  if (base::Optional<wtf_size_t> stack_id = GetOrInsertStackId(node))
+    sample->setStackId(*stack_id);
+
+  samples_.push_back(sample);
+}
+
+base::Optional<wtf_size_t> ProfilerTraceBuilder::GetOrInsertStackId(
+    const v8::CpuProfileNode* node) {
+  if (!node)
+    return base::Optional<wtf_size_t>();
+
+  // Omit frames that don't pass a cross-origin check.
+  // Do this at the stack level (rather than the frame level) to avoid
+  // including skeleton frames without data.
+  KURL resource_url(node->GetScriptResourceNameStr());
+  if (!ShouldIncludeStackFrame(resource_url, node->GetScriptId(),
+                               node->GetSourceType(),
+                               node->IsScriptSharedCrossOrigin())) {
+    return GetOrInsertStackId(node->GetParent());
+  }
+
+  auto existing_stack_id = node_to_stack_map_.find(node);
+  if (existing_stack_id != node_to_stack_map_.end()) {
+    // If we found a stack entry for this node ID, the subpath to the root
+    // already exists in the trace, and we may coalesce.
+    return existing_stack_id->value;
+  }
+
+  auto* stack = ProfilerStack::Create();
+  wtf_size_t frame_id = GetOrInsertFrameId(node);
+  stack->setFrameId(frame_id);
+  if (base::Optional<int> parent_stack_id =
+          GetOrInsertStackId(node->GetParent()))
+    stack->setParentId(*parent_stack_id);
+
+  wtf_size_t stack_id = stacks_.size();
+  stacks_.push_back(stack);
+  node_to_stack_map_.Set(node, stack_id);
+  return stack_id;
+}
+
+wtf_size_t ProfilerTraceBuilder::GetOrInsertFrameId(
+    const v8::CpuProfileNode* node) {
+  auto existing_frame_id = node_to_frame_map_.find(node);
+
+  if (existing_frame_id != node_to_frame_map_.end())
+    return existing_frame_id->value;
+
+  auto* frame = ProfilerFrame::Create();
+  frame->setName(node->GetFunctionNameStr());
+  if (*node->GetScriptResourceNameStr() != '\0') {
+    wtf_size_t resource_id =
+        GetOrInsertResourceId(node->GetScriptResourceNameStr());
+    frame->setResourceId(resource_id);
+  }
+  if (node->GetLineNumber() != v8::CpuProfileNode::kNoLineNumberInfo)
+    frame->setLine(node->GetLineNumber());
+  if (node->GetColumnNumber() != v8::CpuProfileNode::kNoColumnNumberInfo)
+    frame->setColumn(node->GetColumnNumber());
+
+  wtf_size_t frame_id = frames_.size();
+  frames_.push_back(frame);
+  node_to_frame_map_.Set(node, frame_id);
+
+  return frame_id;
+}
+
+wtf_size_t ProfilerTraceBuilder::GetOrInsertResourceId(
+    const char* resource_name) {
+  // Since V8's CPU profiler already does string interning, pointer equality is
+  // value equality here.
+  auto existing_resource_id = resource_map_.find(resource_name);
+
+  if (existing_resource_id != resource_map_.end())
+    return existing_resource_id->value;
+
+  wtf_size_t resource_id = resources_.size();
+  resources_.push_back(resource_name);
+  resource_map_.Set(resource_name, resource_id);
+
+  return resource_id;
+}
+
+ProfilerTrace* ProfilerTraceBuilder::GetTrace() const {
+  ProfilerTrace* trace = ProfilerTrace::Create();
+  trace->setResources(resources_);
+  trace->setFrames(frames_);
+  trace->setStacks(stacks_);
+  trace->setSamples(samples_);
+  return trace;
+}
+
+bool ProfilerTraceBuilder::ShouldIncludeStackFrame(
+    const KURL& script_url,
+    int script_id,
+    v8::CpuProfileNode::SourceType source_type,
+    bool script_shared_cross_origin) {
+  // Omit V8 metadata frames.
+  if (source_type != v8::CpuProfileNode::kScript &&
+      source_type != v8::CpuProfileNode::kBuiltin &&
+      source_type != v8::CpuProfileNode::kCallback) {
+    return false;
+  }
+
+  // If we couldn't derive script data, only allow builtins and callbacks.
+  if (script_id == v8::UnboundScript::kNoScriptId) {
+    return source_type == v8::CpuProfileNode::kBuiltin ||
+           source_type == v8::CpuProfileNode::kCallback;
+  }
+
+  // If we already tested whether or not this script was cross-origin, return
+  // the cached results.
+  auto it = script_same_origin_cache_.find(script_id);
+  if (it != script_same_origin_cache_.end())
+    return it->value;
+
+  if (!script_url.IsValid())
+    return false;
+
+  auto origin = SecurityOrigin::Create(script_url);
+  // TODO(acomminos): Consider easing this check based on optional headers.
+  bool allowed = script_shared_cross_origin ||
+                 origin->IsSameSchemeHostPort(allowed_origin_);
+  script_same_origin_cache_.Set(script_id, allowed);
+  return allowed;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/profiler_trace_builder.h b/third_party/blink/renderer/bindings/core/v8/profiler_trace_builder.h
new file mode 100644
index 0000000..48ccf85
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/profiler_trace_builder.h
@@ -0,0 +1,133 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_PROFILER_TRACE_BUILDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_PROFILER_TRACE_BUILDER_H_
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
+#include "v8/include/v8-profiler.h"
+
+namespace blink {
+
+class ProfilerFrame;
+class ProfilerSample;
+class ProfilerStack;
+class ProfilerTrace;
+class ScriptState;
+
+// A hash uniquely identified by the substack associated with the node.
+struct ProfilerNodeStackHash {
+  STATIC_ONLY(ProfilerNodeStackHash);
+
+  static bool Equal(const v8::CpuProfileNode* a, const v8::CpuProfileNode* b) {
+    return a->GetNodeId() == b->GetNodeId();
+  }
+
+  static unsigned GetHash(const v8::CpuProfileNode* node) {
+    return node->GetNodeId();
+  }
+
+  static const bool safe_to_compare_to_empty_or_deleted = false;
+};
+
+// A hash uniquely identified by the stack frame associated with the node.
+struct ProfilerNodeFrameHash {
+  STATIC_ONLY(ProfilerNodeFrameHash);
+
+  static bool Equal(const v8::CpuProfileNode* a, const v8::CpuProfileNode* b) {
+    return a->GetFunctionName() == b->GetFunctionName() &&
+           a->GetScriptResourceName() == b->GetScriptResourceName() &&
+           a->GetLineNumber() == b->GetLineNumber() &&
+           a->GetColumnNumber() == b->GetColumnNumber();
+  }
+
+  static unsigned GetHash(const v8::CpuProfileNode* node) {
+    return StringHash::GetHash(node->GetFunctionNameStr()) ^
+           StringHash::GetHash(node->GetScriptResourceNameStr()) ^
+           DefaultHash<unsigned>::Hash().GetHash(node->GetLineNumber()) ^
+           DefaultHash<unsigned>::Hash().GetHash(node->GetColumnNumber());
+  }
+
+  static const bool safe_to_compare_to_empty_or_deleted = false;
+};
+
+// Produces a structurally compressed trace from a v8::CpuProfile relative to a
+// time origin, and omits frames from cross-origin scripts that do not
+// participate in CORS.
+//
+// The trace format is described at:
+// https://wicg.github.io/js-self-profiling/#the-profilertrace-dictionary
+class ProfilerTraceBuilder
+    : public GarbageCollectedFinalized<ProfilerTraceBuilder> {
+ public:
+  static ProfilerTrace* FromProfile(ScriptState*,
+                                    const v8::CpuProfile* profile,
+                                    const SecurityOrigin* allowed_origin,
+                                    base::TimeTicks time_origin);
+
+  explicit ProfilerTraceBuilder(ScriptState*,
+                                const SecurityOrigin* allowed_origin,
+                                base::TimeTicks time_origin);
+
+  void Trace(blink::Visitor*);
+
+ private:
+  // Adds a stack sample from V8 to the trace, performing necessary filtering
+  // and coalescing.
+  void AddSample(const v8::CpuProfileNode* node, base::TimeTicks timestamp);
+  // Obtains the stack ID of the substack with the given node as its leaf,
+  // performing origin-based filtering.
+  base::Optional<wtf_size_t> GetOrInsertStackId(const v8::CpuProfileNode* node);
+  // Obtains the frame ID of the stack frame represented by the given node.
+  wtf_size_t GetOrInsertFrameId(const v8::CpuProfileNode* node);
+  // Obtains the resource ID for the given resource name.
+  wtf_size_t GetOrInsertResourceId(const char* resource_name);
+
+  ProfilerTrace* GetTrace() const;
+
+  // Discards metadata frames and performs an origin check on the given stack
+  // frame, returning true if it either has the same origin as the profiler, or
+  // if it should be shared cross origin.
+  bool ShouldIncludeStackFrame(const KURL& script_url,
+                               int script_id,
+                               v8::CpuProfileNode::SourceType source_type,
+                               bool script_shared_cross_origin);
+
+  Member<ScriptState> script_state_;
+
+  const SecurityOrigin* allowed_origin_;
+  const base::TimeTicks time_origin_;
+
+  Vector<String> resources_;
+  HeapVector<Member<ProfilerFrame>> frames_;
+  HeapVector<Member<ProfilerStack>> stacks_;
+  HeapVector<Member<ProfilerSample>> samples_;
+
+  // Maps V8-managed resource strings to their indices in the resources table.
+  HashMap<const char*, wtf_size_t> resource_map_;
+  HashMap<const v8::CpuProfileNode*, wtf_size_t, ProfilerNodeStackHash>
+      node_to_stack_map_;
+  HashMap<const v8::CpuProfileNode*, wtf_size_t, ProfilerNodeFrameHash>
+      node_to_frame_map_;
+
+  // A mapping from a V8 internal script ID to whether or not it passes the
+  // same-origin policy for the ScriptState that the trace belongs to.
+  HashMap<int, bool> script_same_origin_cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProfilerTraceBuilder);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_PROFILER_TRACE_BUILDER_H_
diff --git a/third_party/blink/renderer/core/animation/animation_sim_test.cc b/third_party/blink/renderer/core/animation/animation_sim_test.cc
index aed7877..72d10464 100644
--- a/third_party/blink/renderer/core/animation/animation_sim_test.cc
+++ b/third_party/blink/renderer/core/animation/animation_sim_test.cc
@@ -95,7 +95,7 @@
                                 GetDocument().ElementSheet().Contents());
   keyframes.clear();
   keyframes.push_back(std::move(keyframe));
-  timing = Timing::Defaults();
+  timing = Timing();
   timing.iteration_duration = AnimationTimeDelta::FromSecondsD(1);
   ElementAnimation::animateInternal(
       *target, MakeGarbageCollected<StringKeyframeEffectModel>(keyframes),
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc
index 3241e6b..4e456ba86 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -376,7 +376,7 @@
       Timing specified_timing = timing;
       scoped_refptr<TimingFunction> keyframe_timing_function =
           timing.timing_function;
-      timing.timing_function = Timing::Defaults().timing_function;
+      timing.timing_function = Timing().timing_function;
 
       StyleRuleKeyframes* keyframes_rule =
           resolver->FindKeyframesRule(element_for_scoping, name);
diff --git a/third_party/blink/renderer/core/animation/timing.h b/third_party/blink/renderer/core/animation/timing.h
index 9b0fd2da..41833b6 100644
--- a/third_party/blink/renderer/core/animation/timing.h
+++ b/third_party/blink/renderer/core/animation/timing.h
@@ -55,11 +55,6 @@
   static FillMode StringToFillMode(const String&);
   static String PlaybackDirectionString(PlaybackDirection);
 
-  static const Timing& Defaults() {
-    DEFINE_STATIC_LOCAL(Timing, timing, ());
-    return timing;
-  }
-
   Timing()
       : start_delay(0),
         end_delay(0),
diff --git a/third_party/blink/renderer/core/animation/timing_input.cc b/third_party/blink/renderer/core/animation/timing_input.cc
index 92a29c8f8..13ee259 100644
--- a/third_party/blink/renderer/core/animation/timing_input.cc
+++ b/third_party/blink/renderer/core/animation/timing_input.cc
@@ -62,7 +62,7 @@
     Document* document,
     ExceptionState& exception_state) {
   if (options.IsNull()) {
-    return Timing::Defaults();
+    return Timing();
   }
 
   if (options.IsKeyframeEffectOptions()) {
@@ -86,9 +86,8 @@
     const UnrestrictedDoubleOrKeyframeAnimationOptions& options,
     Document* document,
     ExceptionState& exception_state) {
-  if (options.IsNull()) {
-    return Timing::Defaults();
-  }
+  if (options.IsNull())
+    return Timing();
 
   if (options.IsKeyframeAnimationOptions()) {
     return ConvertEffectTiming(options.GetAsKeyframeAnimationOptions(),
diff --git a/third_party/blink/renderer/core/core_idl_files.gni b/third_party/blink/renderer/core/core_idl_files.gni
index 4f3137d..589d59f 100644
--- a/third_party/blink/renderer/core/core_idl_files.gni
+++ b/third_party/blink/renderer/core/core_idl_files.gni
@@ -696,7 +696,11 @@
                     "timing/performance_mark_options.idl",
                     "timing/performance_measure_options.idl",
                     "timing/performance_observer_init.idl",
+                    "timing/profiler_frame.idl",
                     "timing/profiler_init_options.idl",
+                    "timing/profiler_sample.idl",
+                    "timing/profiler_stack.idl",
+                    "timing/profiler_trace.idl",
                     "trustedtypes/trusted_type_policy_options.idl",
                     "workers/worker_options.idl",
                     "workers/worklet_options.idl",
diff --git a/third_party/blink/renderer/core/css/css_default_style_sheets.cc b/third_party/blink/renderer/core/css/css_default_style_sheets.cc
index 089a92e..bcad862 100644
--- a/third_party/blink/renderer/core/css/css_default_style_sheets.cc
+++ b/third_party/blink/renderer/core/css/css_default_style_sheets.cc
@@ -52,13 +52,13 @@
 }
 
 static const MediaQueryEvaluator& ScreenEval() {
-  DEFINE_STATIC_LOCAL(Persistent<MediaQueryEvaluator>, static_screen_eval,
+  DEFINE_STATIC_LOCAL(const Persistent<MediaQueryEvaluator>, static_screen_eval,
                       (MakeGarbageCollected<MediaQueryEvaluator>("screen")));
   return *static_screen_eval;
 }
 
 static const MediaQueryEvaluator& PrintEval() {
-  DEFINE_STATIC_LOCAL(Persistent<MediaQueryEvaluator>, static_print_eval,
+  DEFINE_STATIC_LOCAL(const Persistent<MediaQueryEvaluator>, static_print_eval,
                       (MakeGarbageCollected<MediaQueryEvaluator>("print")));
   return *static_print_eval;
 }
diff --git a/third_party/blink/renderer/core/css/css_property_value_set.cc b/third_party/blink/renderer/core/css/css_property_value_set.cc
index 808cb0f..a628fec6 100644
--- a/third_party/blink/renderer/core/css/css_property_value_set.cc
+++ b/third_party/blink/renderer/core/css/css_property_value_set.cc
@@ -507,7 +507,7 @@
   property_vector_.clear();
 }
 
-inline bool ContainsId(const CSSProperty** set,
+inline bool ContainsId(const CSSProperty* const set[],
                        unsigned length,
                        CSSPropertyID id) {
   for (unsigned i = 0; i < length; ++i) {
@@ -517,8 +517,9 @@
   return false;
 }
 
-bool MutableCSSPropertyValueSet::RemovePropertiesInSet(const CSSProperty** set,
-                                                       unsigned length) {
+bool MutableCSSPropertyValueSet::RemovePropertiesInSet(
+    const CSSProperty* const set[],
+    unsigned length) {
   if (property_vector_.IsEmpty())
     return false;
 
@@ -663,7 +664,7 @@
 
 #ifndef NDEBUG
 void CSSPropertyValueSet::ShowStyle() {
-  fprintf(stderr, "%s\n", AsText().Ascii().data());
+  fprintf(stderr, "%s\n", AsText().Ascii().c_str());
 }
 #endif
 
diff --git a/third_party/blink/renderer/core/css/css_property_value_set.h b/third_party/blink/renderer/core/css/css_property_value_set.h
index 02f6019..a81a3bb 100644
--- a/third_party/blink/renderer/core/css/css_property_value_set.h
+++ b/third_party/blink/renderer/core/css/css_property_value_set.h
@@ -268,7 +268,7 @@
 
   template <typename T>  // CSSPropertyID or AtomicString
   bool RemoveProperty(T property, String* return_text = nullptr);
-  bool RemovePropertiesInSet(const CSSProperty** set, unsigned length);
+  bool RemovePropertiesInSet(const CSSProperty* const set[], unsigned length);
   void RemoveEquivalentProperties(const CSSPropertyValueSet*);
   void RemoveEquivalentProperties(const CSSStyleDeclaration*);
 
diff --git a/third_party/blink/renderer/core/css/css_selector.cc b/third_party/blink/renderer/core/css/css_selector.cc
index d68bd27..e441087e 100644
--- a/third_party/blink/renderer/core/css/css_selector.cc
+++ b/third_party/blink/renderer/core/css/css_selector.cc
@@ -461,19 +461,21 @@
 
 #ifndef NDEBUG
 void CSSSelector::Show(int indent) const {
-  printf("%*sSelectorText(): %s\n", indent, "", SelectorText().Ascii().data());
+  printf("%*sSelectorText(): %s\n", indent, "", SelectorText().Ascii().c_str());
   printf("%*smatch_: %d\n", indent, "", match_);
   if (match_ != kTag)
-    printf("%*sValue(): %s\n", indent, "", Value().Ascii().data());
+    printf("%*sValue(): %s\n", indent, "", Value().Ascii().c_str());
   printf("%*sGetPseudoType(): %d\n", indent, "", GetPseudoType());
-  if (match_ == kTag)
+  if (match_ == kTag) {
     printf("%*sTagQName().LocalName(): %s\n", indent, "",
-           TagQName().LocalName().Ascii().data());
+           TagQName().LocalName().Ascii().c_str());
+  }
   printf("%*sIsAttributeSelector(): %d\n", indent, "", IsAttributeSelector());
-  if (IsAttributeSelector())
+  if (IsAttributeSelector()) {
     printf("%*sAttribute(): %s\n", indent, "",
-           Attribute().LocalName().Ascii().data());
-  printf("%*sArgument(): %s\n", indent, "", Argument().Ascii().data());
+           Attribute().LocalName().Ascii().c_str());
+  }
+  printf("%*sArgument(): %s\n", indent, "", Argument().Ascii().c_str());
   printf("%*sSpecificity(): %u\n", indent, "", Specificity());
   if (TagHistory()) {
     printf("\n%*s--> (Relation() == %d)\n", indent, "", Relation());
@@ -485,7 +487,7 @@
 
 void CSSSelector::Show() const {
   printf("\n******* CSSSelector::Show(\"%s\") *******\n",
-         SelectorText().Ascii().data());
+         SelectorText().Ascii().c_str());
   Show(2);
   printf("******* end *******\n");
 }
diff --git a/third_party/blink/renderer/core/css/css_test_helpers.cc b/third_party/blink/renderer/core/css/css_test_helpers.cc
index f86d38bd..ccc601ba 100644
--- a/third_party/blink/renderer/core/css/css_test_helpers.cc
+++ b/third_party/blink/renderer/core/css/css_test_helpers.cc
@@ -47,7 +47,7 @@
   return rule_set;
 }
 
-void TestStyleSheet::AddCSSRules(const char* css_text, bool is_empty_sheet) {
+void TestStyleSheet::AddCSSRules(const String& css_text, bool is_empty_sheet) {
   TextPosition position;
   unsigned sheet_length = style_sheet_->length();
   style_sheet_->Contents()->ParseStringAtPosition(css_text, position);
diff --git a/third_party/blink/renderer/core/css/css_test_helpers.h b/third_party/blink/renderer/core/css/css_test_helpers.h
index cdb0e4a..fe8395c2 100644
--- a/third_party/blink/renderer/core/css/css_test_helpers.h
+++ b/third_party/blink/renderer/core/css/css_test_helpers.h
@@ -33,7 +33,7 @@
 
   const Document& GetDocument() { return *document_; }
 
-  void AddCSSRules(const char* rule_text, bool is_empty_sheet = false);
+  void AddCSSRules(const String& rule_text, bool is_empty_sheet = false);
   RuleSet& GetRuleSet();
   CSSRuleList* CssRules();
 
diff --git a/third_party/blink/renderer/core/css/invalidation/invalidation_set.cc b/third_party/blink/renderer/core/css/invalidation/invalidation_set.cc
index 483c6be..5769a5d 100644
--- a/third_party/blink/renderer/core/css/invalidation/invalidation_set.cc
+++ b/third_party/blink/renderer/core/css/invalidation/invalidation_set.cc
@@ -370,7 +370,7 @@
   value->BeginArray("InvalidationSet");
   ToTracedValue(value.get());
   value->EndArray();
-  fprintf(stderr, "%s\n", value->ToString().Ascii().data());
+  fprintf(stderr, "%s\n", value->ToString().Ascii().c_str());
 }
 #endif  // NDEBUG
 
diff --git a/third_party/blink/renderer/core/css/media_query_set_test.cc b/third_party/blink/renderer/core/css/media_query_set_test.cc
index abc7506b..603ae1d 100644
--- a/third_party/blink/renderer/core/css/media_query_set_test.cc
+++ b/third_party/blink/renderer/core/css/media_query_set_test.cc
@@ -28,9 +28,9 @@
     output.Append(", ");
   }
   if (test.output)
-    ASSERT_STREQ(test.output, output.ToString().Ascii().data());
+    ASSERT_EQ(test.output, output.ToString());
   else
-    ASSERT_STREQ(test.input, output.ToString().Ascii().data());
+    ASSERT_EQ(test.input, output.ToString());
 }
 
 TEST(MediaQuerySetTest, Basic) {
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
index 3d24654..603c7ae 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
@@ -41,14 +41,6 @@
     case CSSPropertyID::kPaddingLeft:
     case CSSPropertyID::kPaddingRight:
     case CSSPropertyID::kPaddingTop:
-    case CSSPropertyID::kScrollMarginBlockEnd:
-    case CSSPropertyID::kScrollMarginBlockStart:
-    case CSSPropertyID::kScrollMarginBottom:
-    case CSSPropertyID::kScrollMarginInlineEnd:
-    case CSSPropertyID::kScrollMarginInlineStart:
-    case CSSPropertyID::kScrollMarginLeft:
-    case CSSPropertyID::kScrollMarginRight:
-    case CSSPropertyID::kScrollMarginTop:
     case CSSPropertyID::kScrollPaddingBlockEnd:
     case CSSPropertyID::kScrollPaddingBlockStart:
     case CSSPropertyID::kScrollPaddingBottom:
diff --git a/third_party/blink/renderer/core/css/parser/css_tokenizer_test.cc b/third_party/blink/renderer/core/css/parser/css_tokenizer_test.cc
index c70effc..740c827 100644
--- a/third_party/blink/renderer/core/css/parser/css_tokenizer_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_tokenizer_test.cc
@@ -15,7 +15,7 @@
 #define TEST_TOKENS(string, ...)     \
   {                                  \
     String s = string;               \
-    SCOPED_TRACE(s.Ascii().data());  \
+    SCOPED_TRACE(s);                 \
     TestTokens(string, __VA_ARGS__); \
   }
 
diff --git a/third_party/blink/renderer/core/css/parser/media_condition_test.cc b/third_party/blink/renderer/core/css/parser/media_condition_test.cc
index eba2225..cc8668d 100644
--- a/third_party/blink/renderer/core/css/parser/media_condition_test.cc
+++ b/third_party/blink/renderer/core/css/parser/media_condition_test.cc
@@ -43,7 +43,7 @@
         MediaQueryParser::ParseMediaCondition(CSSParserTokenRange(tokens));
     ASSERT_EQ(media_condition_query_set->QueryVector().size(), (unsigned)1);
     String query_text = media_condition_query_set->QueryVector()[0]->CssText();
-    ASSERT_STREQ(test_cases[i].output, query_text.Ascii().data());
+    ASSERT_EQ(test_cases[i].output, query_text);
   }
 }
 
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index e2f60f43..a90aca7e 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -500,7 +500,7 @@
 
 const CSSValue* AnimationDelay::InitialValue() const {
   DEFINE_STATIC_LOCAL(
-      Persistent<CSSValue>, value,
+      const Persistent<CSSValue>, value,
       (CSSPrimitiveValue::Create(CSSTimingData::InitialDelay(),
                                  CSSPrimitiveValue::UnitType::kSeconds)));
   return value;
@@ -537,7 +537,7 @@
 }
 
 const CSSValue* AnimationDirection::InitialValue() const {
-  DEFINE_STATIC_LOCAL(Persistent<CSSValue>, value,
+  DEFINE_STATIC_LOCAL(const Persistent<CSSValue>, value,
                       (CSSIdentifierValue::Create(CSSValueID::kNormal)));
   return value;
 }
@@ -561,7 +561,7 @@
 
 const CSSValue* AnimationDuration::InitialValue() const {
   DEFINE_STATIC_LOCAL(
-      Persistent<CSSValue>, value,
+      const Persistent<CSSValue>, value,
       (CSSPrimitiveValue::Create(CSSTimingData::InitialDuration(),
                                  CSSPrimitiveValue::UnitType::kSeconds)));
   return value;
@@ -598,7 +598,7 @@
 }
 
 const CSSValue* AnimationFillMode::InitialValue() const {
-  DEFINE_STATIC_LOCAL(Persistent<CSSValue>, value,
+  DEFINE_STATIC_LOCAL(const Persistent<CSSValue>, value,
                       (CSSIdentifierValue::Create(CSSValueID::kNone)));
   return value;
 }
@@ -633,7 +633,7 @@
 
 const CSSValue* AnimationIterationCount::InitialValue() const {
   DEFINE_STATIC_LOCAL(
-      Persistent<CSSValue>, value,
+      const Persistent<CSSValue>, value,
       (CSSPrimitiveValue::Create(CSSAnimationData::InitialIterationCount(),
                                  CSSPrimitiveValue::UnitType::kNumber)));
   return value;
@@ -669,7 +669,7 @@
 }
 
 const CSSValue* AnimationName::InitialValue() const {
-  DEFINE_STATIC_LOCAL(Persistent<CSSValue>, value,
+  DEFINE_STATIC_LOCAL(const Persistent<CSSValue>, value,
                       (CSSIdentifierValue::Create(CSSValueID::kNone)));
   return value;
 }
@@ -704,7 +704,7 @@
 }
 
 const CSSValue* AnimationPlayState::InitialValue() const {
-  DEFINE_STATIC_LOCAL(Persistent<CSSValue>, value,
+  DEFINE_STATIC_LOCAL(const Persistent<CSSValue>, value,
                       (CSSIdentifierValue::Create(CSSValueID::kRunning)));
   return value;
 }
@@ -728,7 +728,7 @@
 }
 
 const CSSValue* AnimationTimingFunction::InitialValue() const {
-  DEFINE_STATIC_LOCAL(Persistent<CSSValue>, value,
+  DEFINE_STATIC_LOCAL(const Persistent<CSSValue>, value,
                       (CSSIdentifierValue::Create(CSSValueID::kEase)));
   return value;
 }
@@ -1197,7 +1197,7 @@
 
 const CSSValue* BorderImageOutset::InitialValue() const {
   DEFINE_STATIC_LOCAL(
-      Persistent<CSSQuadValue>, value,
+      const Persistent<CSSQuadValue>, value,
       (CSSQuadValue::Create(
           CSSPrimitiveValue::Create(0, CSSPrimitiveValue::UnitType::kInteger),
           CSSQuadValue::kSerializeAsQuad)));
@@ -1221,7 +1221,7 @@
 }
 
 const CSSValue* BorderImageRepeat::InitialValue() const {
-  DEFINE_STATIC_LOCAL(Persistent<CSSValue>, value,
+  DEFINE_STATIC_LOCAL(const Persistent<CSSValue>, value,
                       (CSSIdentifierValue::Create(CSSValueID::kStretch)));
   return value;
 }
@@ -1245,7 +1245,7 @@
 
 const CSSValue* BorderImageSlice::InitialValue() const {
   DEFINE_STATIC_LOCAL(
-      Persistent<CSSBorderImageSliceValue>, value,
+      const Persistent<CSSBorderImageSliceValue>, value,
       (MakeGarbageCollected<CSSBorderImageSliceValue>(
           CSSQuadValue::Create(
               CSSPrimitiveValue::Create(
@@ -1274,7 +1274,7 @@
 }
 
 const CSSValue* BorderImageSource::InitialValue() const {
-  DEFINE_STATIC_LOCAL(Persistent<CSSValue>, value,
+  DEFINE_STATIC_LOCAL(const Persistent<CSSValue>, value,
                       (CSSIdentifierValue::Create(CSSValueID::kNone)));
   return value;
 }
@@ -1304,7 +1304,7 @@
 
 const CSSValue* BorderImageWidth::InitialValue() const {
   DEFINE_STATIC_LOCAL(
-      Persistent<CSSQuadValue>, value,
+      const Persistent<CSSQuadValue>, value,
       (CSSQuadValue::Create(
           CSSPrimitiveValue::Create(1, CSSPrimitiveValue::UnitType::kInteger),
           CSSQuadValue::kSerializeAsQuad)));
@@ -6672,7 +6672,7 @@
 
 const CSSValue* TransitionDelay::InitialValue() const {
   DEFINE_STATIC_LOCAL(
-      Persistent<CSSValue>, value,
+      const Persistent<CSSValue>, value,
       (CSSPrimitiveValue::Create(CSSTimingData::InitialDelay(),
                                  CSSPrimitiveValue::UnitType::kSeconds)));
   return value;
@@ -6697,7 +6697,7 @@
 
 const CSSValue* TransitionDuration::InitialValue() const {
   DEFINE_STATIC_LOCAL(
-      Persistent<CSSValue>, value,
+      const Persistent<CSSValue>, value,
       (CSSPrimitiveValue::Create(CSSTimingData::InitialDuration(),
                                  CSSPrimitiveValue::UnitType::kSeconds)));
   return value;
@@ -6724,7 +6724,7 @@
 }
 
 const CSSValue* TransitionProperty::InitialValue() const {
-  DEFINE_STATIC_LOCAL(Persistent<CSSValue>, value,
+  DEFINE_STATIC_LOCAL(const Persistent<CSSValue>, value,
                       (CSSIdentifierValue::Create(CSSValueID::kAll)));
   return value;
 }
@@ -6748,7 +6748,7 @@
 }
 
 const CSSValue* TransitionTimingFunction::InitialValue() const {
-  DEFINE_STATIC_LOCAL(Persistent<CSSValue>, value,
+  DEFINE_STATIC_LOCAL(const Persistent<CSSValue>, value,
                       (CSSIdentifierValue::Create(CSSValueID::kEase)));
   return value;
 }
diff --git a/third_party/blink/renderer/core/css/rule_set_test.cc b/third_party/blink/renderer/core/css/rule_set_test.cc
index 474ae9a..5959b54 100644
--- a/third_party/blink/renderer/core/css/rule_set_test.cc
+++ b/third_party/blink/renderer/core/css/rule_set_test.cc
@@ -361,7 +361,7 @@
   builder.Append("b,span {}");
 
   TestStyleSheet sheet;
-  sheet.AddCSSRules(builder.ToString().Ascii().data());
+  sheet.AddCSSRules(builder.ToString());
   const RuleSet& rule_set = sheet.GetRuleSet();
   const HeapVector<Member<const RuleData>>* rules = rule_set.TagRules("b");
   ASSERT_EQ(1u, rules->size());
diff --git a/third_party/blink/renderer/core/css/style_property_serializer.cc b/third_party/blink/renderer/core/css/style_property_serializer.cc
index f2a88bb9..0d77885 100644
--- a/third_party/blink/renderer/core/css/style_property_serializer.cc
+++ b/third_party/blink/renderer/core/css/style_property_serializer.cc
@@ -1094,7 +1094,8 @@
                                         const CSSValue& repeat_xcss_value,
                                         const CSSValue& repeat_ycss_value) {
   // FIXME: Ensure initial values do not appear in CSS_VALUE_LISTS.
-  DEFINE_STATIC_LOCAL(Persistent<CSSIdentifierValue>, initial_repeat_value,
+  DEFINE_STATIC_LOCAL(const Persistent<CSSIdentifierValue>,
+                      initial_repeat_value,
                       (CSSIdentifierValue::Create(CSSValueID::kRepeat)));
   const CSSIdentifierValue& repeat_x =
       repeat_xcss_value.IsInitialValue()
diff --git a/third_party/blink/renderer/core/css/style_property_shorthand_custom.cc b/third_party/blink/renderer/core/css/style_property_shorthand_custom.cc
index e6a15cb..a3b9cdd 100644
--- a/third_party/blink/renderer/core/css/style_property_shorthand_custom.cc
+++ b/third_party/blink/renderer/core/css/style_property_shorthand_custom.cc
@@ -44,9 +44,10 @@
       &GetCSSPropertyAnimationFillMode(),
       &GetCSSPropertyAnimationPlayState(),
       &GetCSSPropertyAnimationName()};
-  static StylePropertyShorthand webkit_animation_longhands_for_parsing(
-      CSSPropertyID::kAnimation, kAnimationPropertiesForParsing,
-      base::size(kAnimationPropertiesForParsing));
+  static constexpr StylePropertyShorthand
+      webkit_animation_longhands_for_parsing(
+          CSSPropertyID::kAnimation, kAnimationPropertiesForParsing,
+          base::size(kAnimationPropertiesForParsing));
   return webkit_animation_longhands_for_parsing;
 }
 
diff --git a/third_party/blink/renderer/core/dom/container_node.cc b/third_party/blink/renderer/core/dom/container_node.cc
index 21aef573..84f1ad7 100644
--- a/third_party/blink/renderer/core/dom/container_node.cc
+++ b/third_party/blink/renderer/core/dom/container_node.cc
@@ -1019,12 +1019,10 @@
     AppendChild(child.Clone(GetDocument(), CloneChildrenFlag::kClone));
 }
 
-LayoutRect ContainerNode::BoundingBox() const {
+PhysicalRect ContainerNode::BoundingBox() const {
   if (!GetLayoutObject())
-    return LayoutRect();
-  return GetLayoutObject()
-      ->AbsoluteBoundingBoxRectHandlingEmptyInline()
-      .ToLayoutRect();
+    return PhysicalRect();
+  return GetLayoutObject()->AbsoluteBoundingBoxRectHandlingEmptyInline();
 }
 
 // This is used by FrameSelection to denote when the active-state of the page
diff --git a/third_party/blink/renderer/core/dom/container_node.h b/third_party/blink/renderer/core/dom/container_node.h
index 1f0cb174..93340ac 100644
--- a/third_party/blink/renderer/core/dom/container_node.h
+++ b/third_party/blink/renderer/core/dom/container_node.h
@@ -150,7 +150,7 @@
 
   void AttachLayoutTree(AttachContext&) override;
   void DetachLayoutTree(bool performing_reattach = false) override;
-  LayoutRect BoundingBox() const final;
+  PhysicalRect BoundingBox() const final;
   void SetFocused(bool, WebFocusType) override;
   void SetHasFocusWithinUpToAncestor(bool, Node* ancestor);
   void FocusStateChanged();
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 845bbc3..69db86bf 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -4301,7 +4301,7 @@
 
 MouseEventWithHitTestResults Document::PerformMouseEventHitTest(
     const HitTestRequest& request,
-    const LayoutPoint& document_point,
+    const PhysicalOffset& document_point,
     const WebMouseEvent& event) {
   DCHECK(!GetLayoutView() || GetLayoutView()->IsLayoutView());
 
@@ -4312,7 +4312,7 @@
   // lead to a premature layout() happening, which could show a flash of white.
   // See also the similar code in EventHandler::hitTestResultAtPoint.
   if (!GetLayoutView() || !View() || !View()->DidFirstLayout()) {
-    HitTestLocation location((LayoutPoint()));
+    HitTestLocation location((PhysicalOffset()));
     return MouseEventWithHitTestResults(event, location,
                                         HitTestResult(request, location));
   }
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 080f2f4..fcc62c0 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -144,7 +144,6 @@
 class HttpRefreshScheduler;
 class IdleRequestOptions;
 class IntersectionObserverController;
-class LayoutPoint;
 class LayoutView;
 class LazyLoadImageObserver;
 class LiveNodeListBase;
@@ -200,6 +199,7 @@
 struct AnnotatedRegionValue;
 struct FocusParams;
 struct IconURL;
+struct PhysicalOffset;
 
 using MouseEventWithHitTestResults = EventWithHitTestResults<WebMouseEvent>;
 
@@ -765,7 +765,7 @@
   VisitedLinkState& GetVisitedLinkState() const { return *visited_link_state_; }
 
   MouseEventWithHitTestResults PerformMouseEventHitTest(const HitTestRequest&,
-                                                        const LayoutPoint&,
+                                                        const PhysicalOffset&,
                                                         const WebMouseEvent&);
 
   void SetHadKeyboardEvent(bool had_keyboard_event) {
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index d055e4a..8fc22c1d 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -737,7 +737,7 @@
       ToPhysicalAlignment(options, kVerticalScroll, is_horizontal_writing_mode,
                           is_flipped_blocks_mode);
 
-  LayoutRect bounds = BoundingBoxForScrollIntoView();
+  PhysicalRect bounds = BoundingBoxForScrollIntoView();
   GetLayoutObject()->ScrollRectToVisible(
       bounds, {align_x, align_y, kProgrammaticScroll,
                /*make_visible_in_visual_viewport=*/true, behavior});
@@ -751,7 +751,7 @@
   if (!GetLayoutObject())
     return;
 
-  LayoutRect bounds = BoundingBoxForScrollIntoView();
+  PhysicalRect bounds = BoundingBoxForScrollIntoView();
   if (center_if_needed) {
     GetLayoutObject()->ScrollRectToVisible(
         bounds, {ScrollAlignment::kAlignCenterIfNeeded,
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 32762b5..ae893ca 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -951,20 +951,18 @@
              : nullptr;
 }
 
-LayoutRect Node::BoundingBox() const {
+PhysicalRect Node::BoundingBox() const {
   if (GetLayoutObject())
-    return LayoutRect(GetLayoutObject()->AbsoluteBoundingBoxRect());
-  return LayoutRect();
+    return PhysicalRect(GetLayoutObject()->AbsoluteBoundingBoxRect());
+  return PhysicalRect();
 }
 
-LayoutRect Node::BoundingBoxForScrollIntoView() const {
+PhysicalRect Node::BoundingBoxForScrollIntoView() const {
   if (GetLayoutObject()) {
-    return GetLayoutObject()
-        ->AbsoluteBoundingBoxRectForScrollIntoView()
-        .ToLayoutRect();
+    return GetLayoutObject()->AbsoluteBoundingBoxRectForScrollIntoView();
   }
 
-  return LayoutRect();
+  return PhysicalRect();
 }
 
 Node& Node::ShadowIncludingRoot() const {
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h
index 11c60822..5dffa89 100644
--- a/third_party/blink/renderer/core/dom/node.h
+++ b/third_party/blink/renderer/core/dom/node.h
@@ -34,9 +34,9 @@
 #include "third_party/blink/renderer/core/dom/mutation_observer_options.h"
 #include "third_party/blink/renderer/core/dom/node_rare_data.h"
 #include "third_party/blink/renderer/core/dom/tree_scope.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
 #include "third_party/blink/renderer/core/scroll/scroll_customization.h"
 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
-#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
 
 // This needs to be here because element.cc also depends on it.
 #define DUMP_NODE_STATISTICS 0
@@ -580,7 +580,7 @@
   // inert to prevent text selection.
   bool IsInert() const;
 
-  virtual LayoutRect BoundingBox() const;
+  virtual PhysicalRect BoundingBox() const;
   IntRect PixelSnappedBoundingBox() const {
     return PixelSnappedIntRect(BoundingBox());
   }
@@ -588,7 +588,7 @@
   // BoundingBoxForScrollIntoView() is the node's scroll snap area.
   // It is expanded from the BoundingBox() by scroll-margin.
   // https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-area
-  LayoutRect BoundingBoxForScrollIntoView() const;
+  PhysicalRect BoundingBoxForScrollIntoView() const;
 
   unsigned NodeIndex() const;
 
diff --git a/third_party/blink/renderer/core/editing/editing_style.cc b/third_party/blink/renderer/core/editing/editing_style.cc
index 82c6495..b29d5db 100644
--- a/third_party/blink/renderer/core/editing/editing_style.cc
+++ b/third_party/blink/renderer/core/editing/editing_style.cc
@@ -703,7 +703,7 @@
     CSSPropertyID::kTextAlignLast, CSSPropertyID::kTextIndent,
     CSSPropertyID::kTextJustify, CSSPropertyID::kWidows};
 
-static Vector<const CSSProperty*>& BlockPropertiesVector() {
+static const Vector<const CSSProperty*>& BlockPropertiesVector() {
   DEFINE_STATIC_LOCAL(Vector<const CSSProperty*>, properties, ());
   if (properties.IsEmpty()) {
     CSSProperty::FilterEnabledCSSPropertiesIntoVector(
diff --git a/third_party/blink/renderer/core/editing/editing_utilities.cc b/third_party/blink/renderer/core/editing/editing_utilities.cc
index 9ecbe1f..38509593 100644
--- a/third_party/blink/renderer/core/editing/editing_utilities.cc
+++ b/third_party/blink/renderer/core/editing/editing_utilities.cc
@@ -1311,30 +1311,32 @@
 
 PositionWithAffinity PositionRespectingEditingBoundary(
     const Position& position,
-    const LayoutPoint& local_point,
+    const PhysicalOffset& local_point,
     Node* target_node) {
-  if (!target_node->GetLayoutObject())
+  const LayoutObject* target_object = target_node->GetLayoutObject();
+  if (!target_object)
     return PositionWithAffinity();
 
-  LayoutPoint selection_end_point = local_point;
+  PhysicalOffset selection_end_point = local_point;
   Element* editable_element = RootEditableElementOf(position);
 
   if (editable_element && !editable_element->contains(target_node)) {
-    if (!editable_element->GetLayoutObject())
+    const LayoutObject* editable_object = editable_element->GetLayoutObject();
+    if (!editable_object)
       return PositionWithAffinity();
 
-    // TODO(yosin): This kIgnoreTransforms correct here?
-    PhysicalOffset absolute_point =
-        target_node->GetLayoutObject()->LocalToAbsolutePoint(
-            PhysicalOffsetToBeNoop(selection_end_point), kIgnoreTransforms);
-    selection_end_point =
-        editable_element->GetLayoutObject()
-            ->AbsoluteToLocalPoint(absolute_point, kIgnoreTransforms)
-            .ToLayoutPoint();
-    target_node = editable_element;
+    // TODO(yosin): Is this kIgnoreTransforms correct here?
+    PhysicalOffset absolute_point = target_object->LocalToAbsolutePoint(
+        selection_end_point, kIgnoreTransforms);
+    selection_end_point = editable_object->AbsoluteToLocalPoint(
+        absolute_point, kIgnoreTransforms);
+    target_object = editable_object;
   }
 
-  return target_node->GetLayoutObject()->PositionForPoint(selection_end_point);
+  // TODO(wangxianzhu): LayoutObject::PositionForPoint() still expects flipped
+  // coordinates.
+  return target_object->PositionForPoint(
+      target_object->FlipForWritingMode(selection_end_point));
 }
 
 Position ComputePositionForNodeRemoval(const Position& position,
diff --git a/third_party/blink/renderer/core/editing/editing_utilities.h b/third_party/blink/renderer/core/editing/editing_utilities.h
index 2b99881..e2e99928 100644
--- a/third_party/blink/renderer/core/editing/editing_utilities.h
+++ b/third_party/blink/renderer/core/editing/editing_utilities.h
@@ -256,7 +256,7 @@
 
 PositionWithAffinity PositionRespectingEditingBoundary(
     const Position&,
-    const LayoutPoint& local_point,
+    const PhysicalOffset& local_point,
     Node* target_node);
 Position ComputePositionForNodeRemoval(const Position&, const Node&);
 
diff --git a/third_party/blink/renderer/core/editing/editor.cc b/third_party/blink/renderer/core/editing/editor.cc
index 1ec394b..f6ed360b 100644
--- a/third_party/blink/renderer/core/editing/editor.cc
+++ b/third_party/blink/renderer/core/editing/editor.cc
@@ -677,7 +677,7 @@
 
 EphemeralRange Editor::RangeForPoint(const IntPoint& frame_point) const {
   const PositionWithAffinity position_with_affinity =
-      GetFrame().PositionForPoint(frame_point);
+      GetFrame().PositionForPoint(PhysicalOffset(frame_point));
   if (position_with_affinity.IsNull())
     return EphemeralRange();
 
diff --git a/third_party/blink/renderer/core/editing/finder/text_finder.cc b/third_party/blink/renderer/core/editing/finder/text_finder.cc
index 4b604b04..98c941c 100644
--- a/third_party/blink/renderer/core/editing/finder/text_finder.cc
+++ b/third_party/blink/renderer/core/editing/finder/text_finder.cc
@@ -88,7 +88,7 @@
   ScrollBehavior scroll_behavior =
       smooth_find_enabled ? kScrollBehaviorSmooth : kScrollBehaviorAuto;
   first_node.GetLayoutObject()->ScrollRectToVisible(
-      LayoutRect(match->BoundingBox()),
+      PhysicalRect(match->BoundingBox()),
       WebScrollIntoViewParams(
           ScrollAlignment::kAlignCenterIfNeeded,
           ScrollAlignment::kAlignCenterIfNeeded, kUserScroll,
@@ -621,7 +621,7 @@
     if (active_match_->FirstNode() &&
         active_match_->FirstNode()->GetLayoutObject()) {
       active_match_->FirstNode()->GetLayoutObject()->ScrollRectToVisible(
-          LayoutRect(active_match_bounding_box),
+          PhysicalRect(active_match_bounding_box),
           WebScrollIntoViewParams(ScrollAlignment::kAlignCenterIfNeeded,
                                   ScrollAlignment::kAlignCenterIfNeeded,
                                   kUserScroll));
diff --git a/third_party/blink/renderer/core/editing/frame_selection.cc b/third_party/blink/renderer/core/editing/frame_selection.cc
index 302f6ee4..dd51aa0 100644
--- a/third_party/blink/renderer/core/editing/frame_selection.cc
+++ b/third_party/blink/renderer/core/editing/frame_selection.cc
@@ -568,7 +568,7 @@
   frame_caret_->PaintCaret(context, paint_offset);
 }
 
-bool FrameSelection::Contains(const LayoutPoint& point) {
+bool FrameSelection::Contains(const PhysicalOffset& point) {
   if (!GetDocument().GetLayoutView())
     return false;
 
@@ -596,7 +596,10 @@
 
   const PositionInFlatTreeWithAffinity pos_with_affinity =
       FromPositionInDOMTree<EditingInFlatTreeStrategy>(
-          inner_node->GetLayoutObject()->PositionForPoint(result.LocalPoint()));
+          // TODO(wangxianzhu): PositionForPoint still requires flipped
+          // coordinates.
+          inner_node->GetLayoutObject()->PositionForPoint(
+              result.FlippedLocalPoint()));
   if (pos_with_affinity.IsNull())
     return false;
 
@@ -960,15 +963,15 @@
                  .Build());
 }
 
-LayoutRect FrameSelection::AbsoluteUnclippedBounds() const {
+PhysicalRect FrameSelection::AbsoluteUnclippedBounds() const {
   LocalFrameView* view = frame_->View();
   LayoutView* layout_view = frame_->ContentLayoutObject();
 
   if (!view || !layout_view)
-    return LayoutRect();
+    return PhysicalRect();
 
   view->UpdateLifecycleToLayoutClean();
-  return LayoutRect(layout_selection_->AbsoluteSelectionBounds());
+  return PhysicalRect(layout_selection_->AbsoluteSelectionBounds());
 }
 
 IntRect FrameSelection::ComputeRectToScroll(
@@ -1009,9 +1012,8 @@
   // This function is needed to make sure that ComputeRectToScroll below has the
   // sticky offset info available before the computation.
   GetDocument().EnsurePaintLocationDataValidForNode(start.AnchorNode());
-  LayoutRect selection_rect =
-      LayoutRect(ComputeRectToScroll(reveal_extent_option));
-  if (selection_rect == LayoutRect() ||
+  PhysicalRect selection_rect(ComputeRectToScroll(reveal_extent_option));
+  if (selection_rect == PhysicalRect() ||
       !start.AnchorNode()->GetLayoutObject()->EnclosingBox())
     return;
 
diff --git a/third_party/blink/renderer/core/editing/frame_selection.h b/third_party/blink/renderer/core/editing/frame_selection.h
index 91da03de..2478f729 100644
--- a/third_party/blink/renderer/core/editing/frame_selection.h
+++ b/third_party/blink/renderer/core/editing/frame_selection.h
@@ -60,6 +60,7 @@
 class TextIteratorBehavior;
 struct PaintInvalidatorContext;
 struct PhysicalOffset;
+struct PhysicalRect;
 
 enum RevealExtentOption { kRevealExtent, kDoNotRevealExtent };
 
@@ -172,7 +173,7 @@
   // the frame you entirely selected.
   void SelectFrameElementInParentIfFullySelected();
 
-  bool Contains(const LayoutPoint&);
+  bool Contains(const PhysicalOffset&);
 
   bool Modify(SelectionModifyAlteration,
               SelectionModifyDirection,
@@ -202,6 +203,10 @@
   // Note: this updates styles and layout, use cautiously.
   bool ComputeAbsoluteBounds(IntRect& anchor, IntRect& focus) const;
 
+  // Computes the rect we should use when scrolling/zooming a selection into
+  // view.
+  IntRect ComputeRectToScroll(RevealExtentOption);
+
   void DidChangeFocus();
 
   SelectionInDOMTree GetSelectionInDOMTree() const;
@@ -254,7 +259,7 @@
 
   // This returns last layouted selection bounds of LayoutSelection rather than
   // SelectionEditor keeps.
-  LayoutRect AbsoluteUnclippedBounds() const;
+  PhysicalRect AbsoluteUnclippedBounds() const;
 
   // TODO(tkent): This function has a bug that scrolling doesn't work well in
   // a case of RangeSelection. crbug.com/443061
@@ -300,8 +305,6 @@
 
   GranularityStrategy* GetGranularityStrategy();
 
-  IntRect ComputeRectToScroll(RevealExtentOption);
-
   void MoveRangeSelectionInternal(const SelectionInDOMTree&, TextGranularity);
 
   // Implementation of |SynchronousMutationObserver| member functions.
diff --git a/third_party/blink/renderer/core/editing/frame_selection_test.cc b/third_party/blink/renderer/core/editing/frame_selection_test.cc
index 444efad..37efa61 100644
--- a/third_party/blink/renderer/core/editing/frame_selection_test.cc
+++ b/third_party/blink/renderer/core/editing/frame_selection_test.cc
@@ -24,6 +24,7 @@
 #include "third_party/blink/renderer/core/paint/paint_info.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
+#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
@@ -1055,7 +1056,7 @@
   const int node_margin_top = 2;
   // The top of the node should be visible but the bottom should be outside
   // by the viewport. The unclipped selection bounds should not be clipped.
-  EXPECT_EQ(LayoutRect(0, node_margin_top, node_width, node_height),
+  EXPECT_EQ(PhysicalRect(0, node_margin_top, node_width, node_height),
             Selection().AbsoluteUnclippedBounds());
 
   // Scroll 500px down so the top of the node is outside the viewport and the
@@ -1064,14 +1065,14 @@
   LocalFrameView* frame_view = GetDocument().View();
   frame_view->LayoutViewport()->SetScrollOffset(ScrollOffset(0, scroll_offset),
                                                 kProgrammaticScroll);
-  EXPECT_EQ(LayoutRect(0, node_margin_top, node_width, node_height),
+  EXPECT_EQ(PhysicalRect(0, node_margin_top, node_width, node_height),
             frame_view->FrameToDocument(Selection().AbsoluteUnclippedBounds()));
 
   // Adjust the page scale factor which changes the selection bounds as seen
   // through the viewport. The unclipped selection bounds should not be clipped.
   const int page_scale_factor = 2;
   GetPage().SetPageScaleFactor(page_scale_factor);
-  EXPECT_EQ(LayoutRect(0, node_margin_top, node_width, node_height),
+  EXPECT_EQ(PhysicalRect(0, node_margin_top, node_width, node_height),
             frame_view->FrameToDocument(Selection().AbsoluteUnclippedBounds()));
 }
 
@@ -1083,11 +1084,11 @@
       SetSelectionOptions());
 
   // Check the right half of 'c'
-  const LayoutPoint c_right(35, 13);
+  const PhysicalOffset c_right(35, 13);
   EXPECT_TRUE(Selection().Contains(c_right));
 
   // Check the left half of "F"
-  const LayoutPoint f_left(45, 13);
+  const PhysicalOffset f_left(45, 13);
   EXPECT_TRUE(Selection().Contains(f_left));
 }
 
diff --git a/third_party/blink/renderer/core/editing/markers/text_match_marker.cc b/third_party/blink/renderer/core/editing/markers/text_match_marker.cc
index 797cf3c2..276a5f1 100644
--- a/third_party/blink/renderer/core/editing/markers/text_match_marker.cc
+++ b/third_party/blink/renderer/core/editing/markers/text_match_marker.cc
@@ -27,21 +27,21 @@
   return layout_status_ == LayoutStatus::kValidNotNull;
 }
 
-bool TextMatchMarker::Contains(const LayoutPoint& point) const {
+bool TextMatchMarker::Contains(const PhysicalOffset& point) const {
   DCHECK_EQ(layout_status_, LayoutStatus::kValidNotNull);
-  return layout_rect_.Contains(point);
+  return rect_.Contains(point);
 }
 
-void TextMatchMarker::SetLayoutRect(const LayoutRect& rect) {
-  if (layout_status_ == LayoutStatus::kValidNotNull && rect == layout_rect_)
+void TextMatchMarker::SetRect(const PhysicalRect& rect) {
+  if (layout_status_ == LayoutStatus::kValidNotNull && rect == rect_)
     return;
   layout_status_ = LayoutStatus::kValidNotNull;
-  layout_rect_ = rect;
+  rect_ = rect;
 }
 
-const LayoutRect& TextMatchMarker::GetLayoutRect() const {
+const PhysicalRect& TextMatchMarker::GetRect() const {
   DCHECK_EQ(layout_status_, LayoutStatus::kValidNotNull);
-  return layout_rect_;
+  return rect_;
 }
 
 void TextMatchMarker::NullifyLayoutRect() {
diff --git a/third_party/blink/renderer/core/editing/markers/text_match_marker.h b/third_party/blink/renderer/core/editing/markers/text_match_marker.h
index 88b0acb..5920fac 100644
--- a/third_party/blink/renderer/core/editing/markers/text_match_marker.h
+++ b/third_party/blink/renderer/core/editing/markers/text_match_marker.h
@@ -28,7 +28,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_MARKERS_TEXT_MATCH_MARKER_H_
 
 #include "third_party/blink/renderer/core/editing/markers/document_marker.h"
-#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
 
 namespace blink {
@@ -54,9 +54,9 @@
   void SetIsActiveMatch(bool active);
 
   bool IsRendered() const;
-  bool Contains(const LayoutPoint&) const;
-  void SetLayoutRect(const LayoutRect&);
-  const LayoutRect& GetLayoutRect() const;
+  bool Contains(const PhysicalOffset&) const;
+  void SetRect(const PhysicalRect&);
+  const PhysicalRect& GetRect() const;
   void NullifyLayoutRect();
 
   void Invalidate();
@@ -65,7 +65,7 @@
  private:
   MatchStatus match_status_;
   LayoutStatus layout_status_ = LayoutStatus::kInvalid;
-  LayoutRect layout_rect_;
+  PhysicalRect rect_;
 
   DISALLOW_COPY_AND_ASSIGN(TextMatchMarker);
 };
diff --git a/third_party/blink/renderer/core/editing/markers/text_match_marker_list_impl.cc b/third_party/blink/renderer/core/editing/markers/text_match_marker_list_impl.cc
index 6842d1e..c1b3d44 100644
--- a/third_party/blink/renderer/core/editing/markers/text_match_marker_list_impl.cc
+++ b/third_party/blink/renderer/core/editing/markers/text_match_marker_list_impl.cc
@@ -85,8 +85,8 @@
   LocalFrameView* frame_view = node.GetDocument().GetFrame()->View();
 
   DCHECK(frame_view);
-  marker.SetLayoutRect(
-      frame_view->FrameToDocument(LayoutRect(ComputeTextRect(range))));
+  marker.SetRect(
+      frame_view->FrameToDocument(PhysicalRect(ComputeTextRect(range))));
 }
 
 Vector<IntRect> TextMatchMarkerListImpl::LayoutRects(const Node& node) const {
@@ -98,7 +98,7 @@
       UpdateMarkerLayoutRect(node, *text_match_marker);
     if (!text_match_marker->IsRendered())
       continue;
-    result.push_back(PixelSnappedIntRect(text_match_marker->GetLayoutRect()));
+    result.push_back(PixelSnappedIntRect(text_match_marker->GetRect()));
   }
 
   return result;
diff --git a/third_party/blink/renderer/core/editing/selection_controller.cc b/third_party/blink/renderer/core/editing/selection_controller.cc
index 997a6086..cf5196c 100644
--- a/third_party/blink/renderer/core/editing/selection_controller.cc
+++ b/third_party/blink/renderer/core/editing/selection_controller.cc
@@ -124,7 +124,7 @@
     const HitTestResult& hit_test_result) {
   return FromPositionInDOMTree<EditingInFlatTreeStrategy>(
       hit_test_result.InnerNode()->GetLayoutObject()->PositionForPoint(
-          hit_test_result.LocalPoint()));
+          hit_test_result.FlippedLocalPoint()));
 }
 
 DocumentMarker* SpellCheckMarkerAtPosition(
@@ -335,8 +335,8 @@
   // Don't restart the selection when the mouse is pressed on an
   // existing selection so we can allow for text dragging.
   if (LocalFrameView* view = frame_->View()) {
-    const LayoutPoint v_point = view->ConvertFromRootFrame(
-        FlooredIntPoint(event.Event().PositionInRootFrame()));
+    const PhysicalOffset v_point(view->ConvertFromRootFrame(
+        FlooredIntPoint(event.Event().PositionInRootFrame())));
     if (!extend_selection && this->Selection().Contains(v_point)) {
       mouse_down_was_single_click_in_selection_ = true;
       if (!event.Event().FromTouch())
@@ -472,8 +472,8 @@
 
 void SelectionController::UpdateSelectionForMouseDrag(
     const HitTestResult& hit_test_result,
-    const LayoutPoint& drag_start_pos,
-    const LayoutPoint& last_known_mouse_position) {
+    const PhysicalOffset& drag_start_pos,
+    const PhysicalOffset& last_known_mouse_position) {
   if (!mouse_down_may_start_select_)
     return;
 
@@ -623,9 +623,10 @@
   // and isn't desirable for touch).
   HitTestResult adjusted_hit_test_result = result;
   if (select_input_event_type == SelectInputEventType::kTouch &&
-      result.GetImage())
+      result.GetImage()) {
     adjusted_hit_test_result.SetNodeAndPosition(result.InnerNode(),
-                                                LayoutPoint(0, 0));
+                                                PhysicalOffset());
+  }
 
   const PositionInFlatTreeWithAffinity pos =
       CreateVisiblePosition(
@@ -1014,8 +1015,8 @@
 void SelectionController::HandleMouseDraggedEvent(
     const MouseEventWithHitTestResults& event,
     const IntPoint& mouse_down_pos,
-    const LayoutPoint& drag_start_pos,
-    const LayoutPoint& last_known_mouse_position) {
+    const PhysicalOffset& drag_start_pos,
+    const PhysicalOffset& last_known_mouse_position) {
   TRACE_EVENT0("blink", "SelectionController::handleMouseDraggedEvent");
 
   if (!Selection().IsAvailable())
@@ -1034,8 +1035,8 @@
 }
 
 void SelectionController::UpdateSelectionForMouseDrag(
-    const LayoutPoint& drag_start_pos,
-    const LayoutPoint& last_known_mouse_position) {
+    const PhysicalOffset& drag_start_pos,
+    const PhysicalOffset& last_known_mouse_position) {
   LocalFrameView* view = frame_->View();
   if (!view)
     return;
@@ -1054,7 +1055,7 @@
 
 bool SelectionController::HandleMouseReleaseEvent(
     const MouseEventWithHitTestResults& event,
-    const LayoutPoint& drag_start_pos) {
+    const PhysicalOffset& drag_start_pos) {
   TRACE_EVENT0("blink", "SelectionController::handleMouseReleaseEvent");
 
   if (!Selection().IsAvailable())
@@ -1068,7 +1069,8 @@
   // editing, place the caret.
   if (mouse_down_was_single_click_in_selection_ &&
       selection_state_ != SelectionState::kExtendedSelection &&
-      drag_start_pos == FlooredIntPoint(event.Event().PositionInRootFrame()) &&
+      drag_start_pos == PhysicalOffset(FlooredIntPoint(
+                            event.Event().PositionInRootFrame())) &&
       Selection().ComputeVisibleSelectionInDOMTreeDeprecated().IsRange() &&
       event.Event().button != WebPointerProperties::Button::kRight) {
     // TODO(editing-dev): Use of UpdateStyleAndLayout
@@ -1189,7 +1191,8 @@
   if (!inner_node || !inner_node->GetLayoutObject())
     return false;
   PositionWithAffinity pos_with_affinity =
-      inner_node->GetLayoutObject()->PositionForPoint(result.LocalPoint());
+      inner_node->GetLayoutObject()->PositionForPoint(
+          result.FlippedLocalPoint());
   if (pos_with_affinity.IsNull())
     return false;
   // TODO(xiaochengh): Don't use |ParentAnchoredEquivalent()|.
@@ -1203,7 +1206,7 @@
 
 void SelectionController::SendContextMenuEvent(
     const MouseEventWithHitTestResults& mev,
-    const LayoutPoint& position) {
+    const PhysicalOffset& position) {
   if (!Selection().IsAvailable())
     return;
   if (Selection().Contains(position) || mev.GetScrollbar() ||
@@ -1242,8 +1245,8 @@
   // greyed out even though we're clicking on the selection.  This looks
   // really strange (having the whole frame be greyed out), so we deselect the
   // selection.
-  IntPoint p = frame_->View()->ConvertFromRootFrame(
-      FlooredIntPoint(mev.Event().PositionInRootFrame()));
+  PhysicalOffset p(frame_->View()->ConvertFromRootFrame(
+      FlooredIntPoint(mev.Event().PositionInRootFrame())));
   if (!Selection().Contains(p))
     return;
 
diff --git a/third_party/blink/renderer/core/editing/selection_controller.h b/third_party/blink/renderer/core/editing/selection_controller.h
index 3bcd0af..a92a5b1 100644
--- a/third_party/blink/renderer/core/editing/selection_controller.h
+++ b/third_party/blink/renderer/core/editing/selection_controller.h
@@ -55,21 +55,22 @@
   bool HandleMousePressEvent(const MouseEventWithHitTestResults&);
   void HandleMouseDraggedEvent(const MouseEventWithHitTestResults&,
                                const IntPoint&,
-                               const LayoutPoint&,
-                               const LayoutPoint&);
+                               const PhysicalOffset&,
+                               const PhysicalOffset&);
   bool HandleMouseReleaseEvent(const MouseEventWithHitTestResults&,
-                               const LayoutPoint&);
+                               const PhysicalOffset&);
   bool HandlePasteGlobalSelection(const WebMouseEvent&);
   bool HandleGestureLongPress(const HitTestResult&);
   void HandleGestureTwoFingerTap(const GestureEventWithHitTestResults&);
   void HandleGestureLongTap(const GestureEventWithHitTestResults&);
 
-  void UpdateSelectionForMouseDrag(const LayoutPoint&, const LayoutPoint&);
+  void UpdateSelectionForMouseDrag(const PhysicalOffset&,
+                                   const PhysicalOffset&);
   void UpdateSelectionForMouseDrag(const HitTestResult&,
-                                   const LayoutPoint&,
-                                   const LayoutPoint&);
+                                   const PhysicalOffset&,
+                                   const PhysicalOffset&);
   void SendContextMenuEvent(const MouseEventWithHitTestResults&,
-                            const LayoutPoint&);
+                            const PhysicalOffset&);
   void PassMousePressEventToSubframe(const MouseEventWithHitTestResults&);
 
   void InitializeSelectionState();
diff --git a/third_party/blink/renderer/core/editing/visible_units_word.cc b/third_party/blink/renderer/core/editing/visible_units_word.cc
index a4dce2d..5be163a6 100644
--- a/third_party/blink/renderer/core/editing/visible_units_word.cc
+++ b/third_party/blink/renderer/core/editing/visible_units_word.cc
@@ -102,7 +102,9 @@
              text[runner - 1] == kLowLineCharacter))
           return Position::After(runner - 1);
       }
-      return Position::After(text.length() - 1);
+      if (text[text.length() - 1] != kNewlineCharacter)
+        return Position::After(text.length() - 1);
+      return Position();
     }
   } finder;
   return TextSegments::FindBoundaryForward(position, &finder);
diff --git a/third_party/blink/renderer/core/editing/visible_units_word_test.cc b/third_party/blink/renderer/core/editing/visible_units_word_test.cc
index fa4611f..0d36d705 100644
--- a/third_party/blink/renderer/core/editing/visible_units_word_test.cc
+++ b/third_party/blink/renderer/core/editing/visible_units_word_test.cc
@@ -502,6 +502,12 @@
   EXPECT_EQ("<p>abc</p><p>def|</p>", DoNextWord("<p>abc|</p><p>def</p>"));
 }
 
+TEST_P(ParameterizedVisibleUnitsWordTest, NextWordCrossingPlaceholderBR) {
+  // TODO(crbug.com/122304): NextWordPosition should respect paragraph
+  // boundaries. On Windows, it should move to "|abc".
+  EXPECT_EQ("<p><br></p><p>abc|</p>", DoNextWord("<p>|<br></p><p>abc</p>"));
+}
+
 TEST_P(ParameterizedVisibleUnitsWordTest, NextWordMixedEditability) {
   EXPECT_EQ(
       "<p contenteditable>"
diff --git a/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.cc b/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.cc
index 67b24bce..7fbe62a 100644
--- a/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.cc
@@ -217,9 +217,8 @@
   for (const auto& header : response.HttpHeaderFields()) {
     if (FetchUtils::IsForbiddenResponseHeaderName(header.key) ||
         (!cors::IsCorsSafelistedResponseHeader(header.key) &&
-         exposed_headers.find(header.key.Ascii().data()) ==
-             exposed_headers.end()))
-      blocked_headers.insert(header.key.Ascii().data());
+         exposed_headers.find(header.key.Ascii()) == exposed_headers.end()))
+      blocked_headers.insert(header.key.Ascii());
   }
 
   if (blocked_headers.empty()) {
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index 384e068..8076238 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -120,6 +120,7 @@
 #include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
 #include "third_party/blink/renderer/core/geometry/dom_rect.h"
 #include "third_party/blink/renderer/core/html/forms/html_form_element.h"
+#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
 #include "third_party/blink/renderer/core/html/html_body_element.h"
 #include "third_party/blink/renderer/core/html/html_iframe_element.h"
 #include "third_party/blink/renderer/core/html/image_document.h"
@@ -11267,7 +11268,7 @@
                            HitTestRequest::kActive |
                            HitTestRequest::kIgnoreClipping;
   HitTestLocation location(
-      frame_view->ConvertFromRootFrame(LayoutPoint(100, -50)));
+      frame_view->ConvertFromRootFrame(PhysicalOffset(100, -50)));
   HitTestResult result(request, location);
   frame_view->GetLayoutView()->HitTest(location, result);
 
@@ -11743,6 +11744,58 @@
   EXPECT_GT(clip->scrollTop(), 0);
 }
 
+//  This test ensures that we scroll to the correct scale when the focused
+//  element has a selection rather than a carret.
+TEST_F(WebFrameSimTest, ScrollFocusedSelectionIntoView) {
+  UseAndroidSettings();
+  WebView().MainFrameWidget()->Resize(WebSize(400, 600));
+  WebView().EnableFakePageScaleAnimationForTesting(true);
+  WebView().GetPage()->GetSettings().SetTextAutosizingEnabled(false);
+
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(R"HTML(
+      <!DOCTYPE html>
+      <style>
+        ::-webkit-scrollbar {
+          width: 0px;
+          height: 0px;
+        }
+        body, html {
+          margin: 0px;
+          width: 100%;
+          height: 100%;
+        }
+        input {
+          padding: 0;
+          width: 100px;
+          height: 20px;
+        }
+      </style>
+      <input type="text" id="target" value="test">
+  )HTML");
+
+  Compositor().BeginFrame();
+  WebView().AdvanceFocus(false);
+
+  HTMLInputElement* input =
+      ToHTMLInputElement(GetDocument().getElementById("target"));
+  input->select();
+
+  // Simulate the keyboard being shown and resizing the widget. Cause a scroll
+  // into view after.
+  ASSERT_EQ(WebView().FakePageScaleAnimationPageScaleForTesting(), 0.f);
+  WebFrameWidget* widget = WebView().MainFrameImpl()->FrameWidgetImpl();
+  widget->ScrollFocusedEditableElementIntoView();
+
+  // Make sure zoomed in but only up to a legible scale. The bounds are
+  // arbitrary and fuzzy since we don't specifically care to constrain the
+  // amount of zooming (that should be tested elsewhere), we just care that it
+  // zooms but not off to infinity.
+  EXPECT_GT(WebView().FakePageScaleAnimationPageScaleForTesting(), .75f);
+  EXPECT_LT(WebView().FakePageScaleAnimationPageScaleForTesting(), 2.f);
+}
+
 TEST_F(WebFrameSimTest, DoubleTapZoomWhileScrolled) {
   UseAndroidSettings();
   WebView().MainFrameWidget()->Resize(WebSize(490, 500));
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
index d01f083..a654d9b 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
@@ -566,10 +566,9 @@
   if (!frame)
     return false;
 
-  IntPoint frame_location = Location();
-  HitTestLocation location(LayoutRect(frame_location.X() + rect.x,
-                                      frame_location.Y() + rect.y, rect.width,
-                                      rect.height));
+  IntRect frame_rect = rect;
+  frame_rect.MoveBy(Location());
+  HitTestLocation location((PhysicalRect(frame_rect)));
   HitTestResult result = frame->GetEventHandler().HitTestResultAtLocation(
       location, HitTestRequest::kReadOnly | HitTestRequest::kActive |
                     HitTestRequest::kListBased);
diff --git a/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc b/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc
index e98696f..eadba9e 100644
--- a/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc
@@ -398,11 +398,8 @@
   }
 
   // Schedule the scroll.
-  LayoutRect absolute_rect =
-      owner_object
-          ->LocalToAncestorRect(PhysicalRect(rect_to_scroll),
-                                owner_object->View())
-          .ToLayoutRect();
+  PhysicalRect absolute_rect = owner_object->LocalToAncestorRect(
+      PhysicalRect(rect_to_scroll), owner_object->View());
 
   if (!params.zoom_into_rect ||
       !owner_object->GetDocument().GetFrame()->LocalFrameRoot().IsMainFrame()) {
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
index 33cbdd94..04bdb5f 100644
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
@@ -353,6 +353,13 @@
   // fetch (https://crbug.com/824646).
   mojom::ScriptType script_type = mojom::ScriptType::kClassic;
 
+  auto worker_settings = std::make_unique<WorkerSettings>(
+      false /* disable_reading_from_canvas */,
+      false /* strict_mixed_content_checking */,
+      true /* allow_running_of_insecure_content */,
+      false /* strictly_block_blockable_mixed_content */,
+      GenericFontFamilySettings());
+
   if (features::IsOffMainThreadSharedWorkerScriptFetchEnabled()) {
     // Off-the-main-thread script fetch:
     // Some params (e.g., referrer policy, address space, CSP) passed to
@@ -369,8 +376,8 @@
         document->IsSecureContext(), outside_settings_object->GetHttpsState(),
         CreateWorkerClients(), base::nullopt /* response_address_space */,
         nullptr /* origin_trial_tokens */, devtools_worker_token_,
-        std::make_unique<WorkerSettings>(document->GetFrame()->GetSettings()),
-        kV8CacheOptionsDefault, nullptr /* worklet_module_response_map */,
+        std::move(worker_settings), kV8CacheOptionsDefault,
+        nullptr /* worklet_module_response_map */,
         std::move(pending_interface_provider_), BeginFrameProviderParams(),
         nullptr /* parent_feature_policy */, base::UnguessableToken());
     StartWorkerThread(std::move(creation_params), script_request_url_,
@@ -405,8 +412,8 @@
       document->IsSecureContext(), outside_settings_object->GetHttpsState(),
       CreateWorkerClients(), main_script_loader_->ResponseAddressSpace(),
       main_script_loader_->OriginTrialTokens(), devtools_worker_token_,
-      std::make_unique<WorkerSettings>(document->GetFrame()->GetSettings()),
-      kV8CacheOptionsDefault, nullptr /* worklet_module_response_map */,
+      std::move(worker_settings), kV8CacheOptionsDefault,
+      nullptr /* worklet_module_response_map */,
       std::move(pending_interface_provider_));
   StartWorkerThread(std::move(creation_params), script_response_url,
                     main_script_loader_->SourceText(),
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 53907276..51aa08e 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -432,7 +432,8 @@
   WebMouseEvent transformed_event =
       TransformWebMouseEvent(MainFrameImpl()->GetFrameView(), event);
   transformed_event.menu_source_type = kMenuSourceMouse;
-  LayoutPoint position_in_root_frame(transformed_event.PositionInRootFrame());
+  PhysicalOffset position_in_root_frame = PhysicalOffset::FromFloatPointRound(
+      transformed_event.PositionInRootFrame());
 
   // Find the right target frame. See issue 1186900.
   HitTestResult result = HitTestResultForRootFramePos(position_in_root_frame);
@@ -2149,7 +2150,7 @@
                                  kScrollBehaviorInstant);
   params.stop_at_main_frame_layout_viewport = true;
   layout_object->ScrollRectToVisible(
-      LayoutRect(layout_object->AbsoluteBoundingBoxRect()), params);
+      PhysicalRect(layout_object->AbsoluteBoundingBoxRect()), params);
 
   ZoomAndScrollToFocusedEditableElementRect(
       main_frame_view->RootFrameToDocument(
@@ -2160,7 +2161,7 @@
               element->GetDocument()
                   .GetFrame()
                   ->Selection()
-                  .AbsoluteCaretBounds())),
+                  .ComputeRectToScroll(kDoNotRevealExtent))),
       ShouldZoomToLegibleScale(*element));
 
   return true;
@@ -2779,7 +2780,7 @@
                                       const gfx::Point& location) {
   // FIXME: Location is probably in viewport coordinates
   HitTestResult result =
-      HitTestResultForRootFramePos(LayoutPoint(IntPoint(location)));
+      HitTestResultForRootFramePos(PhysicalOffset(IntPoint(location)));
   Node* node = result.InnerNode();
   if (!IsHTMLObjectElement(*node) && !IsHTMLEmbedElement(*node))
     return;
@@ -2824,8 +2825,8 @@
   DocumentLifecycle::AllowThrottlingScope throttling_scope(
       MainFrameImpl()->GetFrame()->GetDocument()->Lifecycle());
   LocalFrameView* view = MainFrameImpl()->GetFrameView();
-  LayoutPoint point_in_root_frame =
-      view->ViewportToFrame(LayoutPoint(IntPoint(point_in_viewport)));
+  PhysicalOffset point_in_root_frame =
+      view->ViewportToFrame(PhysicalOffset(IntPoint(point_in_viewport)));
   return HitTestResultForRootFramePos(point_in_root_frame);
 }
 
@@ -3170,7 +3171,7 @@
 }
 
 HitTestResult WebViewImpl::HitTestResultForRootFramePos(
-    const LayoutPoint& pos_in_root_frame) {
+    const PhysicalOffset& pos_in_root_frame) {
   auto* main_frame = DynamicTo<LocalFrame>(AsView().page->MainFrame());
   if (!main_frame)
     return HitTestResult();
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
index 0f1c5c1..b2fe5872 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -502,7 +502,7 @@
               WebViewImpl* opener);
   ~WebViewImpl() override;
 
-  HitTestResult HitTestResultForRootFramePos(const LayoutPoint&);
+  HitTestResult HitTestResultForRootFramePos(const PhysicalOffset&);
 
   void ConfigureAutoResizeMode();
 
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index 80c238c..e1e5b4a0 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -3252,48 +3252,68 @@
   ScopedMiddleClickAutoscrollForTest middle_click_autoscroll(true);
   RegisterMockedHttpURLLoad("content-width-1000.html");
 
-  WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
-      base_url_ + "content-width-1000.html", nullptr, nullptr, &client);
-  web_view->MainFrameWidget()->Resize(WebSize(100, 100));
-  UpdateAllLifecyclePhases();
-  RunPendingTasks();
+  // We will be changing the size of the page to test each of the panning
+  // cursor variations. For reference, content-width-1000.html is 1000px wide
+  // and 2000px tall.
+  // 1. 100 x 100 - The page will be scrollable in both x and y directions, so
+  //      we expect to see the cursor with arrows in all four directions.
+  // 2. 1010 x 100 - The page will be scrollable in the y direction, but not x,
+  //      so we expect to see the cursor with only the vertical arrows.
+  // 3. 100 x 2010 - The page will be scrollable in the x direction, but not y,
+  //      so we expect to see the cursor with only the horizontal arrows.
+  struct CursorTests {
+    int resize_width;
+    int resize_height;
+    blink::Cursor::Type expected_cursor;
+  } cursor_tests[] = {{100, 100, MiddlePanningCursor().GetType()},
+                      {1010, 100, MiddlePanningVerticalCursor().GetType()},
+                      {100, 2010, MiddlePanningHorizontalCursor().GetType()}};
 
-  WebMouseEvent mouse_event(WebInputEvent::kMouseDown,
-                            WebInputEvent::kNoModifiers,
-                            WebInputEvent::GetStaticTimeStampForTests());
-  mouse_event.button = WebMouseEvent::Button::kMiddle;
-  mouse_event.SetPositionInWidget(1, 1);
-  mouse_event.click_count = 1;
+  for (const CursorTests current_test : cursor_tests) {
+    WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
+        base_url_ + "content-width-1000.html", nullptr, nullptr, &client);
+    web_view->MainFrameWidget()->Resize(
+        WebSize(current_test.resize_width, current_test.resize_height));
+    UpdateAllLifecyclePhases();
+    RunPendingTasks();
 
-  // Start middle-click autoscroll.
-  web_view->MainFrameWidget()->HandleInputEvent(
-      WebCoalescedInputEvent(mouse_event));
-  mouse_event.SetType(WebInputEvent::kMouseUp);
-  web_view->MainFrameWidget()->HandleInputEvent(
-      WebCoalescedInputEvent(mouse_event));
+    WebMouseEvent mouse_event(WebInputEvent::kMouseDown,
+                              WebInputEvent::kNoModifiers,
+                              WebInputEvent::GetStaticTimeStampForTests());
+    mouse_event.button = WebMouseEvent::Button::kMiddle;
+    mouse_event.SetPositionInWidget(1, 1);
+    mouse_event.click_count = 1;
 
-  EXPECT_EQ(MiddlePanningCursor().GetType(), client.GetLastCursorType());
+    // Start middle-click autoscroll.
+    web_view->MainFrameWidget()->HandleInputEvent(
+        WebCoalescedInputEvent(mouse_event));
+    mouse_event.SetType(WebInputEvent::kMouseUp);
+    web_view->MainFrameWidget()->HandleInputEvent(
+        WebCoalescedInputEvent(mouse_event));
 
-  LocalFrame* local_frame =
-      To<WebLocalFrameImpl>(web_view->MainFrame())->GetFrame();
+    EXPECT_EQ(current_test.expected_cursor, client.GetLastCursorType());
 
-  // Even if a plugin tries to change the cursor type, that should be ignored
-  // during middle-click autoscroll.
-  web_view->GetChromeClient().SetCursorForPlugin(WebCursorInfo(PointerCursor()),
-                                                 local_frame);
-  EXPECT_EQ(MiddlePanningCursor().GetType(), client.GetLastCursorType());
+    LocalFrame* local_frame =
+        To<WebLocalFrameImpl>(web_view->MainFrame())->GetFrame();
 
-  // End middle-click autoscroll.
-  mouse_event.SetType(WebInputEvent::kMouseDown);
-  web_view->MainFrameWidget()->HandleInputEvent(
-      WebCoalescedInputEvent(mouse_event));
-  mouse_event.SetType(WebInputEvent::kMouseUp);
-  web_view->MainFrameWidget()->HandleInputEvent(
-      WebCoalescedInputEvent(mouse_event));
+    // Even if a plugin tries to change the cursor type, that should be ignored
+    // during middle-click autoscroll.
+    web_view->GetChromeClient().SetCursorForPlugin(
+        WebCursorInfo(PointerCursor()), local_frame);
+    EXPECT_EQ(current_test.expected_cursor, client.GetLastCursorType());
 
-  web_view->GetChromeClient().SetCursorForPlugin(WebCursorInfo(IBeamCursor()),
-                                                 local_frame);
-  EXPECT_EQ(IBeamCursor().GetType(), client.GetLastCursorType());
+    // End middle-click autoscroll.
+    mouse_event.SetType(WebInputEvent::kMouseDown);
+    web_view->MainFrameWidget()->HandleInputEvent(
+        WebCoalescedInputEvent(mouse_event));
+    mouse_event.SetType(WebInputEvent::kMouseUp);
+    web_view->MainFrameWidget()->HandleInputEvent(
+        WebCoalescedInputEvent(mouse_event));
+
+    web_view->GetChromeClient().SetCursorForPlugin(WebCursorInfo(IBeamCursor()),
+                                                   local_frame);
+    EXPECT_EQ(IBeamCursor().GetType(), client.GetLastCursorType());
+  }
 
   // Explicitly reset to break dependency on locally scoped client.
   web_view_helper_.Reset();
diff --git a/third_party/blink/renderer/core/fetch/fetch_response_data.cc b/third_party/blink/renderer/core/fetch/fetch_response_data.cc
index 7bba68b..53c95f8 100644
--- a/third_party/blink/renderer/core/fetch/fetch_response_data.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_response_data.cc
@@ -89,7 +89,7 @@
   for (const auto& header : header_list_->List()) {
     const String& name = header.first;
     if (cors::IsCorsSafelistedResponseHeader(name) ||
-        (exposed_headers.find(name.Ascii().data()) != exposed_headers.end() &&
+        (exposed_headers.find(name.Ascii()) != exposed_headers.end() &&
          !FetchUtils::IsForbiddenResponseHeaderName(name))) {
       response->header_list_->Append(name, header.second);
     }
diff --git a/third_party/blink/renderer/core/fetch/response.cc b/third_party/blink/renderer/core/fetch/response.cc
index ee59e01..b0c43c6 100644
--- a/third_party/blink/renderer/core/fetch/response.cc
+++ b/third_party/blink/renderer/core/fetch/response.cc
@@ -55,7 +55,7 @@
     case network::mojom::FetchResponseType::kCors: {
       WebHTTPHeaderSet header_names;
       for (const auto& header : headers)
-        header_names.insert(header.Ascii().data());
+        header_names.insert(header.Ascii());
       return response->CreateCorsFilteredResponse(header_names);
       break;
     }
diff --git a/third_party/blink/renderer/core/frame/frame_serializer.cc b/third_party/blink/renderer/core/frame/frame_serializer.cc
index a260cf8..e29d000 100644
--- a/third_party/blink/renderer/core/frame/frame_serializer.cc
+++ b/third_party/blink/renderer/core/frame/frame_serializer.cc
@@ -572,8 +572,8 @@
 String FrameSerializer::MarkOfTheWebDeclaration(const KURL& url) {
   StringBuilder builder;
   bool emits_minus = false;
-  CString orignal_url = url.GetString().Ascii();
-  for (const char* string = orignal_url.data(); *string; ++string) {
+  std::string orignal_url = url.GetString().Ascii();
+  for (const char* string = orignal_url.c_str(); *string; ++string) {
     const char ch = *string;
     if (ch == '-' && emits_minus) {
       builder.Append("%2D");
@@ -583,10 +583,10 @@
     emits_minus = ch == '-';
     builder.Append(ch);
   }
-  CString escaped_url = builder.ToString().Ascii();
+  std::string escaped_url = builder.ToString().Ascii();
   return String::Format("saved from url=(%04d)%s",
                         static_cast<int>(escaped_url.length()),
-                        escaped_url.data());
+                        escaped_url.c_str());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index bdf2c291..d2153bea 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -831,7 +831,7 @@
 }
 
 PositionWithAffinity LocalFrame::PositionForPoint(
-    const LayoutPoint& frame_point) {
+    const PhysicalOffset& frame_point) {
   HitTestLocation location(frame_point);
   HitTestResult result = GetEventHandler().HitTestResultAtLocation(location);
   Node* node = result.InnerNodeOrImageMapImage();
@@ -841,13 +841,14 @@
   if (!layout_object)
     return PositionWithAffinity();
   const PositionWithAffinity position =
-      layout_object->PositionForPoint(result.LocalPoint());
+      layout_object->PositionForPoint(result.FlippedLocalPoint());
   if (position.IsNull())
     return PositionWithAffinity(FirstPositionInOrBeforeNode(*node));
   return position;
 }
 
-Document* LocalFrame::DocumentAtPoint(const LayoutPoint& point_in_root_frame) {
+Document* LocalFrame::DocumentAtPoint(
+    const PhysicalOffset& point_in_root_frame) {
   if (!View())
     return nullptr;
 
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index f9d2ce6..48379e1 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -262,8 +262,8 @@
   String SelectedTextForClipboard() const;
 
   PositionWithAffinityTemplate<EditingAlgorithm<NodeTraversal>>
-  PositionForPoint(const LayoutPoint& frame_point);
-  Document* DocumentAtPoint(const LayoutPoint&);
+  PositionForPoint(const PhysicalOffset& frame_point);
+  Document* DocumentAtPoint(const PhysicalOffset&);
 
   void RemoveSpellingMarkersUnderWords(const Vector<String>& words);
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 5e41377..00da8cb0 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -2946,7 +2946,7 @@
     // cropping implementation should not do this!
     bool horizontal_writing_mode =
         layout_view->StyleRef().IsHorizontalWritingMode();
-    const LayoutRect& document_rect = LayoutRect(layout_view->DocumentRect());
+    PhysicalRect document_rect(layout_view->DocumentRect());
     LayoutUnit doc_logical_width = horizontal_writing_mode
                                        ? document_rect.Width()
                                        : document_rect.Height();
@@ -2973,8 +2973,7 @@
           layout_invalidation_reason::kPrintingChanged);
       UpdateLayout();
 
-      const LayoutRect& updated_document_rect =
-          LayoutRect(layout_view->DocumentRect());
+      PhysicalRect updated_document_rect(layout_view->DocumentRect());
       LayoutUnit doc_logical_height = horizontal_writing_mode
                                           ? updated_document_rect.Height()
                                           : updated_document_rect.Width();
@@ -2982,8 +2981,8 @@
                                        ? updated_document_rect.Y()
                                        : updated_document_rect.X();
       LayoutUnit doc_logical_right = horizontal_writing_mode
-                                         ? updated_document_rect.MaxX()
-                                         : updated_document_rect.MaxY();
+                                         ? updated_document_rect.Right()
+                                         : updated_document_rect.Bottom();
       LayoutUnit clipped_logical_left;
       if (!layout_view->StyleRef().IsLeftToRightDirection()) {
         clipped_logical_left =
@@ -3024,22 +3023,15 @@
 IntPoint LocalFrameView::ConvertFromLayoutObject(
     const LayoutObject& layout_object,
     const IntPoint& layout_object_point) const {
-  return RoundedIntPoint(
-      ConvertFromLayoutObject(layout_object, LayoutPoint(layout_object_point)));
+  return RoundedIntPoint(ConvertFromLayoutObject(
+      layout_object, PhysicalOffset(layout_object_point)));
 }
 
 IntPoint LocalFrameView::ConvertToLayoutObject(
     const LayoutObject& layout_object,
     const IntPoint& frame_point) const {
   return RoundedIntPoint(
-      ConvertToLayoutObject(layout_object, LayoutPoint(frame_point)));
-}
-
-LayoutPoint LocalFrameView::ConvertFromLayoutObject(
-    const LayoutObject& layout_object,
-    const LayoutPoint& layout_object_point) const {
-  return layout_object.LocalToAbsolutePoint(PhysicalOffset(layout_object_point))
-      .ToLayoutPoint();
+      ConvertToLayoutObject(layout_object, PhysicalOffset(frame_point)));
 }
 
 PhysicalOffset LocalFrameView::ConvertFromLayoutObject(
@@ -3048,13 +3040,6 @@
   return layout_object.LocalToAbsolutePoint(layout_object_offset);
 }
 
-LayoutPoint LocalFrameView::ConvertToLayoutObject(
-    const LayoutObject& layout_object,
-    const LayoutPoint& frame_point) const {
-  return LayoutPoint(
-      ConvertToLayoutObject(layout_object, FloatPoint(frame_point)));
-}
-
 PhysicalOffset LocalFrameView::ConvertToLayoutObject(
     const LayoutObject& layout_object,
     const PhysicalOffset& frame_offset) const {
@@ -3122,24 +3107,14 @@
   return FloatPoint(DocumentToFrame(DoublePoint(point_in_document)));
 }
 
-LayoutPoint LocalFrameView::DocumentToFrame(
-    const LayoutPoint& point_in_document) const {
-  ScrollableArea* layout_viewport = LayoutViewport();
-  if (!layout_viewport)
-    return point_in_document;
-
-  return point_in_document - LayoutSize(layout_viewport->GetScrollOffset());
-}
-
 PhysicalOffset LocalFrameView::DocumentToFrame(
     const PhysicalOffset& offset_in_document) const {
-  return PhysicalOffset(DocumentToFrame(offset_in_document.ToLayoutPoint()));
-}
+  ScrollableArea* layout_viewport = LayoutViewport();
+  if (!layout_viewport)
+    return offset_in_document;
 
-LayoutRect LocalFrameView::DocumentToFrame(
-    const LayoutRect& rect_in_document) const {
-  return LayoutRect(DocumentToFrame(rect_in_document.Location()),
-                    rect_in_document.Size());
+  return offset_in_document -
+         PhysicalOffset::FromFloatSizeRound(layout_viewport->GetScrollOffset());
 }
 
 PhysicalRect LocalFrameView::DocumentToFrame(
@@ -3149,21 +3124,17 @@
 }
 
 IntPoint LocalFrameView::FrameToDocument(const IntPoint& point_in_frame) const {
-  return FlooredIntPoint(FrameToDocument(LayoutPoint(point_in_frame)));
-}
-
-LayoutPoint LocalFrameView::FrameToDocument(
-    const LayoutPoint& point_in_frame) const {
-  ScrollableArea* layout_viewport = LayoutViewport();
-  if (!layout_viewport)
-    return point_in_frame;
-
-  return point_in_frame + LayoutSize(layout_viewport->GetScrollOffset());
+  return FlooredIntPoint(FrameToDocument(PhysicalOffset(point_in_frame)));
 }
 
 PhysicalOffset LocalFrameView::FrameToDocument(
     const PhysicalOffset& offset_in_frame) const {
-  return PhysicalOffset(FrameToDocument(offset_in_frame.ToLayoutPoint()));
+  ScrollableArea* layout_viewport = LayoutViewport();
+  if (!layout_viewport)
+    return offset_in_frame;
+
+  return offset_in_frame +
+         PhysicalOffset::FromFloatSizeRound(layout_viewport->GetScrollOffset());
 }
 
 IntRect LocalFrameView::FrameToDocument(const IntRect& rect_in_frame) const {
@@ -3171,12 +3142,6 @@
                  rect_in_frame.Size());
 }
 
-LayoutRect LocalFrameView::FrameToDocument(
-    const LayoutRect& rect_in_frame) const {
-  return LayoutRect(FrameToDocument(rect_in_frame.Location()),
-                    rect_in_frame.Size());
-}
-
 PhysicalRect LocalFrameView::FrameToDocument(
     const PhysicalRect& rect_in_frame) const {
   return PhysicalRect(FrameToDocument(rect_in_frame.offset),
@@ -3213,24 +3178,6 @@
   return parent_rect;
 }
 
-LayoutPoint LocalFrameView::ConvertToContainingEmbeddedContentView(
-    const LayoutPoint& local_point) const {
-  if (LocalFrameView* parent = ParentFrameView()) {
-    auto* layout_object = GetLayoutEmbeddedContent();
-    if (!layout_object)
-      return local_point;
-
-    LayoutPoint point(local_point);
-
-    // Add borders and padding
-    point.Move((layout_object->BorderLeft() + layout_object->PaddingLeft()),
-               (layout_object->BorderTop() + layout_object->PaddingTop()));
-    return parent->ConvertFromLayoutObject(*layout_object, point);
-  }
-
-  return local_point;
-}
-
 PhysicalOffset LocalFrameView::ConvertToContainingEmbeddedContentView(
     const PhysicalOffset& local_offset) const {
   if (LocalFrameView* parent = ParentFrameView()) {
@@ -3268,16 +3215,10 @@
   return local_point;
 }
 
-LayoutPoint LocalFrameView::ConvertFromContainingEmbeddedContentView(
-    const LayoutPoint& parent_point) const {
-  return LayoutPoint(
-      ConvertFromContainingEmbeddedContentView(DoublePoint(parent_point)));
-}
-
 PhysicalOffset LocalFrameView::ConvertFromContainingEmbeddedContentView(
     const PhysicalOffset& parent_offset) const {
-  return PhysicalOffset(
-      ConvertFromContainingEmbeddedContentView(parent_offset.ToLayoutPoint()));
+  return PhysicalOffset::FromFloatPointRound(
+      ConvertFromContainingEmbeddedContentView(FloatPoint(parent_offset)));
 }
 
 FloatPoint LocalFrameView::ConvertFromContainingEmbeddedContentView(
@@ -3310,7 +3251,7 @@
 IntPoint LocalFrameView::ConvertToContainingEmbeddedContentView(
     const IntPoint& local_point) const {
   return RoundedIntPoint(
-      ConvertToContainingEmbeddedContentView(LayoutPoint(local_point)));
+      ConvertToContainingEmbeddedContentView(PhysicalOffset(local_point)));
 }
 
 void LocalFrameView::SetInitialTracksPaintInvalidationsForTesting(
@@ -3611,11 +3552,11 @@
 }
 
 void LocalFrameView::ScrollRectToVisibleInRemoteParent(
-    const LayoutRect& rect_to_scroll,
+    const PhysicalRect& rect_to_scroll,
     const WebScrollIntoViewParams& params) {
   DCHECK(GetFrame().IsLocalRoot() && !GetFrame().IsMainFrame() &&
          safe_to_propagate_scroll_to_parent_);
-  LayoutRect new_rect = ConvertToRootFrame(rect_to_scroll);
+  PhysicalRect new_rect = ConvertToRootFrame(rect_to_scroll);
   GetFrame().Client()->ScrollRectToVisibleInParentFrame(
       WebRect(new_rect.X().ToInt(), new_rect.Y().ToInt(),
               new_rect.Width().ToInt(), new_rect.Height().ToInt()),
@@ -3629,9 +3570,9 @@
   }
 }
 
-LayoutPoint LocalFrameView::ViewportToFrame(
-    const LayoutPoint& point_in_viewport) const {
-  LayoutPoint point_in_root_frame(
+PhysicalOffset LocalFrameView::ViewportToFrame(
+    const PhysicalOffset& point_in_viewport) const {
+  PhysicalOffset point_in_root_frame = PhysicalOffset::FromFloatPointRound(
       frame_->GetPage()->GetVisualViewport().ViewportToRootFrame(
           FloatPoint(point_in_viewport)));
   return ConvertFromRootFrame(point_in_root_frame);
@@ -3654,7 +3595,7 @@
 
 IntPoint LocalFrameView::ViewportToFrame(
     const IntPoint& point_in_viewport) const {
-  return RoundedIntPoint(ViewportToFrame(LayoutPoint(point_in_viewport)));
+  return RoundedIntPoint(ViewportToFrame(PhysicalOffset(point_in_viewport)));
 }
 
 IntRect LocalFrameView::FrameToViewport(const IntRect& rect_in_frame) const {
@@ -3778,17 +3719,7 @@
 }
 
 IntPoint LocalFrameView::ConvertToRootFrame(const IntPoint& local_point) const {
-  return RoundedIntPoint(ConvertToRootFrame(LayoutPoint(local_point)));
-}
-
-LayoutPoint LocalFrameView::ConvertToRootFrame(
-    const LayoutPoint& local_point) const {
-  if (LocalFrameView* parent = ParentFrameView()) {
-    LayoutPoint parent_point =
-        ConvertToContainingEmbeddedContentView(local_point);
-    return parent->ConvertToRootFrame(parent_point);
-  }
-  return local_point;
+  return RoundedIntPoint(ConvertToRootFrame(PhysicalOffset(local_point)));
 }
 
 PhysicalOffset LocalFrameView::ConvertToRootFrame(
@@ -3811,17 +3742,6 @@
   return local_point;
 }
 
-LayoutRect LocalFrameView::ConvertToRootFrame(
-    const LayoutRect& local_rect) const {
-  if (LocalFrameView* parent = ParentFrameView()) {
-    LayoutPoint parent_point =
-        ConvertToContainingEmbeddedContentView(local_rect.Location());
-    LayoutRect parent_rect(parent_point, local_rect.Size());
-    return parent->ConvertToRootFrame(parent_rect);
-  }
-  return local_rect;
-}
-
 PhysicalRect LocalFrameView::ConvertToRootFrame(
     const PhysicalRect& local_rect) const {
   if (LocalFrameView* parent = ParentFrameView()) {
@@ -3845,17 +3765,7 @@
 IntPoint LocalFrameView::ConvertFromRootFrame(
     const IntPoint& point_in_root_frame) const {
   return RoundedIntPoint(
-      ConvertFromRootFrame(LayoutPoint(point_in_root_frame)));
-}
-
-LayoutPoint LocalFrameView::ConvertFromRootFrame(
-    const LayoutPoint& point_in_root_frame) const {
-  if (LocalFrameView* parent = ParentFrameView()) {
-    LayoutPoint parent_point =
-        parent->ConvertFromRootFrame(point_in_root_frame);
-    return ConvertFromContainingEmbeddedContentView(parent_point);
-  }
-  return point_in_root_frame;
+      ConvertFromRootFrame(PhysicalOffset(point_in_root_frame)));
 }
 
 PhysicalOffset LocalFrameView::ConvertFromRootFrame(
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index de146c2e..6f2a9c5 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -75,7 +75,6 @@
 class LayoutBox;
 class LayoutEmbeddedObject;
 class LayoutObject;
-class LayoutRect;
 class LayoutSVGRoot;
 class LayoutView;
 class LocalFrame;
@@ -399,12 +398,8 @@
   IntRect ConvertToLayoutObject(const LayoutObject&, const IntRect&) const;
   IntPoint ConvertFromLayoutObject(const LayoutObject&, const IntPoint&) const;
   IntPoint ConvertToLayoutObject(const LayoutObject&, const IntPoint&) const;
-  LayoutPoint ConvertFromLayoutObject(const LayoutObject&,
-                                      const LayoutPoint&) const;
   PhysicalOffset ConvertFromLayoutObject(const LayoutObject&,
                                          const PhysicalOffset&) const;
-  LayoutPoint ConvertToLayoutObject(const LayoutObject&,
-                                    const LayoutPoint&) const;
   PhysicalOffset ConvertToLayoutObject(const LayoutObject&,
                                        const PhysicalOffset&) const;
   FloatPoint ConvertToLayoutObject(const LayoutObject&,
@@ -496,7 +491,7 @@
   IntPoint FrameToViewport(const IntPoint&) const;
   IntPoint ViewportToFrame(const IntPoint&) const;
   FloatPoint ViewportToFrame(const FloatPoint&) const;
-  LayoutPoint ViewportToFrame(const LayoutPoint&) const;
+  PhysicalOffset ViewportToFrame(const PhysicalOffset&) const;
 
   // FIXME: Some external callers expect to get back a rect that's positioned
   // in viewport space, but sized in CSS pixels. This is an artifact of the
@@ -508,19 +503,14 @@
   IntRect FrameToScreen(const IntRect&) const;
 
   // Converts from/to local frame coordinates to the root frame coordinates.
-  // TODO(wangxianzhu): Remove LayoutPoint/LayoutRect version after all clients
-  // switch to use PhysicalPoint/PhysicalRect.
   IntRect ConvertToRootFrame(const IntRect&) const;
   IntPoint ConvertToRootFrame(const IntPoint&) const;
-  LayoutPoint ConvertToRootFrame(const LayoutPoint&) const;
   PhysicalOffset ConvertToRootFrame(const PhysicalOffset&) const;
   FloatPoint ConvertToRootFrame(const FloatPoint&) const;
-  LayoutRect ConvertToRootFrame(const LayoutRect&) const;
   PhysicalRect ConvertToRootFrame(const PhysicalRect&) const;
   IntRect ConvertFromRootFrame(const IntRect&) const;
   IntPoint ConvertFromRootFrame(const IntPoint&) const;
   FloatPoint ConvertFromRootFrame(const FloatPoint&) const;
-  LayoutPoint ConvertFromRootFrame(const LayoutPoint&) const;
   PhysicalOffset ConvertFromRootFrame(const PhysicalOffset&) const;
   IntPoint ConvertSelfToChild(const EmbeddedContentView&,
                               const IntPoint&) const;
@@ -531,16 +521,12 @@
   IntPoint DocumentToFrame(const IntPoint&) const;
   FloatPoint DocumentToFrame(const FloatPoint&) const;
   DoublePoint DocumentToFrame(const DoublePoint&) const;
-  LayoutPoint DocumentToFrame(const LayoutPoint&) const;
   PhysicalOffset DocumentToFrame(const PhysicalOffset&) const;
   IntRect DocumentToFrame(const IntRect&) const;
-  LayoutRect DocumentToFrame(const LayoutRect&) const;
   PhysicalRect DocumentToFrame(const PhysicalRect&) const;
   IntPoint FrameToDocument(const IntPoint&) const;
-  LayoutPoint FrameToDocument(const LayoutPoint&) const;
   PhysicalOffset FrameToDocument(const PhysicalOffset&) const;
   IntRect FrameToDocument(const IntRect&) const;
-  LayoutRect FrameToDocument(const LayoutRect&) const;
   PhysicalRect FrameToDocument(const PhysicalRect&) const;
 
   // Normally a LocalFrameView synchronously paints during full lifecycle
@@ -658,7 +644,7 @@
 
   // When the frame is a local root and not a main frame, any recursive
   // scrolling should continue in the parent process.
-  void ScrollRectToVisibleInRemoteParent(const LayoutRect&,
+  void ScrollRectToVisibleInRemoteParent(const PhysicalRect&,
                                          const WebScrollIntoViewParams&);
 
   PaintArtifactCompositor* GetPaintArtifactCompositorForTesting() {
@@ -792,14 +778,11 @@
   // transforms into account.
   IntRect ConvertToContainingEmbeddedContentView(const IntRect&) const;
   IntPoint ConvertToContainingEmbeddedContentView(const IntPoint&) const;
-  LayoutPoint ConvertToContainingEmbeddedContentView(const LayoutPoint&) const;
   PhysicalOffset ConvertToContainingEmbeddedContentView(
       const PhysicalOffset&) const;
   FloatPoint ConvertToContainingEmbeddedContentView(const FloatPoint&) const;
   IntRect ConvertFromContainingEmbeddedContentView(const IntRect&) const;
   IntPoint ConvertFromContainingEmbeddedContentView(const IntPoint&) const;
-  LayoutPoint ConvertFromContainingEmbeddedContentView(
-      const LayoutPoint&) const;
   PhysicalOffset ConvertFromContainingEmbeddedContentView(
       const PhysicalOffset&) const;
   FloatPoint ConvertFromContainingEmbeddedContentView(const FloatPoint&) const;
diff --git a/third_party/blink/renderer/core/frame/mhtml_loading_test.cc b/third_party/blink/renderer/core/frame/mhtml_loading_test.cc
index 71a33c4..1a93ed8 100644
--- a/third_party/blink/renderer/core/frame/mhtml_loading_test.cc
+++ b/third_party/blink/renderer/core/frame/mhtml_loading_test.cc
@@ -102,7 +102,7 @@
   EXPECT_EQ(kFileURL, frame->DomWindow()->location()->toString());
 
   const SecurityOrigin* origin = document->GetSecurityOrigin();
-  EXPECT_STRNE("localhost", origin->Domain().Ascii().data());
+  EXPECT_NE("localhost", origin->Domain().Ascii());
 }
 
 // Checks that full sandboxing protection has been turned on.
diff --git a/third_party/blink/renderer/core/frame/root_frame_viewport.cc b/third_party/blink/renderer/core/frame/root_frame_viewport.cc
index 5c61e50..39fd4ba5 100644
--- a/third_party/blink/renderer/core/frame/root_frame_viewport.cc
+++ b/third_party/blink/renderer/core/frame/root_frame_viewport.cc
@@ -70,10 +70,10 @@
   return *layout_viewport_;
 }
 
-LayoutRect RootFrameViewport::RootContentsToLayoutViewportContents(
+PhysicalRect RootFrameViewport::RootContentsToLayoutViewportContents(
     LocalFrameView& root_frame_view,
-    const LayoutRect& rect) const {
-  LayoutRect ret(rect);
+    const PhysicalRect& rect) const {
+  PhysicalRect ret = rect;
 
   // If the root LocalFrameView is the layout viewport then coordinates in the
   // root LocalFrameView's content space are already in the layout viewport's
@@ -85,7 +85,8 @@
   // by adding the scroll position.
   // TODO(bokan): This will have to be revisited if we ever remove the
   // restriction that a root scroller must be exactly screen filling.
-  ret.Move(LayoutSize(LayoutViewport().GetScrollOffset()));
+  ret.Move(
+      PhysicalOffset::FromFloatSizeRound(LayoutViewport().GetScrollOffset()));
 
   return ret;
 }
@@ -179,21 +180,22 @@
       VisualViewport().VisibleContentRect(scrollbar_inclusion).Size());
 }
 
-LayoutRect RootFrameViewport::VisibleScrollSnapportRect(
+PhysicalRect RootFrameViewport::VisibleScrollSnapportRect(
     IncludeScrollbarsInRect scrollbar_inclusion) const {
   // The effective viewport is the intersection of the visual viewport with the
   // layout viewport.
-  LayoutRect frame_rect_in_content = LayoutRect(
-      FloatPoint(LayoutViewport().GetScrollOffset()),
-      FloatSize(
+  PhysicalRect frame_rect_in_content(
+      PhysicalOffset::FromFloatSizeRound(LayoutViewport().GetScrollOffset()),
+      PhysicalSize(
           LayoutViewport().VisibleContentRect(scrollbar_inclusion).Size()));
-  LayoutRect visual_rect_in_content = LayoutRect(
-      FloatPoint(LayoutViewport().GetScrollOffset() +
-                 VisualViewport().GetScrollAnimator().CurrentOffset()),
-      FloatSize(
+  PhysicalRect visual_rect_in_content(
+      PhysicalOffset::FromFloatSizeRound(
+          LayoutViewport().GetScrollOffset() +
+          VisualViewport().GetScrollAnimator().CurrentOffset()),
+      PhysicalSize(
           VisualViewport().VisibleContentRect(scrollbar_inclusion).Size()));
 
-  LayoutRect visible_scroll_snapport =
+  PhysicalRect visible_scroll_snapport =
       Intersection(visual_rect_in_content, frame_rect_in_content);
   if (!LayoutViewport().GetLayoutBox())
     return visible_scroll_snapport;
@@ -282,13 +284,14 @@
   return scroll_offset;
 }
 
-LayoutRect RootFrameViewport::ScrollIntoView(
-    const LayoutRect& rect_in_absolute,
+PhysicalRect RootFrameViewport::ScrollIntoView(
+    const PhysicalRect& rect_in_absolute,
     const WebScrollIntoViewParams& params) {
-  LayoutRect scroll_snapport_rect(VisibleScrollSnapportRect());
+  PhysicalRect scroll_snapport_rect = VisibleScrollSnapportRect();
 
-  LayoutRect rect_in_document = rect_in_absolute;
-  rect_in_document.Move(LayoutSize(LayoutViewport().GetScrollOffset()));
+  PhysicalRect rect_in_document = rect_in_absolute;
+  rect_in_document.Move(
+      PhysicalOffset::FromFloatSizeRound(LayoutViewport().GetScrollOffset()));
 
   ScrollOffset new_scroll_offset =
       ClampScrollOffset(ScrollAlignment::GetScrollOffsetToExpose(
@@ -328,7 +331,8 @@
   // Return the newly moved rect to absolute coordinates.
   // TODO(szager): PaintLayerScrollableArea::ScrollIntoView clips the return
   // value to the visible content rect, but this does not.
-  rect_in_document.Move(-LayoutSize(LayoutViewport().GetScrollOffset()));
+  rect_in_document.Move(
+      -PhysicalOffset::FromFloatSizeRound(LayoutViewport().GetScrollOffset()));
   return rect_in_document;
 }
 
diff --git a/third_party/blink/renderer/core/frame/root_frame_viewport.h b/third_party/blink/renderer/core/frame/root_frame_viewport.h
index 5423d0d..46d8b5ec 100644
--- a/third_party/blink/renderer/core/frame/root_frame_viewport.h
+++ b/third_party/blink/renderer/core/frame/root_frame_viewport.h
@@ -14,7 +14,7 @@
 namespace blink {
 
 class LocalFrameView;
-class LayoutRect;
+struct PhysicalRect;
 struct WebScrollIntoViewParams;
 
 // ScrollableArea for the root frame's viewport. This class ties together the
@@ -46,9 +46,9 @@
   // and so the root content is the layout viewport's content but if the page
   // sets a custom root scroller via document.rootScroller, another element
   // may be the layout viewport.
-  LayoutRect RootContentsToLayoutViewportContents(
+  PhysicalRect RootContentsToLayoutViewportContents(
       LocalFrameView& root_frame_view,
-      const LayoutRect&) const;
+      const PhysicalRect&) const;
 
   void RestoreToAnchor(const ScrollOffset&);
 
@@ -61,11 +61,11 @@
                        ScrollType,
                        ScrollBehavior,
                        ScrollCallback on_finish) override;
-  LayoutRect ScrollIntoView(const LayoutRect&,
-                            const WebScrollIntoViewParams&) override;
+  PhysicalRect ScrollIntoView(const PhysicalRect&,
+                              const WebScrollIntoViewParams&) override;
   IntRect VisibleContentRect(
       IncludeScrollbarsInRect = kExcludeScrollbars) const override;
-  LayoutRect VisibleScrollSnapportRect(
+  PhysicalRect VisibleScrollSnapportRect(
       IncludeScrollbarsInRect = kExcludeScrollbars) const override;
   bool ShouldUseIntegerScrollOffset() const override;
   bool IsThrottled() const override {
diff --git a/third_party/blink/renderer/core/frame/root_frame_viewport_test.cc b/third_party/blink/renderer/core/frame/root_frame_viewport_test.cc
index d09e984..29dbeb4 100644
--- a/third_party/blink/renderer/core/frame/root_frame_viewport_test.cc
+++ b/third_party/blink/renderer/core/frame/root_frame_viewport_test.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/core/scroll/scroll_types.h"
 #include "third_party/blink/renderer/core/scroll/scrollable_area.h"
 #include "third_party/blink/renderer/core/scroll/scrollbar_theme_mock.h"
+#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
 #include "third_party/blink/renderer/platform/geometry/double_rect.h"
 #include "third_party/blink/renderer/platform/geometry/layout_rect.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
@@ -145,9 +146,9 @@
     return ScrollOffset(ContentsSize() - ViewportSize());
   }
 
-  LayoutRect DocumentToFrame(const LayoutRect& rect) const {
-    LayoutRect ret = rect;
-    ret.Move(LayoutSize(-GetScrollOffset()));
+  PhysicalRect DocumentToFrame(const PhysicalRect& rect) const {
+    PhysicalRect ret = rect;
+    ret.Move(-PhysicalOffset::FromFloatSizeRound(GetScrollOffset()));
     return ret;
   }
 
@@ -334,7 +335,7 @@
   // scaled.
   visual_viewport->SetViewportSize(IntSize(100, 100));
   root_frame_viewport->ScrollIntoView(
-      layout_viewport->DocumentToFrame(LayoutRect(100, 250, 50, 50)),
+      layout_viewport->DocumentToFrame(PhysicalRect(100, 250, 50, 50)),
       WebScrollIntoViewParams(ScrollAlignment::kAlignToEdgeIfNeeded,
                               ScrollAlignment::kAlignToEdgeIfNeeded,
                               kProgrammaticScroll, true,
@@ -343,7 +344,7 @@
   EXPECT_EQ(ScrollOffset(0, 50), visual_viewport->GetScrollOffset());
 
   root_frame_viewport->ScrollIntoView(
-      layout_viewport->DocumentToFrame(LayoutRect(25, 75, 50, 50)),
+      layout_viewport->DocumentToFrame(PhysicalRect(25, 75, 50, 50)),
       WebScrollIntoViewParams(ScrollAlignment::kAlignToEdgeIfNeeded,
                               ScrollAlignment::kAlignToEdgeIfNeeded,
                               kProgrammaticScroll, true,
@@ -359,7 +360,7 @@
                                        ScrollableArea::ScrollCallback());
 
   root_frame_viewport->ScrollIntoView(
-      layout_viewport->DocumentToFrame(LayoutRect(50, 75, 50, 75)),
+      layout_viewport->DocumentToFrame(PhysicalRect(50, 75, 50, 75)),
       WebScrollIntoViewParams(ScrollAlignment::kAlignToEdgeIfNeeded,
                               ScrollAlignment::kAlignToEdgeIfNeeded,
                               kProgrammaticScroll, true,
@@ -368,7 +369,7 @@
   EXPECT_EQ(ScrollOffset(50, 75), visual_viewport->GetScrollOffset());
 
   root_frame_viewport->ScrollIntoView(
-      layout_viewport->DocumentToFrame(LayoutRect(190, 290, 10, 10)),
+      layout_viewport->DocumentToFrame(PhysicalRect(190, 290, 10, 10)),
       WebScrollIntoViewParams(ScrollAlignment::kAlignToEdgeIfNeeded,
                               ScrollAlignment::kAlignToEdgeIfNeeded,
                               kProgrammaticScroll, true,
@@ -386,7 +387,7 @@
       kScrollBehaviorInstant, ScrollableArea::ScrollCallback());
 
   root_frame_viewport->ScrollIntoView(
-      layout_viewport->DocumentToFrame(LayoutRect(
+      layout_viewport->DocumentToFrame(PhysicalRect(
           root_frame_viewport->VisibleContentRect(kExcludeScrollbars))),
       WebScrollIntoViewParams(ScrollAlignment::kAlignToEdgeIfNeeded,
                               ScrollAlignment::kAlignToEdgeIfNeeded,
@@ -396,7 +397,7 @@
   EXPECT_EQ(ScrollOffset(0, 10), visual_viewport->GetScrollOffset());
 
   root_frame_viewport->ScrollIntoView(
-      layout_viewport->DocumentToFrame(LayoutRect(
+      layout_viewport->DocumentToFrame(PhysicalRect(
           root_frame_viewport->VisibleContentRect(kExcludeScrollbars))),
       WebScrollIntoViewParams(ScrollAlignment::kAlignCenterAlways,
                               ScrollAlignment::kAlignCenterAlways,
@@ -406,7 +407,7 @@
   EXPECT_EQ(ScrollOffset(0, 10), visual_viewport->GetScrollOffset());
 
   root_frame_viewport->ScrollIntoView(
-      layout_viewport->DocumentToFrame(LayoutRect(
+      layout_viewport->DocumentToFrame(PhysicalRect(
           root_frame_viewport->VisibleContentRect(kExcludeScrollbars))),
       WebScrollIntoViewParams(
           ScrollAlignment::kAlignTopAlways, ScrollAlignment::kAlignTopAlways,
diff --git a/third_party/blink/renderer/core/frame/rotation_viewport_anchor.cc b/third_party/blink/renderer/core/frame/rotation_viewport_anchor.cc
index fa7863d..ce59e84 100644
--- a/third_party/blink/renderer/core/frame/rotation_viewport_anchor.cc
+++ b/third_party/blink/renderer/core/frame/rotation_viewport_anchor.cc
@@ -128,7 +128,7 @@
       FloatPoint(root_frame_viewport->VisibleContentRect().Location());
 
   anchor_node_.Clear();
-  anchor_node_bounds_ = LayoutRect();
+  anchor_node_bounds_ = PhysicalRect();
   anchor_in_node_coords_ = FloatSize();
   normalized_visual_viewport_offset_ = FloatSize();
 
@@ -169,9 +169,9 @@
 
   anchor_node_ = node;
   anchor_node_bounds_ = root_frame_view_->FrameToDocument(
-      LayoutRect(node->GetLayoutObject()->AbsoluteBoundingBoxRect()));
+      PhysicalRect(node->GetLayoutObject()->AbsoluteBoundingBoxRect()));
   anchor_in_node_coords_ =
-      anchor_point_in_document - FloatPoint(anchor_node_bounds_.Location());
+      anchor_point_in_document - FloatPoint(anchor_node_bounds_.offset);
   anchor_in_node_coords_.Scale(1.f / anchor_node_bounds_.Width(),
                                1.f / anchor_node_bounds_.Height());
 }
@@ -242,24 +242,24 @@
       !anchor_node_->GetLayoutObject())
     return visual_viewport_in_document_;
 
-  const LayoutRect current_node_bounds = root_frame_view_->FrameToDocument(
-      LayoutRect(anchor_node_->GetLayoutObject()->AbsoluteBoundingBoxRect()));
+  const PhysicalRect current_node_bounds = root_frame_view_->FrameToDocument(
+      PhysicalRect(anchor_node_->GetLayoutObject()->AbsoluteBoundingBoxRect()));
   if (anchor_node_bounds_ == current_node_bounds)
     return visual_viewport_in_document_;
 
   RootFrameViewport* root_frame_viewport =
       root_frame_view_->GetRootFrameViewport();
-  const LayoutRect current_node_bounds_in_layout_viewport =
+  const PhysicalRect current_node_bounds_in_layout_viewport =
       root_frame_viewport->RootContentsToLayoutViewportContents(
           *root_frame_view_.Get(), current_node_bounds);
 
   // Compute the new anchor point relative to the node position
   FloatSize anchor_offset_from_node(
-      current_node_bounds_in_layout_viewport.Size());
+      current_node_bounds_in_layout_viewport.size);
   anchor_offset_from_node.Scale(anchor_in_node_coords_.Width(),
                                 anchor_in_node_coords_.Height());
   FloatPoint anchor_point =
-      FloatPoint(current_node_bounds_in_layout_viewport.Location()) +
+      FloatPoint(current_node_bounds_in_layout_viewport.offset) +
       anchor_offset_from_node;
 
   // Compute the new origin point relative to the new anchor point
diff --git a/third_party/blink/renderer/core/frame/rotation_viewport_anchor.h b/third_party/blink/renderer/core/frame/rotation_viewport_anchor.h
index 6ee1c76..6eb40056c 100644
--- a/third_party/blink/renderer/core/frame/rotation_viewport_anchor.h
+++ b/third_party/blink/renderer/core/frame/rotation_viewport_anchor.h
@@ -6,10 +6,9 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_ROTATION_VIEWPORT_ANCHOR_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
 #include "third_party/blink/renderer/platform/geometry/float_size.h"
 #include "third_party/blink/renderer/platform/geometry/int_point.h"
-#include "third_party/blink/renderer/platform/geometry/int_rect.h"
-#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 
 namespace blink {
@@ -65,7 +64,7 @@
   Member<Node> anchor_node_;
 
   // In Document coordinates.
-  LayoutRect anchor_node_bounds_;
+  PhysicalRect anchor_node_bounds_;
 
   FloatSize anchor_in_inner_view_coords_;
   FloatSize anchor_in_node_coords_;
diff --git a/third_party/blink/renderer/core/frame/visual_viewport_test.cc b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
index 66f221f..52e6c61 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport_test.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
@@ -2021,8 +2021,7 @@
 
   Element* output = GetFrame()->GetDocument()->getElementById("output");
   DCHECK(output);
-  EXPECT_EQ(std::string("1600x1200"),
-            std::string(output->InnerHTMLAsString().Ascii().data()));
+  EXPECT_EQ("1600x1200", output->InnerHTMLAsString());
 }
 
 // Similar to above but make sure the initial scale is updated with the content
@@ -2037,8 +2036,7 @@
 
   Element* output = GetFrame()->GetDocument()->getElementById("output");
   DCHECK(output);
-  EXPECT_EQ(std::string("2000x1500"),
-            std::string(output->InnerHTMLAsString().Ascii().data()));
+  EXPECT_EQ("2000x1500", output->InnerHTMLAsString());
 }
 
 TEST_P(VisualViewportTest, ResizeWithScrollAnchoring) {
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
index 9b28d20..303223c 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
@@ -77,7 +77,7 @@
     const gfx::Point& point_in_root_frame,
     bool ignore_clipping) const {
   HitTestLocation location(local_root_->GetFrameView()->ConvertFromRootFrame(
-      LayoutPoint(IntPoint(point_in_root_frame))));
+      PhysicalOffset(IntPoint(point_in_root_frame))));
   HitTestRequest::HitTestRequestType hit_type =
       HitTestRequest::kReadOnly | HitTestRequest::kActive |
       (ignore_clipping ? HitTestRequest::kIgnoreClipping : 0);
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index 28c5d4eb..af01358 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -540,7 +540,7 @@
   if (!element->GetLayoutObject())
     return false;
 
-  LayoutRect rect_to_scroll;
+  PhysicalRect rect_to_scroll;
   WebScrollIntoViewParams params;
   GetScrollParamsForFocusedEditableElement(*element, rect_to_scroll, params);
   element->GetLayoutObject()->ScrollRectToVisible(rect_to_scroll, params);
@@ -696,7 +696,8 @@
   // Take capture on a mouse down on a plugin so we can send it mouse events.
   // If the hit node is a plugin but a scrollbar is over it don't start mouse
   // capture because it will interfere with the scrollbar receiving events.
-  LayoutPoint point(event.PositionInWidget().x, event.PositionInWidget().y);
+  PhysicalOffset point(LayoutUnit(event.PositionInWidget().x),
+                       LayoutUnit(event.PositionInWidget().y));
   if (event.button == WebMouseEvent::Button::kLeft) {
     HitTestLocation location(
         LocalRootImpl()->GetFrameView()->ConvertFromRootFrame(point));
@@ -752,7 +753,8 @@
       FlooredIntPoint(transformed_event.PositionInRootFrame());
 
   // Find the right target frame. See issue 1186900.
-  HitTestResult result = HitTestResultForRootFramePos(position_in_root_frame);
+  HitTestResult result =
+      HitTestResultForRootFramePos(PhysicalOffset(position_in_root_frame));
   Frame* target_frame;
   if (result.InnerNodeOrImageMapImage())
     target_frame = result.InnerNodeOrImageMapImage()->GetDocument().GetFrame();
@@ -1067,8 +1069,8 @@
   DocumentLifecycle::AllowThrottlingScope throttling_scope(
       LocalRootImpl()->GetFrame()->GetDocument()->Lifecycle());
   LocalFrameView* view = LocalRootImpl()->GetFrameView();
-  IntPoint point_in_root_frame =
-      view->ViewportToFrame(IntPoint(point_in_viewport));
+  PhysicalOffset point_in_root_frame(
+      view->ViewportToFrame(IntPoint(point_in_viewport)));
   return HitTestResultForRootFramePos(point_in_root_frame);
 }
 
@@ -1078,10 +1080,10 @@
 }
 
 HitTestResult WebFrameWidgetImpl::HitTestResultForRootFramePos(
-    const LayoutPoint& pos_in_root_frame) {
-  LayoutPoint doc_point(
+    const PhysicalOffset& pos_in_root_frame) {
+  PhysicalOffset doc_point =
       LocalRootImpl()->GetFrame()->View()->ConvertFromRootFrame(
-          pos_in_root_frame));
+          pos_in_root_frame);
   HitTestLocation location(doc_point);
   HitTestResult result =
       LocalRootImpl()->GetFrame()->GetEventHandler().HitTestResultAtLocation(
@@ -1108,7 +1110,7 @@
 
 void WebFrameWidgetImpl::GetScrollParamsForFocusedEditableElement(
     const Element& element,
-    LayoutRect& rect_to_scroll,
+    PhysicalRect& rect_to_scroll,
     WebScrollIntoViewParams& params) {
   LocalFrameView& frame_view = *element.GetDocument().View();
   IntRect absolute_element_bounds =
@@ -1151,7 +1153,7 @@
   params.relative_caret_bounds = NormalizeRect(
       Intersection(absolute_caret_bounds, maximal_rect), maximal_rect);
   params.behavior = WebScrollIntoViewParams::kInstant;
-  rect_to_scroll = LayoutRect(maximal_rect);
+  rect_to_scroll = PhysicalRect(maximal_rect);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
index 9961a1b..d332bfd 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -153,7 +153,7 @@
 
   // Perform a hit test for a point relative to the root frame of the page.
   HitTestResult HitTestResultForRootFramePos(
-      const LayoutPoint& pos_in_root_frame);
+      const PhysicalOffset& pos_in_root_frame);
 
   void SetIsAcceleratedCompositingActive(bool);
   void UpdateLayerTreeViewport();
@@ -178,7 +178,7 @@
   // includes the caret and is with respect to absolute coordinates.
   void GetScrollParamsForFocusedEditableElement(
       const Element& element,
-      LayoutRect& rect_to_scroll,
+      PhysicalRect& rect_to_scroll,
       WebScrollIntoViewParams& params);
 
   base::Optional<WebSize> size_;
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
index 858c10a..022cdb0 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
@@ -152,7 +152,7 @@
   virtual void StyleDidChange(const ComputedStyle* old_style,
                               const ComputedStyle& new_style) {}
   virtual HitTestCanvasResult* GetControlAndIdIfHitRegionExists(
-      const LayoutPoint& location) {
+      const PhysicalOffset& location) {
     NOTREACHED();
     return MakeGarbageCollected<HitTestCanvasResult>(String(), nullptr);
   }
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 8b17f0e..cee6954 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -784,11 +784,6 @@
     context_->MarkLayerComposited();
 }
 
-bool HTMLCanvasElement::IsAnimated2d() const {
-  return Is2d() && canvas2d_bridge_ &&
-         canvas2d_bridge_->WasDrawnToAfterSnapshot();
-}
-
 void HTMLCanvasElement::SetSurfaceSize(const IntSize& size) {
   size_ = size;
   did_fail_to_create_resource_provider_ = false;
@@ -1401,7 +1396,7 @@
 }
 
 HitTestCanvasResult* HTMLCanvasElement::GetControlAndIdIfHitRegionExists(
-    const LayoutPoint& location) {
+    const PhysicalOffset& location) {
   if (Is2d())
     return context_->GetControlAndIdIfHitRegionExists(location);
   return MakeGarbageCollected<HitTestCanvasResult>(String(), nullptr);
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
index 161f84b..90131dd 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
@@ -168,8 +168,6 @@
   bool OriginClean() const override;
   void SetOriginTainted() override { origin_clean_ = false; }
 
-  bool IsAnimated2d() const;
-
   Canvas2DLayerBridge* GetCanvas2DLayerBridge() {
     return canvas2d_bridge_.get();
   }
@@ -259,7 +257,7 @@
 
   // For Canvas HitRegions
   bool IsSupportedInteractiveCanvasFallback(const Element&);
-  HitTestCanvasResult* GetControlAndIdIfHitRegionExists(const LayoutPoint&);
+  HitTestCanvasResult* GetControlAndIdIfHitRegionExists(const PhysicalOffset&);
   String GetIdFromControl(const Element*);
 
   // For OffscreenCanvas that controls this html canvas element
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.cc b/third_party/blink/renderer/core/html/forms/html_select_element.cc
index cf67296..025ccb51 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.cc
@@ -915,7 +915,7 @@
   GetDocument().UpdateStyleAndLayout();
   if (!GetLayoutObject() || !GetLayoutObject()->IsListBox())
     return;
-  LayoutRect bounds = option->BoundingBoxForScrollIntoView();
+  PhysicalRect bounds = option->BoundingBoxForScrollIntoView();
   ToLayoutListBox(GetLayoutObject())->ScrollToRect(bounds);
 }
 
diff --git a/third_party/blink/renderer/core/html/html_area_element.cc b/third_party/blink/renderer/core/html/html_area_element.cc
index b8a6256..3df9f0a 100644
--- a/third_party/blink/renderer/core/html/html_area_element.cc
+++ b/third_party/blink/renderer/core/html/html_area_element.cc
@@ -86,15 +86,15 @@
   path_ = nullptr;
 }
 
-bool HTMLAreaElement::PointInArea(const LayoutPoint& location,
+bool HTMLAreaElement::PointInArea(const PhysicalOffset& location,
                                   const LayoutObject* container_object) const {
   return GetPath(container_object).Contains(FloatPoint(location));
 }
 
-LayoutRect HTMLAreaElement::ComputeAbsoluteRect(
+PhysicalRect HTMLAreaElement::ComputeAbsoluteRect(
     const LayoutObject* container_object) const {
   if (!container_object)
-    return LayoutRect();
+    return PhysicalRect();
 
   // FIXME: This doesn't work correctly with transforms.
   PhysicalOffset abs_pos = container_object->LocalToAbsolutePoint(
@@ -102,7 +102,7 @@
 
   Path path = GetPath(container_object);
   path.Translate(FloatSize(abs_pos));
-  return EnclosingLayoutRect(path.BoundingRect());
+  return PhysicalRect::EnclosingRect(path.BoundingRect());
 }
 
 Path HTMLAreaElement::GetPath(const LayoutObject* container_object) const {
diff --git a/third_party/blink/renderer/core/html/html_area_element.h b/third_party/blink/renderer/core/html/html_area_element.h
index 6dece51..2e6f36c 100644
--- a/third_party/blink/renderer/core/html/html_area_element.h
+++ b/third_party/blink/renderer/core/html/html_area_element.h
@@ -27,7 +27,7 @@
 #include "third_party/blink/public/platform/web_focus_type.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/html/html_anchor_element.h"
-#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
 
 namespace blink {
 
@@ -49,9 +49,9 @@
   // specified container object, e.g.  the rectangle of the default shape will
   // be the border box rect of the container object, and effective zoom factor
   // of the container object will be applied on non-default shape.
-  bool PointInArea(const LayoutPoint&,
+  bool PointInArea(const PhysicalOffset&,
                    const LayoutObject* container_object) const;
-  LayoutRect ComputeAbsoluteRect(const LayoutObject* container_object) const;
+  PhysicalRect ComputeAbsoluteRect(const LayoutObject* container_object) const;
   Path GetPath(const LayoutObject* container_object) const;
 
   // The parent map's image.
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index 3c42c94..089695c3 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -1543,6 +1543,6 @@
 void dumpInnerHTML(blink::HTMLElement*);
 
 void dumpInnerHTML(blink::HTMLElement* element) {
-  printf("%s\n", element->InnerHTMLAsString().Ascii().data());
+  printf("%s\n", element->InnerHTMLAsString().Ascii().c_str());
 }
 #endif
diff --git a/third_party/blink/renderer/core/html/html_map_element.cc b/third_party/blink/renderer/core/html/html_map_element.cc
index 2755e32..85c56a5 100644
--- a/third_party/blink/renderer/core/html/html_map_element.cc
+++ b/third_party/blink/renderer/core/html/html_map_element.cc
@@ -43,7 +43,7 @@
 HTMLMapElement::~HTMLMapElement() = default;
 
 HTMLAreaElement* HTMLMapElement::AreaForPoint(
-    const LayoutPoint& location,
+    const PhysicalOffset& location,
     const LayoutObject* container_object) {
   HTMLAreaElement* default_area = nullptr;
   for (HTMLAreaElement& area :
diff --git a/third_party/blink/renderer/core/html/html_map_element.h b/third_party/blink/renderer/core/html/html_map_element.h
index 76ab0844..56e936d 100644
--- a/third_party/blink/renderer/core/html/html_map_element.h
+++ b/third_party/blink/renderer/core/html/html_map_element.h
@@ -29,6 +29,7 @@
 namespace blink {
 
 class HTMLImageElement;
+struct PhysicalOffset;
 
 class CORE_EXPORT HTMLMapElement final : public HTMLElement {
   DEFINE_WRAPPERTYPEINFO();
@@ -39,7 +40,7 @@
 
   const AtomicString& GetName() const { return name_; }
 
-  HTMLAreaElement* AreaForPoint(const LayoutPoint&,
+  HTMLAreaElement* AreaForPoint(const PhysicalOffset&,
                                 const LayoutObject* container_object);
 
   HTMLImageElement* ImageElement();
diff --git a/third_party/blink/renderer/core/html/parser/html_srcset_parser_test.cc b/third_party/blink/renderer/core/html/parser/html_srcset_parser_test.cc
index a94eb2e5..cc3aed6 100644
--- a/third_party/blink/renderer/core/html/parser/html_srcset_parser_test.cc
+++ b/third_party/blink/renderer/core/html/parser/html_srcset_parser_test.cc
@@ -169,7 +169,7 @@
         test.srcset_input);
     ASSERT_EQ(test.output_density, candidate.Density());
     ASSERT_EQ(test.output_resource_width, candidate.GetResourceWidth());
-    ASSERT_STREQ(test.output_url, candidate.ToString().Ascii().data());
+    ASSERT_EQ(test.output_url, candidate.ToString().Ascii());
   }
 }
 
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc
index fecd45f..a1f929f 100644
--- a/third_party/blink/renderer/core/input/event_handler.cc
+++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -241,9 +241,21 @@
   AutoscrollController* controller = scroll_manager_->GetAutoscrollController();
   if (!controller)
     return;
+  LayoutBox* scrollable = LayoutBox::FindAutoscrollable(layout_object);
+  Page* page = frame_->GetPage();
+  bool vertical_scroll_offset = false;
+  bool horizontal_scroll_offset = false;
+  if (page) {
+    ScrollOffset maximum_scroll_offset =
+        page->GetVisualViewport().MaximumScrollOffset();
+    vertical_scroll_offset = maximum_scroll_offset.Height() > 0;
+    horizontal_scroll_offset = maximum_scroll_offset.Width() > 0;
+  }
   controller->StartMiddleClickAutoscroll(
       layout_object->GetFrame(), LastKnownMousePositionInRootFrame(),
-      mouse_event_manager_->LastKnownMouseScreenPosition());
+      mouse_event_manager_->LastKnownMouseScreenPosition(),
+      scrollable->HasScrollableOverflowY() || vertical_scroll_offset,
+      scrollable->HasScrollableOverflowX() || horizontal_scroll_offset);
   mouse_event_manager_->InvalidateClick();
 }
 
@@ -297,20 +309,18 @@
           DCHECK(location.IsRectilinear());
           if (hit_type & HitTestRequest::kHitTestVisualOverflow) {
             // Apply ancestor transforms to location rect
-            PhysicalRect local_rect =
-                PhysicalRectToBeNoop(location.BoundingBox());
+            PhysicalRect local_rect = location.BoundingBox();
             PhysicalRect main_frame_rect =
                 frame_view->GetLayoutView()->LocalToAncestorRect(
                     local_rect, main_view->GetLayoutView(),
                     kTraverseDocumentBoundaries);
-            adjusted_location = HitTestLocation(main_frame_rect.ToLayoutRect());
+            adjusted_location = HitTestLocation(main_frame_rect);
           } else {
             // Don't apply ancestor transforms to bounding box
-            LayoutPoint main_content_point =
-                main_view->ConvertFromRootFrame(frame_view->ConvertToRootFrame(
-                    location.BoundingBox().Location()));
+            PhysicalOffset main_content_point = main_view->ConvertFromRootFrame(
+                frame_view->ConvertToRootFrame(location.BoundingBox().offset));
             adjusted_location = HitTestLocation(
-                LayoutRect(main_content_point, location.BoundingBox().Size()));
+                PhysicalRect(main_content_point, location.BoundingBox().size));
           }
         } else {
           adjusted_location = HitTestLocation(main_view->ConvertFromRootFrame(
@@ -480,8 +490,7 @@
 
   if (layout_object) {
     Cursor override_cursor;
-    switch (layout_object->GetCursor(RoundedIntPoint(result.LocalPoint()),
-                                     override_cursor)) {
+    switch (layout_object->GetCursor(result.LocalPoint(), override_cursor)) {
       case kSetCursorBasedOnStyle:
         break;
       case kSetCursor:
@@ -661,8 +670,8 @@
   HitTestRequest request(HitTestRequest::kActive);
   // Save the document point we generate in case the window coordinate is
   // invalidated by what happens when we dispatch the event.
-  LayoutPoint document_point = frame_->View()->ConvertFromRootFrame(
-      FlooredIntPoint(mouse_event.PositionInRootFrame()));
+  PhysicalOffset document_point = frame_->View()->ConvertFromRootFrame(
+      PhysicalOffset(FlooredIntPoint(mouse_event.PositionInRootFrame())));
   MouseEventWithHitTestResults mev = GetMouseEventTarget(request, mouse_event);
   if (!mev.InnerNode()) {
     mouse_event_manager_->InvalidateClick();
@@ -923,7 +932,7 @@
   if (pointer_event_manager_->IsAnyTouchActive() && !force_leave)
     hit_type |= HitTestRequest::kActive | HitTestRequest::kReadOnly;
   HitTestRequest request(hit_type);
-  HitTestLocation out_location((LayoutPoint()));
+  HitTestLocation out_location((PhysicalOffset()));
   MouseEventWithHitTestResults mev = MouseEventWithHitTestResults(
       mouse_event, out_location, HitTestResult(request, out_location));
 
@@ -1793,9 +1802,10 @@
     hit_test_result = root_frame.GetEventHandler().HitTestResultAtLocation(
         location, hit_type);
   } else {
-    LayoutPoint top_left(adjusted_event.PositionInRootFrame());
-    top_left.Move(-hit_rect_size * 0.5f);
-    location = HitTestLocation(LayoutRect(top_left, hit_rect_size));
+    PhysicalOffset top_left = PhysicalOffset::FromFloatPointRound(
+        adjusted_event.PositionInRootFrame());
+    top_left -= PhysicalOffset(hit_rect_size * 0.5f);
+    location = HitTestLocation(PhysicalRect(top_left, hit_rect_size));
     hit_test_result = root_frame.GetEventHandler().HitTestResultAtLocation(
         location, hit_type);
   }
@@ -1866,7 +1876,7 @@
   // FIXME: We should do this even when no candidate matches the node filter.
   // crbug.com/398914
   if (adjusted) {
-    LayoutPoint point = frame_->View()->ConvertFromRootFrame(adjusted_point);
+    PhysicalOffset point(frame_->View()->ConvertFromRootFrame(adjusted_point));
     DCHECK(location.ContainsPoint(FloatPoint(point)));
     DCHECK(location.IsRectBasedTest());
     location = hit_test_result->ResolveRectBasedTest(adjusted_node, point);
@@ -1888,8 +1898,8 @@
   if (last_scrollbar_under_mouse_)
     last_scrollbar_under_mouse_->MouseUp(event);
 
-  LayoutPoint position_in_contents =
-      v->ConvertFromRootFrame(FlooredIntPoint(event.PositionInRootFrame()));
+  PhysicalOffset position_in_contents(
+      v->ConvertFromRootFrame(FlooredIntPoint(event.PositionInRootFrame())));
   HitTestRequest request(HitTestRequest::kActive);
   MouseEventWithHitTestResults mev =
       frame_->GetDocument()->PerformMouseEventHitTest(
@@ -2274,8 +2284,9 @@
 MouseEventWithHitTestResults EventHandler::GetMouseEventTarget(
     const HitTestRequest& request,
     const WebMouseEvent& event) {
-  LayoutPoint document_point = event_handling_util::ContentPointFromRootFrame(
-      frame_, event.PositionInRootFrame());
+  PhysicalOffset document_point =
+      event_handling_util::ContentPointFromRootFrame(
+          frame_, event.PositionInRootFrame());
 
   // TODO(eirage): This does not handle chorded buttons yet.
   if (RuntimeEnabledFeatures::UnifiedPointerCaptureInBlinkEnabled() &&
@@ -2293,14 +2304,9 @@
 
     if (capture_target) {
       LayoutObject* layout_object = capture_target->GetLayoutObject();
-
-      LayoutPoint local_point =
-          layout_object ? layout_object
-                              ->AbsoluteToLocalPoint(
-                                  PhysicalOffsetToBeNoop(document_point))
-                              .ToLayoutPoint()
+      PhysicalOffset local_point =
+          layout_object ? layout_object->AbsoluteToLocalPoint(document_point)
                         : document_point;
-
       result.SetNodeAndPosition(capture_target, local_point);
 
       result.SetScrollbar(last_scrollbar_under_mouse_);
diff --git a/third_party/blink/renderer/core/input/event_handler_test.cc b/third_party/blink/renderer/core/input/event_handler_test.cc
index 2b94c53..ccfd1ca 100644
--- a/third_party/blink/renderer/core/input/event_handler_test.cc
+++ b/third_party/blink/renderer/core/input/event_handler_test.cc
@@ -35,6 +35,7 @@
 #include "third_party/blink/renderer/core/page/autoscroll_controller.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
+#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
@@ -360,7 +361,7 @@
 
 TEST_F(EventHandlerTest, HitOnNothingDoesNotShowIBeam) {
   SetHtmlInnerHTML("");
-  HitTestLocation location((LayoutPoint(10, 10)));
+  HitTestLocation location((PhysicalOffset(10, 10)));
   HitTestResult hit =
       GetDocument().GetFrame()->GetEventHandler().HitTestResultAtLocation(
           location);
diff --git a/third_party/blink/renderer/core/input/event_handling_util.cc b/third_party/blink/renderer/core/input/event_handling_util.cc
index 6139ed7..0125ff2 100644
--- a/third_party/blink/renderer/core/input/event_handling_util.cc
+++ b/third_party/blink/renderer/core/input/event_handling_util.cc
@@ -27,7 +27,7 @@
   if (!frame || !frame->ContentLayoutObject())
     return result;
   if (LocalFrameView* frame_view = frame->View()) {
-    LayoutRect rect(LayoutPoint(), LayoutSize(frame_view->Size()));
+    PhysicalRect rect(PhysicalOffset(), PhysicalSize(frame_view->Size()));
     if (!location.Intersects(rect))
       return result;
   }
@@ -104,13 +104,15 @@
   return FlatTreeTraversal::Parent(node);
 }
 
-LayoutPoint ContentPointFromRootFrame(LocalFrame* frame,
-                                      const FloatPoint& point_in_root_frame) {
+PhysicalOffset ContentPointFromRootFrame(
+    LocalFrame* frame,
+    const FloatPoint& point_in_root_frame) {
   LocalFrameView* view = frame->View();
   // FIXME: Is it really OK to use the wrong coordinates here when view is 0?
   // Historically the code would just crash; this is clearly no worse than that.
-  return LayoutPoint(view ? view->ConvertFromRootFrame(point_in_root_frame)
-                          : point_in_root_frame);
+  return PhysicalOffset::FromFloatPointRound(
+      view ? view->ConvertFromRootFrame(point_in_root_frame)
+           : point_in_root_frame);
 }
 
 MouseEventWithHitTestResults PerformMouseEventHitTest(
diff --git a/third_party/blink/renderer/core/input/event_handling_util.h b/third_party/blink/renderer/core/input/event_handling_util.h
index 1883ce8..36871eb 100644
--- a/third_party/blink/renderer/core/input/event_handling_util.h
+++ b/third_party/blink/renderer/core/input/event_handling_util.h
@@ -8,9 +8,9 @@
 #include "third_party/blink/public/platform/web_input_event_result.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
 #include "third_party/blink/renderer/core/page/event_with_hit_test_results.h"
-#include "third_party/blink/renderer/platform/geometry/layout_point.h"
 
 namespace blink {
 
@@ -40,8 +40,8 @@
 
 ContainerNode* ParentForClickEvent(const Node&);
 
-LayoutPoint ContentPointFromRootFrame(LocalFrame*,
-                                      const FloatPoint& point_in_root_frame);
+PhysicalOffset ContentPointFromRootFrame(LocalFrame*,
+                                         const FloatPoint& point_in_root_frame);
 
 MouseEventWithHitTestResults PerformMouseEventHitTest(LocalFrame*,
                                                       const HitTestRequest&,
diff --git a/third_party/blink/renderer/core/input/fallback_cursor_event_manager.cc b/third_party/blink/renderer/core/input/fallback_cursor_event_manager.cc
index 04ae7f8..2de24b1 100644
--- a/third_party/blink/renderer/core/input/fallback_cursor_event_manager.cc
+++ b/third_party/blink/renderer/core/input/fallback_cursor_event_manager.cc
@@ -75,11 +75,9 @@
   LocalFrameView* view = box->GetFrameView();
   DCHECK(view);
 
-  LayoutRect layout_rect =
-      LayoutRect(scrollable.VisibleContentRect(blink::kIncludeScrollbars));
-  layout_rect = view->DocumentToFrame(layout_rect);
-  IntRect rect = view->ConvertToRootFrame(EnclosedIntRect(layout_rect));
-  return rect.Size();
+  PhysicalRect rect(scrollable.VisibleContentRect(blink::kIncludeScrollbars));
+  rect = view->DocumentToFrame(rect);
+  return view->ConvertToRootFrame(EnclosedIntRect(FloatRect(rect))).Size();
 }
 
 IntPoint RootFrameLocationToScrollable(const IntPoint& location_in_root_frame,
diff --git a/third_party/blink/renderer/core/input/gesture_manager.cc b/third_party/blink/renderer/core/input/gesture_manager.cc
index 5937164..d73ace65 100644
--- a/third_party/blink/renderer/core/input/gesture_manager.cc
+++ b/third_party/blink/renderer/core/input/gesture_manager.cc
@@ -427,8 +427,8 @@
 
   if (!suppress_mouse_events_from_gestures_ && frame_->View()) {
     HitTestRequest request(HitTestRequest::kActive);
-    LayoutPoint document_point = frame_->View()->ConvertFromRootFrame(
-        FlooredIntPoint(targeted_event.Event().PositionInRootFrame()));
+    PhysicalOffset document_point(frame_->View()->ConvertFromRootFrame(
+        FlooredIntPoint(targeted_event.Event().PositionInRootFrame())));
     MouseEventWithHitTestResults mev =
         frame_->GetDocument()->PerformMouseEventHitTest(request, document_point,
                                                         mouse_event);
diff --git a/third_party/blink/renderer/core/input/mouse_event_manager.cc b/third_party/blink/renderer/core/input/mouse_event_manager.cc
index 38265676..ba9d7871 100644
--- a/third_party/blink/renderer/core/input/mouse_event_manager.cc
+++ b/third_party/blink/renderer/core/input/mouse_event_manager.cc
@@ -135,7 +135,7 @@
   mouse_down_timestamp_ = TimeTicks();
   mouse_down_ = WebMouseEvent();
   svg_pan_ = false;
-  drag_start_pos_ = LayoutPoint();
+  drag_start_pos_ = PhysicalOffset();
   hover_state_dirty_ = false;
   fake_mouse_move_event_timer_.Stop();
   ResetDragSource();
@@ -732,7 +732,8 @@
 
   mouse_press_node_ = inner_node;
   frame_->GetDocument()->SetSequentialFocusNavigationStartingPoint(inner_node);
-  drag_start_pos_ = FlooredIntPoint(event.Event().PositionInRootFrame());
+  drag_start_pos_ =
+      PhysicalOffset(FlooredIntPoint(event.Event().PositionInRootFrame()));
 
   mouse_pressed_ = true;
 
@@ -767,8 +768,9 @@
 void MouseEventManager::UpdateSelectionForMouseDrag() {
   frame_->GetEventHandler()
       .GetSelectionController()
-      .UpdateSelectionForMouseDrag(drag_start_pos_,
-                                   LayoutPoint(last_known_mouse_position_));
+      .UpdateSelectionForMouseDrag(
+          drag_start_pos_,
+          PhysicalOffset::FromFloatPointRound(last_known_mouse_position_));
 }
 
 bool MouseEventManager::HandleDragDropIfPossible(
@@ -880,7 +882,7 @@
 
   frame_->GetEventHandler().GetSelectionController().HandleMouseDraggedEvent(
       event, mouse_down_pos_, drag_start_pos_,
-      LayoutPoint(last_known_mouse_position_));
+      PhysicalOffset::FromFloatPointRound(last_known_mouse_position_));
 
   // The call into HandleMouseDraggedEvent may have caused a re-layout,
   // so get the LayoutObject again.
diff --git a/third_party/blink/renderer/core/input/mouse_event_manager.h b/third_party/blink/renderer/core/input/mouse_event_manager.h
index a011f5e..6cfa6fb6 100644
--- a/third_party/blink/renderer/core/input/mouse_event_manager.h
+++ b/third_party/blink/renderer/core/input/mouse_event_manager.h
@@ -251,7 +251,7 @@
   TimeTicks mouse_down_timestamp_;
   WebMouseEvent mouse_down_;
 
-  LayoutPoint drag_start_pos_;
+  PhysicalOffset drag_start_pos_;
   // This indicates that whether we should update the hover at each begin
   // frame. This is set to be true after the compositor or main thread scroll
   // ends, and at each begin frame, we will dispatch a fake mouse move event to
diff --git a/third_party/blink/renderer/core/input/mouse_wheel_event_manager.cc b/third_party/blink/renderer/core/input/mouse_wheel_event_manager.cc
index 824e1e8..66cb45e 100644
--- a/third_party/blink/renderer/core/input/mouse_wheel_event_manager.cc
+++ b/third_party/blink/renderer/core/input/mouse_wheel_event_manager.cc
@@ -116,8 +116,8 @@
                                              const Document* doc,
                                              const LocalFrameView* view) {
   DCHECK(doc && doc->GetLayoutView() && view);
-  LayoutPoint v_point =
-      view->ConvertFromRootFrame(FlooredIntPoint(event.PositionInRootFrame()));
+  PhysicalOffset v_point(
+      view->ConvertFromRootFrame(FlooredIntPoint(event.PositionInRootFrame())));
 
   HitTestRequest request(HitTestRequest::kReadOnly);
   HitTestLocation location(v_point);
diff --git a/third_party/blink/renderer/core/input/pointer_event_manager.cc b/third_party/blink/renderer/core/input/pointer_event_manager.cc
index 70d524b..ba9752f29 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager.cc
+++ b/third_party/blink/renderer/core/input/pointer_event_manager.cc
@@ -367,9 +367,10 @@
       HitTestRequest::kActive | HitTestRequest::kListBased;
   LocalFrame& root_frame = frame_->LocalFrameRoot();
   // TODO(szager): Shouldn't this be PositionInScreen() ?
-  LayoutPoint hit_test_point((FloatPoint)pointer_event.PositionInWidget());
-  hit_test_point.Move(-hit_rect_size * 0.5f);
-  HitTestLocation location(LayoutRect(hit_test_point, hit_rect_size));
+  PhysicalOffset hit_test_point =
+      PhysicalOffset::FromFloatPointRound(pointer_event.PositionInWidget());
+  hit_test_point -= PhysicalOffset(hit_rect_size * 0.5f);
+  HitTestLocation location(PhysicalRect(hit_test_point, hit_rect_size));
   HitTestResult hit_test_result =
       root_frame.GetEventHandler().HitTestResultAtLocation(location, hit_type);
   Node* adjusted_node = nullptr;
@@ -423,7 +424,8 @@
                                                   HitTestRequest::kReadOnly |
                                                   HitTestRequest::kActive;
     HitTestLocation location(frame_->View()->ConvertFromRootFrame(
-        LayoutPoint(web_pointer_event.PositionInWidget())));
+        PhysicalOffset::FromFloatPointRound(
+            web_pointer_event.PositionInWidget())));
     HitTestResult hit_test_tesult =
         frame_->GetEventHandler().HitTestResultAtLocation(location, hit_type);
     Element* target = hit_test_tesult.InnerElement();
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc
index 43d90d3..f812de0 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.cc
+++ b/third_party/blink/renderer/core/input/scroll_manager.cc
@@ -316,9 +316,17 @@
         NOTREACHED();
     }
 
+    ScrollableArea::ScrollCallback callback;
+    if (RuntimeEnabledFeatures::UpdateHoverFromScrollAtBeginFrameEnabled()) {
+      callback = ScrollableArea::ScrollCallback(base::BindOnce(
+          [](WeakPersistent<ScrollableArea> area) {
+            if (area)
+              area->MarkHoverStateDirty();
+          },
+          scrollable_area));
+    }
     ScrollResult result = scrollable_area->UserScroll(
-        granularity, ToScrollDelta(physical_direction, 1),
-        ScrollableArea::ScrollCallback());
+        granularity, ToScrollDelta(physical_direction, 1), std::move(callback));
 
     if (result.DidScroll())
       return true;
@@ -850,8 +858,8 @@
                          TRACE_EVENT_SCOPE_THREAD);
 
     LocalFrameView* view = frame_->View();
-    LayoutPoint view_point = view->ConvertFromRootFrame(
-        FlooredIntPoint(gesture_event.PositionInRootFrame()));
+    PhysicalOffset view_point(view->ConvertFromRootFrame(
+        FlooredIntPoint(gesture_event.PositionInRootFrame())));
     HitTestRequest request(HitTestRequest::kReadOnly);
     HitTestLocation location(view_point);
     HitTestResult result(request, location);
diff --git a/third_party/blink/renderer/core/input/touch_action_test.cc b/third_party/blink/renderer/core/input/touch_action_test.cc
index 59a4c0a..b4097820 100644
--- a/third_party/blink/renderer/core/input/touch_action_test.cc
+++ b/third_party/blink/renderer/core/input/touch_action_test.cc
@@ -245,7 +245,7 @@
 
     std::string failure_context("Test case: ");
     if (element->HasID()) {
-      failure_context.append(element->GetIdAttribute().Ascii().data());
+      failure_context.append(element->GetIdAttribute().Ascii());
     } else if (element->firstChild()) {
       failure_context.append("\"");
       failure_context.append(element->firstChild()
@@ -358,8 +358,7 @@
                     client.LastTouchAction())
               << failure_context_pos;
         } else {
-          FAIL() << "Unrecognized expected-action \""
-                 << expected_action.Ascii().data() << "\" "
+          FAIL() << "Unrecognized expected-action " << expected_action << " "
                  << failure_context_pos;
         }
       }
diff --git a/third_party/blink/renderer/core/input/touch_event_manager.cc b/third_party/blink/renderer/core/input/touch_event_manager.cc
index a6fde8d..4604014d 100644
--- a/third_party/blink/renderer/core/input/touch_event_manager.cc
+++ b/third_party/blink/renderer/core/input/touch_event_manager.cc
@@ -529,7 +529,7 @@
   if (touch_sequence_document_ &&
       (!touch_node || &touch_node->GetDocument() != touch_sequence_document_)) {
     if (touch_sequence_document_->GetFrame()) {
-      HitTestLocation location(LayoutPoint(
+      HitTestLocation location(PhysicalOffset::FromFloatPointRound(
           touch_sequence_document_->GetFrame()->View()->ConvertFromRootFrame(
               event.PositionInWidget())));
       result = event_handling_util::HitTestResultInFrame(
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index a98d0202..4233ebe 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -2416,6 +2416,12 @@
       array of StringIndex text
       # Stacking context information.
       RareBooleanData stackingContexts
+      # The offset rect of nodes. Only available when includeDOMRects is set to true
+      optional array of Rectangle offsetRects
+      # The scroll rect of nodes. Only available when includeDOMRects is set to true
+      optional array of Rectangle scrollRects
+      # The client rect of nodes. Only available when includeDOMRects is set to true
+      optional array of Rectangle clientRects
 
   # Table of details of the post layout rendered text positions. The exact layout should not be regarded as
   # stable and may change between versions.
@@ -2468,6 +2474,8 @@
     parameters
       # Whitelist of computed styles to return.
       array of string computedStyles
+      # Whether to include DOM rectangles (offsetRects, clientRects, scrollRects) into the snapshot
+      optional boolean includeDOMRects
     returns
       # The nodes in the DOM tree. The DOMNode at index 0 corresponds to the root document.
       array of DocumentSnapshot documents
@@ -6756,16 +6764,19 @@
       running
       closed
 
-  # Fields in AudioContext that change in real-time. These are not updated
-  # on OfflineAudioContext.
+  # Fields in AudioContext that change in real-time.
   type ContextRealtimeData extends object
     properties
       # The current context time in second in BaseAudioContext.
-      optional number currentTime
+      number currentTime
       # The time spent on rendering graph divided by render qunatum duration,
       # and multiplied by 100. 100 means the audio renderer reached the full
       # capacity and glitch may occur.
-      optional number renderCapacity
+      number renderCapacity
+      # A running mean of callback interval.
+      number callbackIntervalMean
+      # A running variance of callback interval.
+      number callbackIntervalVariance
 
   # Protocol object for BaseAudioContext
   type BaseAudioContext extends object
diff --git a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
index ac058bc1..fe39b43 100644
--- a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
@@ -151,7 +151,7 @@
 }
 
 // Get the elements which overlap the given rectangle.
-HeapVector<Member<Element>> ElementsFromRect(LayoutRect rect,
+HeapVector<Member<Element>> ElementsFromRect(const PhysicalRect& rect,
                                              Document& document) {
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive |
                          HitTestRequest::kListBased |
@@ -242,7 +242,7 @@
 // walking up all the elements returned by a hit test (but not going beyond
 // |topElement|) covering the area of the rect, and blending their background
 // colors.
-bool GetColorsFromRect(LayoutRect rect,
+bool GetColorsFromRect(PhysicalRect rect,
                        Document& document,
                        Element* top_element,
                        Vector<Color>& colors) {
@@ -2343,7 +2343,7 @@
     return;
   }
 
-  LayoutRect content_bounds(text_node->BoundingBox());
+  PhysicalRect content_bounds = text_node->BoundingBox();
   LocalFrameView* view = text_node->GetDocument().View();
   if (!view)
     return;
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
index eeda7bd..84067ff 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
@@ -1322,8 +1322,9 @@
   bool include_user_agent_shadow_dom =
       optional_include_user_agent_shadow_dom.fromMaybe(false);
   Document* document = inspected_frames_->Root()->GetDocument();
-  LayoutPoint document_point(x * inspected_frames_->Root()->PageZoomFactor(),
-                             y * inspected_frames_->Root()->PageZoomFactor());
+  PhysicalOffset document_point(
+      LayoutUnit(x * inspected_frames_->Root()->PageZoomFactor()),
+      LayoutUnit(y * inspected_frames_->Root()->PageZoomFactor()));
   HitTestRequest request(HitTestRequest::kMove | HitTestRequest::kReadOnly |
                          HitTestRequest::kAllowChildFrameContent);
   HitTestLocation location(document->View()->DocumentToFrame(document_point));
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc
index 8e38ad39..6e72fa0 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc
@@ -48,8 +48,8 @@
 
 namespace {
 
-std::unique_ptr<protocol::DOM::Rect> BuildRectForLayoutRect(
-    const LayoutRect& rect) {
+std::unique_ptr<protocol::DOM::Rect> BuildRectForPhysicalRect(
+    const PhysicalRect& rect) {
   return protocol::DOM::Rect::create()
       .setX(rect.X())
       .setY(rect.Y())
@@ -58,8 +58,8 @@
       .build();
 }
 
-std::unique_ptr<protocol::Array<double>> BuildRectForLayoutRect2(
-    const LayoutRect& rect) {
+std::unique_ptr<protocol::Array<double>> BuildRectForPhysicalRect2(
+    const PhysicalRect& rect) {
   std::unique_ptr<protocol::Array<double>> result =
       protocol::Array<double>::create();
   result->addItem(rect.X());
@@ -69,9 +69,23 @@
   return result;
 }
 
+std::unique_ptr<protocol::Array<double>> BuildRectForLayout(const int x,
+                                                            const int y,
+                                                            const int width,
+                                                            const int height) {
+  std::unique_ptr<protocol::Array<double>> result =
+      protocol::Array<double>::create();
+  result->addItem(x);
+  result->addItem(y);
+  result->addItem(width);
+  result->addItem(height);
+  return result;
+}
+
 // Returns |layout_object|'s bounding box in document coordinates.
-LayoutRect RectInDocument(const LayoutObject* layout_object) {
-  LayoutRect rect_in_absolute(layout_object->AbsoluteBoundingBoxFloatRect());
+PhysicalRect RectInDocument(const LayoutObject* layout_object) {
+  PhysicalRect rect_in_absolute = PhysicalRect::EnclosingRect(
+      layout_object->AbsoluteBoundingBoxFloatRect());
   LocalFrameView* local_frame_view = layout_object->GetFrameView();
   // Don't do frame to document coordinate transformation for layout view,
   // whose bounding box is not affected by scroll offset.
@@ -80,17 +94,17 @@
   return rect_in_absolute;
 }
 
-LayoutRect TextFragmentRectInDocument(const LayoutObject* layout_object,
-                                      const LayoutText::TextBoxInfo& text_box) {
+PhysicalRect TextFragmentRectInDocument(
+    const LayoutObject* layout_object,
+    const LayoutText::TextBoxInfo& text_box) {
   PhysicalRect local_coords_text_box_rect =
       layout_object->FlipForWritingMode(text_box.local_rect);
   PhysicalRect absolute_coords_text_box_rect =
       layout_object->LocalToAbsoluteRect(local_coords_text_box_rect);
   LocalFrameView* local_frame_view = layout_object->GetFrameView();
-  return (local_frame_view
-              ? local_frame_view->FrameToDocument(absolute_coords_text_box_rect)
-              : absolute_coords_text_box_rect)
-      .ToLayoutRect();
+  return local_frame_view
+             ? local_frame_view->FrameToDocument(absolute_coords_text_box_rect)
+             : absolute_coords_text_box_rect;
 }
 
 Document* GetEmbeddedDocument(PaintLayer* layer) {
@@ -290,6 +304,7 @@
 
 protocol::Response InspectorDOMSnapshotAgent::captureSnapshot(
     std::unique_ptr<protocol::Array<String>> computed_styles,
+    protocol::Maybe<bool> include_dom_rects,
     std::unique_ptr<protocol::Array<protocol::DOMSnapshot::DocumentSnapshot>>*
         documents,
     std::unique_ptr<protocol::Array<String>>* strings) {
@@ -307,6 +322,8 @@
         std::make_pair(computed_styles->get(i), property_id));
   }
 
+  include_snapshot_dom_rects_ = include_dom_rects.fromMaybe(false);
+
   for (LocalFrame* frame : *inspected_frames_) {
     if (Document* document = frame->GetDocument())
       document_order_map_.Set(document, document_order_map_.size());
@@ -578,6 +595,15 @@
     document_->setScrollOffsetY(offset.Height());
   }
 
+  if (include_snapshot_dom_rects_) {
+    document_->getLayout()->setOffsetRects(
+        protocol::Array<protocol::Array<double>>::create());
+    document_->getLayout()->setClientRects(
+        protocol::Array<protocol::Array<double>>::create());
+    document_->getLayout()->setScrollRects(
+        protocol::Array<protocol::Array<double>>::create());
+  }
+
   VisitNode2(document, -1);
   documents_->addItem(std::move(document_));
 }
@@ -832,11 +858,11 @@
   if (!layout_object)
     return -1;
 
-  auto layout_tree_node =
-      protocol::DOMSnapshot::LayoutTreeNode::create()
-          .setDomNodeIndex(node_index)
-          .setBoundingBox(BuildRectForLayoutRect(RectInDocument(layout_object)))
-          .build();
+  auto layout_tree_node = protocol::DOMSnapshot::LayoutTreeNode::create()
+                              .setDomNodeIndex(node_index)
+                              .setBoundingBox(BuildRectForPhysicalRect(
+                                  RectInDocument(layout_object)))
+                              .build();
 
   int style_index = GetStyleIndexForNode(node);
   if (style_index != -1)
@@ -868,7 +894,7 @@
             protocol::DOMSnapshot::InlineTextBox::create()
                 .setStartCharacterIndex(text_box.dom_start_offset)
                 .setNumCharacters(text_box.dom_length)
-                .setBoundingBox(BuildRectForLayoutRect(
+                .setBoundingBox(BuildRectForPhysicalRect(
                     TextFragmentRectInDocument(layout_object, text_box)))
                 .build());
       }
@@ -894,7 +920,40 @@
   layout_tree_snapshot->getNodeIndex()->addItem(node_index);
   layout_tree_snapshot->getStyles()->addItem(BuildStylesForNode(node));
   layout_tree_snapshot->getBounds()->addItem(
-      BuildRectForLayoutRect2(RectInDocument(layout_object)));
+      BuildRectForPhysicalRect2(RectInDocument(layout_object)));
+
+  if (include_snapshot_dom_rects_) {
+    protocol::Array<protocol::Array<double>>* offsetRects =
+        layout_tree_snapshot->getOffsetRects(nullptr);
+    DCHECK(offsetRects);
+
+    protocol::Array<protocol::Array<double>>* clientRects =
+        layout_tree_snapshot->getClientRects(nullptr);
+    DCHECK(clientRects);
+
+    protocol::Array<protocol::Array<double>>* scrollRects =
+        layout_tree_snapshot->getScrollRects(nullptr);
+    DCHECK(scrollRects);
+
+    if (node->IsElementNode()) {
+      auto* element = ToElement(node);
+      offsetRects->addItem(
+          BuildRectForLayout(element->OffsetLeft(), element->OffsetTop(),
+                             element->OffsetWidth(), element->OffsetHeight()));
+
+      clientRects->addItem(
+          BuildRectForLayout(element->clientLeft(), element->clientTop(),
+                             element->clientWidth(), element->clientHeight()));
+
+      scrollRects->addItem(
+          BuildRectForLayout(element->scrollLeft(), element->scrollTop(),
+                             element->scrollWidth(), element->scrollHeight()));
+    } else {
+      offsetRects->addItem(protocol::Array<double>::create());
+      clientRects->addItem(protocol::Array<double>::create());
+      scrollRects->addItem(protocol::Array<double>::create());
+    }
+  }
 
   if (layout_object->Style() && layout_object->Style()->IsStackingContext())
     SetRare(layout_tree_snapshot->getStackingContexts(), layout_index);
@@ -913,7 +972,7 @@
 
   for (const auto& text_box : text_boxes) {
     text_box_snapshot->getLayoutIndex()->addItem(layout_index);
-    text_box_snapshot->getBounds()->addItem(BuildRectForLayoutRect2(
+    text_box_snapshot->getBounds()->addItem(BuildRectForPhysicalRect2(
         TextFragmentRectInDocument(layout_object, text_box)));
     text_box_snapshot->getStart()->addItem(text_box.dom_start_offset);
     text_box_snapshot->getLength()->addItem(text_box.dom_length);
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.h b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.h
index 9be646a..1982975 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.h
@@ -53,6 +53,7 @@
           computed_styles) override;
   protocol::Response captureSnapshot(
       std::unique_ptr<protocol::Array<String>> computed_styles,
+      protocol::Maybe<bool> include_dom_rects,
       std::unique_ptr<protocol::Array<protocol::DOMSnapshot::DocumentSnapshot>>*
           documents,
       std::unique_ptr<protocol::Array<String>>* strings) override;
@@ -156,6 +157,7 @@
   // Maps a style string vector to an index in |computed_styles_|. Used to avoid
   // duplicate entries in |computed_styles_|.
   std::unique_ptr<ComputedStylesMap> computed_styles_map_;
+  bool include_snapshot_dom_rects_ = false;
   std::unique_ptr<CSSPropertyFilter> css_property_filter_;
   // Maps a PaintLayer to its paint order index.
   std::unique_ptr<PaintOrderMap> paint_order_map_;
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.cc b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
index 082edae..2c073e5a 100644
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
@@ -400,8 +400,8 @@
   }
 }
 
-std::unique_ptr<protocol::Array<double>> RectForLayoutRect(
-    const LayoutRect& rect) {
+std::unique_ptr<protocol::Array<double>> RectForPhysicalRect(
+    const PhysicalRect& rect) {
   std::unique_ptr<protocol::Array<double>> result =
       protocol::Array<double>::create();
 
@@ -413,25 +413,25 @@
 }
 
 // Returns |layout_object|'s bounding box in document coordinates.
-LayoutRect RectInRootFrame(const LayoutObject* layout_object) {
+PhysicalRect RectInRootFrame(const LayoutObject* layout_object) {
   LocalFrameView* local_frame_view = layout_object->GetFrameView();
-  LayoutRect rect_in_absolute(layout_object->AbsoluteBoundingBoxFloatRect());
+  PhysicalRect rect_in_absolute = PhysicalRect::EnclosingRect(
+      layout_object->AbsoluteBoundingBoxFloatRect());
   return local_frame_view
              ? local_frame_view->ConvertToRootFrame(rect_in_absolute)
              : rect_in_absolute;
 }
 
-LayoutRect TextFragmentRectInRootFrame(
+PhysicalRect TextFragmentRectInRootFrame(
     const LayoutObject* layout_object,
     const LayoutText::TextBoxInfo& text_box) {
   PhysicalRect absolute_coords_text_box_rect =
       layout_object->LocalToAbsoluteRect(
           layout_object->FlipForWritingMode(text_box.local_rect));
   LocalFrameView* local_frame_view = layout_object->GetFrameView();
-  return (local_frame_view ? local_frame_view->ConvertToRootFrame(
-                                 absolute_coords_text_box_rect)
-                           : absolute_coords_text_box_rect)
-      .ToLayoutRect();
+  return local_frame_view ? local_frame_view->ConvertToRootFrame(
+                                absolute_coords_text_box_rect)
+                          : absolute_coords_text_box_rect;
 }
 
 }  // namespace
@@ -511,10 +511,11 @@
   }
 
   VisitAndCollectDistanceInfo(&(node->GetDocument()));
-  LayoutRect document_rect(node->GetDocument().GetLayoutView()->DocumentRect());
+  PhysicalRect document_rect(
+      node->GetDocument().GetLayoutView()->DocumentRect());
   LocalFrameView* local_frame_view = node->GetDocument().View();
   boxes_->addItem(
-      RectForLayoutRect(local_frame_view->ConvertToRootFrame(document_rect)));
+      RectForPhysicalRect(local_frame_view->ConvertToRootFrame(document_rect)));
 }
 
 void InspectorHighlight::VisitAndCollectDistanceInfo(Node* node) {
@@ -563,13 +564,13 @@
   if (layout_object->IsText()) {
     LayoutText* layout_text = ToLayoutText(layout_object);
     for (const auto& text_box : layout_text->GetTextBoxInfo()) {
-      LayoutRect text_rect(
+      PhysicalRect text_rect(
           TextFragmentRectInRootFrame(layout_object, text_box));
-      boxes_->addItem(RectForLayoutRect(text_rect));
+      boxes_->addItem(RectForPhysicalRect(text_rect));
     }
   } else {
-    LayoutRect rect(RectInRootFrame(layout_object));
-    boxes_->addItem(RectForLayoutRect(rect));
+    PhysicalRect rect(RectInRootFrame(layout_object));
+    boxes_->addItem(RectForPhysicalRect(rect));
   }
 }
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
index 98229c0..a505bb3c 100644
--- a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
@@ -820,6 +820,31 @@
   }
 }
 
+scoped_refptr<DOMWrapperWorld> InspectorPageAgent::EnsureDOMWrapperWorld(
+    LocalFrame* frame,
+    const String& world_name,
+    bool grant_universal_access) {
+  if (!isolated_worlds_.Contains(frame))
+    isolated_worlds_.Set(frame, FrameIsolatedWorlds());
+  FrameIsolatedWorlds& frame_worlds = isolated_worlds_.find(frame)->value;
+
+  auto world_it = frame_worlds.find(world_name);
+  if (world_it != frame_worlds.end())
+    return world_it->value;
+  scoped_refptr<DOMWrapperWorld> world =
+      frame->GetScriptController().CreateNewInspectorIsolatedWorld(world_name);
+  if (!world)
+    return nullptr;
+  frame_worlds.Set(world_name, world);
+  scoped_refptr<SecurityOrigin> security_origin =
+      frame->GetSecurityContext()->GetSecurityOrigin()->IsolatedCopy();
+  if (grant_universal_access)
+    security_origin->GrantUniversalAccess();
+  DOMWrapperWorld::SetIsolatedWorldSecurityOrigin(world->GetWorldId(),
+                                                  security_origin);
+  return world;
+}
+
 void InspectorPageAgent::DidClearDocumentOfWindowObject(LocalFrame* frame) {
   if (!GetFrontend())
     return;
@@ -829,7 +854,6 @@
               return Decimal::FromString(a) < Decimal::FromString(b);
             });
 
-  HashMap<String, int> world_id_by_name;
   for (const WTF::String& key : keys) {
     const String source = scripts_to_evaluate_on_load_.Get(key);
     const String world_name = worlds_to_evaluate_on_load_.Get(key);
@@ -840,31 +864,16 @@
       continue;
     }
 
-    auto it = world_id_by_name.find(world_name);
-    int world_id = 0;
-    if (it != world_id_by_name.end()) {
-      world_id = it->value;
-    } else {
-      scoped_refptr<DOMWrapperWorld> world =
-          frame->GetScriptController().CreateNewInspectorIsolatedWorld(
-              world_name);
-      if (!world)
-        continue;
-      world_id = world->GetWorldId();
-      world_id_by_name.Set(world_name, world_id);
-
-      scoped_refptr<SecurityOrigin> security_origin =
-          frame->GetSecurityContext()->GetSecurityOrigin()->IsolatedCopy();
-      security_origin->GrantUniversalAccess();
-      DOMWrapperWorld::SetIsolatedWorldSecurityOrigin(world_id,
-                                                      security_origin);
-    }
+    scoped_refptr<DOMWrapperWorld> world = EnsureDOMWrapperWorld(
+        frame, world_name, true /* grant_universal_access */);
+    if (!world)
+      continue;
 
     // Note: An error event in an isolated world will never be dispatched to
     // a foreign world.
     v8::HandleScope handle_scope(V8PerIsolateData::MainThreadIsolate());
     frame->GetScriptController().ExecuteScriptInIsolatedWorld(
-        world_id, source, KURL(), SanitizeScriptErrors::kSanitize);
+        world->GetWorldId(), source, KURL(), SanitizeScriptErrors::kSanitize);
   }
 
   if (!script_to_evaluate_on_load_once_.IsEmpty()) {
@@ -1206,19 +1215,11 @@
   if (!frame)
     return Response::Error("No frame for given id found");
 
-  scoped_refptr<DOMWrapperWorld> world =
-      frame->GetScriptController().CreateNewInspectorIsolatedWorld(
-          world_name.fromMaybe(""));
+  scoped_refptr<DOMWrapperWorld> world = EnsureDOMWrapperWorld(
+      frame, world_name.fromMaybe(""), grant_universal_access.fromMaybe(false));
   if (!world)
     return Response::Error("Could not create isolated world");
 
-  scoped_refptr<SecurityOrigin> security_origin =
-      frame->GetSecurityContext()->GetSecurityOrigin()->IsolatedCopy();
-  if (grant_universal_access.fromMaybe(false))
-    security_origin->GrantUniversalAccess();
-  DOMWrapperWorld::SetIsolatedWorldSecurityOrigin(world->GetWorldId(),
-                                                  security_origin);
-
   LocalWindowProxy* isolated_world_window_proxy =
       frame->GetScriptController().WindowProxy(*world);
   v8::HandleScope handle_scope(V8PerIsolateData::MainThreadIsolate());
@@ -1370,6 +1371,7 @@
 void InspectorPageAgent::Trace(blink::Visitor* visitor) {
   visitor->Trace(inspected_frames_);
   visitor->Trace(inspector_resource_content_loader_);
+  visitor->Trace(isolated_worlds_);
   InspectorBaseAgent::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_page_agent.h b/third_party/blink/renderer/core/inspector/inspector_page_agent.h
index cbb6824f..7b17903 100644
--- a/third_party/blink/renderer/core/inspector/inspector_page_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_page_agent.h
@@ -228,6 +228,10 @@
       bool case_sensitive,
       bool is_regex,
       std::unique_ptr<SearchInResourceCallback>);
+  scoped_refptr<DOMWrapperWorld> EnsureDOMWrapperWorld(
+      LocalFrame* frame,
+      const String& world_name,
+      bool grant_universal_access);
 
   static KURL UrlWithoutFragment(const KURL&);
 
@@ -240,6 +244,8 @@
       LocalFrame*);
   Member<InspectedFrames> inspected_frames_;
   HashMap<String, protocol::Binary> compilation_cache_;
+  using FrameIsolatedWorlds = HashMap<String, scoped_refptr<DOMWrapperWorld>>;
+  HeapHashMap<WeakMember<LocalFrame>, FrameIsolatedWorlds> isolated_worlds_;
   v8_inspector::V8InspectorSession* v8_session_;
   Client* client_;
   String pending_script_to_evaluate_on_load_once_;
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
index af92219b..c5dbaf3e 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
@@ -69,7 +69,7 @@
   // TODO(layout-dev): This should hit-test the intersection rect, not the
   // target rect; it's not helpful to know that the portion of the target that
   // is clipped is also occluded.
-  HitTestResult result(target->HitTestForOcclusion(rect.ToLayoutRect()));
+  HitTestResult result(target->HitTestForOcclusion(rect));
   Node* hit_node = result.InnerNode();
   if (!hit_node || hit_node == target->GetNode())
     return true;
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
index cdee296..315bf0d 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
@@ -304,7 +304,7 @@
   GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 300),
                                                           kProgrammaticScroll);
 
-  HitTestLocation location(LayoutPoint(0, 0));
+  HitTestLocation location{PhysicalOffset()};
   HitTestResult result(
       HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive |
                      HitTestRequest::kAllowChildFrameContent),
diff --git a/third_party/blink/renderer/core/layout/api/line_layout_box.h b/third_party/blink/renderer/core/layout/api/line_layout_box.h
index 1983f2c2..797958a 100644
--- a/third_party/blink/renderer/core/layout/api/line_layout_box.h
+++ b/third_party/blink/renderer/core/layout/api/line_layout_box.h
@@ -29,6 +29,9 @@
   LineLayoutBox() = default;
 
   LayoutPoint Location() const { return ToBox()->Location(); }
+  PhysicalOffset PhysicalLocation() const {
+    return ToBox()->PhysicalLocation();
+  }
 
   LayoutSize Size() const { return ToBox()->Size(); }
 
diff --git a/third_party/blink/renderer/core/layout/api/line_layout_inline.h b/third_party/blink/renderer/core/layout/api/line_layout_inline.h
index 3099fb2d..5f705679 100644
--- a/third_party/blink/renderer/core/layout/api/line_layout_inline.h
+++ b/third_party/blink/renderer/core/layout/api/line_layout_inline.h
@@ -67,7 +67,7 @@
 
   bool HitTestCulledInline(HitTestResult& result,
                            const HitTestLocation& location_in_container,
-                           const LayoutPoint& accumulated_offset) {
+                           const PhysicalOffset& accumulated_offset) {
     return ToInline()->HitTestCulledInline(result, location_in_container,
                                            accumulated_offset);
   }
diff --git a/third_party/blink/renderer/core/layout/api/line_layout_item.h b/third_party/blink/renderer/core/layout/api/line_layout_item.h
index dfe2be0..d4304f14 100644
--- a/third_party/blink/renderer/core/layout/api/line_layout_item.h
+++ b/third_party/blink/renderer/core/layout/api/line_layout_item.h
@@ -81,7 +81,7 @@
     return layout_object_->IsDescendantOf(item.layout_object_);
   }
 
-  void UpdateHitTestResult(HitTestResult& result, const LayoutPoint& point) {
+  void UpdateHitTestResult(HitTestResult& result, const PhysicalOffset& point) {
     return layout_object_->UpdateHitTestResult(result, point);
   }
 
@@ -235,7 +235,7 @@
 
   bool HitTestAllPhases(HitTestResult& result,
                         const HitTestLocation& location_in_container,
-                        const LayoutPoint& accumulated_offset) {
+                        const PhysicalOffset& accumulated_offset) {
     return layout_object_->HitTestAllPhases(result, location_in_container,
                                             accumulated_offset);
   }
diff --git a/third_party/blink/renderer/core/layout/geometry/logical_rect.cc b/third_party/blink/renderer/core/layout/geometry/logical_rect.cc
index 3ee275d..4b1604f 100644
--- a/third_party/blink/renderer/core/layout/geometry/logical_rect.cc
+++ b/third_party/blink/renderer/core/layout/geometry/logical_rect.cc
@@ -39,10 +39,10 @@
 
 String LogicalRect::ToString() const {
   return String::Format("%s,%s %sx%s",
-                        offset.inline_offset.ToString().Ascii().data(),
-                        offset.block_offset.ToString().Ascii().data(),
-                        size.inline_size.ToString().Ascii().data(),
-                        size.block_size.ToString().Ascii().data());
+                        offset.inline_offset.ToString().Ascii().c_str(),
+                        offset.block_offset.ToString().Ascii().c_str(),
+                        size.inline_size.ToString().Ascii().c_str(),
+                        size.block_size.ToString().Ascii().c_str());
 }
 
 std::ostream& operator<<(std::ostream& os, const LogicalRect& value) {
diff --git a/third_party/blink/renderer/core/layout/geometry/physical_offset.cc b/third_party/blink/renderer/core/layout/geometry/physical_offset.cc
index 8dcf2716..d2c6d16 100644
--- a/third_party/blink/renderer/core/layout/geometry/physical_offset.cc
+++ b/third_party/blink/renderer/core/layout/geometry/physical_offset.cc
@@ -42,8 +42,8 @@
 }
 
 String PhysicalOffset::ToString() const {
-  return String::Format("%s,%s", left.ToString().Ascii().data(),
-                        top.ToString().Ascii().data());
+  return String::Format("%s,%s", left.ToString().Ascii().c_str(),
+                        top.ToString().Ascii().c_str());
 }
 
 std::ostream& operator<<(std::ostream& os, const PhysicalOffset& value) {
diff --git a/third_party/blink/renderer/core/layout/geometry/physical_offset.h b/third_party/blink/renderer/core/layout/geometry/physical_offset.h
index ef88130..e4fc8c2 100644
--- a/third_party/blink/renderer/core/layout/geometry/physical_offset.h
+++ b/third_party/blink/renderer/core/layout/geometry/physical_offset.h
@@ -48,6 +48,11 @@
     return left.HasFraction() || top.HasFraction();
   }
 
+  void ClampNegativeToZero() {
+    left = std::max(left, LayoutUnit());
+    top = std::max(top, LayoutUnit());
+  }
+
   PhysicalOffset operator+(const PhysicalOffset& other) const {
     return PhysicalOffset{this->left + other.left, this->top + other.top};
   }
@@ -92,6 +97,10 @@
   explicit PhysicalOffset(const IntSize& size)
       : left(size.Width()), top(size.Height()) {}
 
+  static PhysicalOffset FromFloatPointFloor(const FloatPoint& point) {
+    return {LayoutUnit::FromFloatFloor(point.X()),
+            LayoutUnit::FromFloatFloor(point.Y())};
+  }
   static PhysicalOffset FromFloatPointRound(const FloatPoint& point) {
     return {LayoutUnit::FromFloatRound(point.X()),
             LayoutUnit::FromFloatRound(point.Y())};
diff --git a/third_party/blink/renderer/core/layout/geometry/physical_rect.cc b/third_party/blink/renderer/core/layout/geometry/physical_rect.cc
index 1dee4cc..3309188 100644
--- a/third_party/blink/renderer/core/layout/geometry/physical_rect.cc
+++ b/third_party/blink/renderer/core/layout/geometry/physical_rect.cc
@@ -133,8 +133,8 @@
 }
 
 String PhysicalRect::ToString() const {
-  return String::Format("%s %s", offset.ToString().Ascii().data(),
-                        size.ToString().Ascii().data());
+  return String::Format("%s %s", offset.ToString().Ascii().c_str(),
+                        size.ToString().Ascii().c_str());
 }
 
 PhysicalRect UnionRect(const Vector<PhysicalRect>& rects) {
diff --git a/third_party/blink/renderer/core/layout/geometry/physical_size.cc b/third_party/blink/renderer/core/layout/geometry/physical_size.cc
index 5c1b6ec..c0b88a4 100644
--- a/third_party/blink/renderer/core/layout/geometry/physical_size.cc
+++ b/third_party/blink/renderer/core/layout/geometry/physical_size.cc
@@ -26,8 +26,8 @@
 }
 
 String PhysicalSize::ToString() const {
-  return String::Format("%sx%s", width.ToString().Ascii().data(),
-                        height.ToString().Ascii().data());
+  return String::Format("%sx%s", width.ToString().Ascii().c_str(),
+                        height.ToString().Ascii().c_str());
 }
 
 std::ostream& operator<<(std::ostream& os, const PhysicalSize& value) {
diff --git a/third_party/blink/renderer/core/layout/hit_test_location.cc b/third_party/blink/renderer/core/layout/hit_test_location.cc
index 4ca0f47..7d83acd 100644
--- a/third_party/blink/renderer/core/layout/hit_test_location.cc
+++ b/third_party/blink/renderer/core/layout/hit_test_location.cc
@@ -29,9 +29,9 @@
     : is_rect_based_(false), is_rectilinear_(true) {}
 
 HitTestLocation::HitTestLocation(const IntPoint& point)
-    : HitTestLocation(LayoutPoint(point)) {}
+    : HitTestLocation(PhysicalOffset(point)) {}
 
-HitTestLocation::HitTestLocation(const LayoutPoint& point)
+HitTestLocation::HitTestLocation(const PhysicalOffset& point)
     : point_(point),
       bounding_box_(RectForPoint(point)),
       transformed_point_(point),
@@ -40,7 +40,7 @@
       is_rectilinear_(true) {}
 
 HitTestLocation::HitTestLocation(const FloatPoint& point)
-    : point_(FlooredLayoutPoint(point)),
+    : point_(PhysicalOffset::FromFloatPointFloor(point)),
       bounding_box_(RectForPoint(point_)),
       transformed_point_(point),
       transformed_rect_(FloatRect(bounding_box_)),
@@ -52,12 +52,12 @@
 
 HitTestLocation::HitTestLocation(const FloatPoint& point, const FloatQuad& quad)
     : transformed_point_(point), transformed_rect_(quad), is_rect_based_(true) {
-  point_ = FlooredLayoutPoint(point);
-  bounding_box_ = EnclosingLayoutRect(quad.BoundingBox());
+  point_ = PhysicalOffset::FromFloatPointFloor(point);
+  bounding_box_ = PhysicalRect::EnclosingRect(quad.BoundingBox());
   is_rectilinear_ = quad.IsRectilinear();
 }
 
-HitTestLocation::HitTestLocation(const LayoutRect& rect)
+HitTestLocation::HitTestLocation(const PhysicalRect& rect)
     : point_(rect.Center()),
       bounding_box_(rect),
       transformed_point_(point_),
@@ -67,7 +67,7 @@
 }
 
 HitTestLocation::HitTestLocation(const HitTestLocation& other,
-                                 const LayoutSize& offset)
+                                 const PhysicalOffset& offset)
     : point_(other.point_),
       bounding_box_(other.bounding_box_),
       transformed_point_(other.transformed_point_),
@@ -84,11 +84,11 @@
 HitTestLocation& HitTestLocation::operator=(const HitTestLocation& other) =
     default;
 
-void HitTestLocation::Move(const LayoutSize& offset) {
-  point_.Move(offset);
+void HitTestLocation::Move(const PhysicalOffset& offset) {
+  point_ += offset;
   bounding_box_.Move(offset);
   transformed_point_.Move(FloatSize(offset));
-  transformed_rect_.Move(offset);
+  transformed_rect_.Move(FloatSize(offset));
 }
 
 template <typename RectType>
@@ -116,7 +116,7 @@
   return transformed_rect_.IntersectsRect(FloatRect(rect));
 }
 
-bool HitTestLocation::Intersects(const LayoutRect& rect) const {
+bool HitTestLocation::Intersects(const PhysicalRect& rect) const {
   return IntersectsRect(rect, bounding_box_);
 }
 
diff --git a/third_party/blink/renderer/core/layout/hit_test_location.h b/third_party/blink/renderer/core/layout/hit_test_location.h
index e4c95df..3b71809 100644
--- a/third_party/blink/renderer/core/layout/hit_test_location.h
+++ b/third_party/blink/renderer/core/layout/hit_test_location.h
@@ -24,9 +24,9 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
 #include "third_party/blink/renderer/platform/geometry/float_quad.h"
 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
-#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 
@@ -46,35 +46,35 @@
   // scroll offset. See:
   // http://www.chromium.org/developers/design-documents/blink-coordinate-spaces
   HitTestLocation();
-  explicit HitTestLocation(const LayoutPoint&);
+  explicit HitTestLocation(const PhysicalOffset&);
   explicit HitTestLocation(const IntPoint&);
   explicit HitTestLocation(const FloatPoint&);
   explicit HitTestLocation(const DoublePoint&);
   explicit HitTestLocation(const FloatPoint&, const FloatQuad&);
-  explicit HitTestLocation(const LayoutRect&);
-  HitTestLocation(const HitTestLocation&, const LayoutSize& offset);
+  explicit HitTestLocation(const PhysicalRect&);
+  HitTestLocation(const HitTestLocation&, const PhysicalOffset& offset);
   HitTestLocation(const HitTestLocation&);
   ~HitTestLocation();
   HitTestLocation& operator=(const HitTestLocation&);
 
-  const LayoutPoint& Point() const { return point_; }
+  const PhysicalOffset& Point() const { return point_; }
   IntPoint RoundedPoint() const { return RoundedIntPoint(point_); }
 
   // Rect-based hit test related methods.
   bool IsRectBasedTest() const { return is_rect_based_; }
   bool IsRectilinear() const { return is_rectilinear_; }
-  const LayoutRect& BoundingBox() const { return bounding_box_; }
+  const PhysicalRect& BoundingBox() const { return bounding_box_; }
   IntRect EnclosingIntRect() const {
     return ::blink::EnclosingIntRect(bounding_box_);
   }
 
   // Returns the 1px x 1px hit test rect for a point.
   // TODO(pdr): Should we be using a one-layout-unit rect instead?
-  static LayoutRect RectForPoint(const LayoutPoint& point) {
-    return LayoutRect(FlooredIntPoint(point), IntSize(1, 1));
+  static PhysicalRect RectForPoint(const PhysicalOffset& point) {
+    return PhysicalRect(IntRect(FlooredIntPoint(point), IntSize(1, 1)));
   }
 
-  bool Intersects(const LayoutRect&) const;
+  bool Intersects(const PhysicalRect&) const;
   // Uses floating-point intersection, which uses inclusive intersection
   // (see LayoutRect::InclusiveIntersect for a definition)
   bool Intersects(const FloatRect&) const;
@@ -88,12 +88,12 @@
  private:
   template <typename RectType>
   bool IntersectsRect(const RectType&, const RectType& bounding_box) const;
-  void Move(const LayoutSize& offset);
+  void Move(const PhysicalOffset& offset);
 
   // These are cached forms of the more accurate |transformed_point_| and
   // |transformed_rect_|, below.
-  LayoutPoint point_;
-  LayoutRect bounding_box_;
+  PhysicalOffset point_;
+  PhysicalRect bounding_box_;
 
   FloatPoint transformed_point_;
   FloatQuad transformed_rect_;
diff --git a/third_party/blink/renderer/core/layout/hit_test_result.cc b/third_party/blink/renderer/core/layout/hit_test_result.cc
index de95358..c1c5eaa 100644
--- a/third_party/blink/renderer/core/layout/hit_test_result.cc
+++ b/third_party/blink/renderer/core/layout/hit_test_result.cc
@@ -144,7 +144,7 @@
     return PositionWithAffinity(MostForwardCaretPosition(
         Position(inner_node_, PositionAnchorType::kBeforeChildren)));
   }
-  return layout_object->PositionForPoint(LocalPoint());
+  return layout_object->PositionForPoint(FlippedLocalPoint());
 }
 
 LayoutObject* HitTestResult::GetLayoutObject() const {
@@ -392,7 +392,7 @@
 ListBasedHitTestBehavior HitTestResult::AddNodeToListBasedTestResult(
     Node* node,
     const HitTestLocation& location,
-    const LayoutRect& rect) {
+    const PhysicalRect& rect) {
   // If not a list-based test, stop testing because the hit has been found.
   if (!GetHitTestRequest().ListBased())
     return kStopHitTesting;
@@ -470,7 +470,7 @@
 
 HitTestLocation HitTestResult::ResolveRectBasedTest(
     Node* resolved_inner_node,
-    const LayoutPoint& resolved_point_in_main_frame) {
+    const PhysicalOffset& resolved_point_in_main_frame) {
   point_in_inner_node_frame_ = resolved_point_in_main_frame;
   SetInnerNode(nullptr);
   list_based_test_result_ = nullptr;
@@ -481,7 +481,7 @@
   // never use it so shouldn't bother with the cost of computing it.
   DCHECK(resolved_inner_node);
   if (auto* layout_object = resolved_inner_node->GetLayoutObject())
-    layout_object->UpdateHitTestResult(*this, LayoutPoint());
+    layout_object->UpdateHitTestResult(*this, PhysicalOffset());
 
   return HitTestLocation(resolved_point_in_main_frame);
 }
@@ -502,4 +502,10 @@
   return image_map_image_element;
 }
 
+LayoutPoint HitTestResult::FlippedLocalPoint() const {
+  if (!inner_node_ || !inner_node_->GetLayoutObject())
+    return local_point_.ToLayoutPoint();
+  return inner_node_->GetLayoutObject()->FlipForWritingMode(local_point_);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/hit_test_result.h b/third_party/blink/renderer/core/layout/hit_test_result.h
index cbe4644..bac556785 100644
--- a/third_party/blink/renderer/core/layout/hit_test_result.h
+++ b/third_party/blink/renderer/core/layout/hit_test_result.h
@@ -24,6 +24,7 @@
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/editing/forward.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
 #include "third_party/blink/renderer/core/layout/hit_test_location.h"
 #include "third_party/blink/renderer/core/layout/hit_test_request.h"
 #include "third_party/blink/renderer/platform/geometry/float_quad.h"
@@ -47,6 +48,7 @@
 class LayoutObject;
 class Region;
 class Scrollbar;
+struct PhysicalOffset;
 
 // List-based hit test testing can continue even after a hit has been found.
 // This is used to support fuzzy matching with rect-based hit tests as well as
@@ -97,10 +99,10 @@
 
   // The hit-tested point in the coordinates of the innerNode frame, the frame
   // containing innerNode.
-  const LayoutPoint& PointInInnerNodeFrame() const {
+  const PhysicalOffset& PointInInnerNodeFrame() const {
     return point_in_inner_node_frame_;
   }
-  void SetPointInInnerNodeFrame(const LayoutPoint& point) {
+  void SetPointInInnerNodeFrame(const PhysicalOffset& point) {
     point_in_inner_node_frame_ = point;
   }
   IntPoint RoundedPointInInnerNodeFrame() const {
@@ -109,8 +111,11 @@
   LocalFrame* InnerNodeFrame() const;
 
   // The hit-tested point in the coordinates of the inner node.
-  const LayoutPoint& LocalPoint() const { return local_point_; }
-  void SetNodeAndPosition(Node* node, const LayoutPoint& p) {
+  const PhysicalOffset& LocalPoint() const { return local_point_; }
+  // TODO(wangxianzhu): This is used in PositionForPoint() where we still
+  // expect flipped blocks coordinates.
+  LayoutPoint FlippedLocalPoint() const;
+  void SetNodeAndPosition(Node* node, const PhysicalOffset& p) {
     local_point_ = p;
     SetInnerNode(node);
   }
@@ -153,12 +158,12 @@
 
   // TODO(pdr): When using the default rect argument, this function does not
   // check if the tapped area is entirely contained by the HitTestLocation's
-  // bounding box. Callers should pass a LayoutRect as the third parameter so
+  // bounding box. Callers should pass a PhysicalRect as the third parameter so
   // hit testing can early-out when a tapped area is covered.
   ListBasedHitTestBehavior AddNodeToListBasedTestResult(
       Node*,
       const HitTestLocation&,
-      const LayoutRect& = LayoutRect());
+      const PhysicalRect& = PhysicalRect());
   ListBasedHitTestBehavior AddNodeToListBasedTestResult(Node*,
                                                         const HitTestLocation&,
                                                         const Region&);
@@ -176,7 +181,7 @@
   // location.
   HitTestLocation ResolveRectBasedTest(
       Node* resolved_inner_node,
-      const LayoutPoint& resolved_point_in_main_frame);
+      const PhysicalOffset& resolved_point_in_main_frame);
 
  private:
   NodeSet& MutableListBasedTestResult();  // See above.
@@ -191,11 +196,11 @@
   Member<Node> inner_possibly_pseudo_node_;
   // FIXME: Nothing changes this to a value different from m_hitTestLocation!
   // The hit-tested point in innerNode frame coordinates.
-  LayoutPoint point_in_inner_node_frame_;
+  PhysicalOffset point_in_inner_node_frame_;
   // A point in the local coordinate space of m_innerNode's layoutObject.Allows
   // us to efficiently determine where inside the layoutObject we hit on
   // subsequent operations.
-  LayoutPoint local_point_;
+  PhysicalOffset local_point_;
   // For non-URL, this is the enclosing that triggers navigation.
   Member<Element> inner_url_element_;
   Member<Scrollbar> scrollbar_;
diff --git a/third_party/blink/renderer/core/layout/hit_testing_transform_state.cc b/third_party/blink/renderer/core/layout/hit_testing_transform_state.cc
index b2aeafe..44078b3 100644
--- a/third_party/blink/renderer/core/layout/hit_testing_transform_state.cc
+++ b/third_party/blink/renderer/core/layout/hit_testing_transform_state.cc
@@ -76,9 +76,10 @@
   return accumulated_transform_.Inverse().ProjectQuad(last_planar_area_);
 }
 
-LayoutRect HitTestingTransformState::BoundsOfMappedArea() const {
-  return accumulated_transform_.Inverse().ClampedBoundsOfProjectedQuad(
-      last_planar_area_);
+PhysicalRect HitTestingTransformState::BoundsOfMappedArea() const {
+  return PhysicalRectToBeNoop(
+      accumulated_transform_.Inverse().ClampedBoundsOfProjectedQuad(
+          last_planar_area_));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/hit_testing_transform_state.h b/third_party/blink/renderer/core/layout/hit_testing_transform_state.h
index 647dd3e..0bb6efa 100644
--- a/third_party/blink/renderer/core/layout/hit_testing_transform_state.h
+++ b/third_party/blink/renderer/core/layout/hit_testing_transform_state.h
@@ -26,9 +26,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_HIT_TESTING_TRANSFORM_STATE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_HIT_TESTING_TRANSFORM_STATE_H_
 
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
 #include "third_party/blink/renderer/platform/geometry/float_point.h"
 #include "third_party/blink/renderer/platform/geometry/float_quad.h"
-#include "third_party/blink/renderer/platform/geometry/int_size.h"
 #include "third_party/blink/renderer/platform/transforms/affine_transform.h"
 #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
@@ -68,7 +68,7 @@
   FloatPoint MappedPoint() const;
   FloatQuad MappedQuad() const;
   FloatQuad MappedArea() const;
-  LayoutRect BoundsOfMappedArea() const;
+  PhysicalRect BoundsOfMappedArea() const;
   void Flatten();
 
   FloatPoint last_planar_point_;
diff --git a/third_party/blink/renderer/core/layout/layout_block.cc b/third_party/blink/renderer/core/layout/layout_block.cc
index 7f210761..476b84d 100644
--- a/third_party/blink/renderer/core/layout/layout_block.cc
+++ b/third_party/blink/renderer/core/layout/layout_block.cc
@@ -1203,25 +1203,24 @@
 
 bool LayoutBlock::IsPointInOverflowControl(
     HitTestResult& result,
-    const LayoutPoint& location_in_container,
-    const LayoutPoint& accumulated_offset) const {
+    const PhysicalOffset& location_in_container,
+    const PhysicalOffset& accumulated_offset) const {
   if (!ScrollsOverflow())
     return false;
 
   return Layer()->GetScrollableArea()->HitTestOverflowControls(
-      result, RoundedIntPoint(location_in_container -
-                              ToLayoutSize(accumulated_offset)));
+      result, RoundedIntPoint(location_in_container - accumulated_offset));
 }
 
 bool LayoutBlock::HitTestOverflowControl(
     HitTestResult& result,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& adjusted_location) {
+    const PhysicalOffset& adjusted_location) {
   if (VisibleToHitTestRequest(result.GetHitTestRequest()) &&
       IsPointInOverflowControl(result, location_in_container.Point(),
                                adjusted_location)) {
-    UpdateHitTestResult(result, location_in_container.Point() -
-                                    ToLayoutSize(adjusted_location));
+    UpdateHitTestResult(result,
+                        location_in_container.Point() - adjusted_location);
     // FIXME: isPointInOverflowControl() doesn't handle rect-based tests yet.
     if (result.AddNodeToListBasedTestResult(
             NodeForHitTest(), location_in_container) == kStopHitTesting)
@@ -1232,7 +1231,7 @@
 
 bool LayoutBlock::HitTestChildren(HitTestResult& result,
                                   const HitTestLocation& location_in_container,
-                                  const LayoutPoint& accumulated_offset,
+                                  const PhysicalOffset& accumulated_offset,
                                   HitTestAction hit_test_action) {
   // We may use legacy code to hit-test the anonymous fieldset content wrapper
   // child. The layout object for the rendered legend will be a child of that
@@ -1241,9 +1240,9 @@
   bool may_contain_rendered_legend = IsAnonymousNGFieldsetContentWrapper();
 
   DCHECK(!ChildrenInline());
-  LayoutPoint scrolled_offset(HasOverflowClip()
-                                  ? accumulated_offset - ScrolledContentOffset()
-                                  : accumulated_offset);
+  PhysicalOffset scrolled_offset = accumulated_offset;
+  if (HasOverflowClip())
+    scrolled_offset -= PhysicalOffset(ScrolledContentOffset());
   HitTestAction child_hit_test = hit_test_action;
   if (hit_test_action == kHitTestChildBlockBackgrounds)
     child_hit_test = kHitTestChildBlockBackground;
@@ -1253,25 +1252,21 @@
         (may_contain_rendered_legend && child->IsRenderedLegend()))
       continue;
 
-    LayoutPoint child_point =
-        FlipForWritingModeForChild(child, scrolled_offset);
-
     bool did_hit;
     if (child->IsFloating()) {
       if (hit_test_action != kHitTestFloat || !IsLayoutNGObject())
         continue;
       // Hit-test the floats in regular tree order if this is LayoutNG. Only
       // legacy layout uses the FloatingObjects list.
-      did_hit =
-          child->HitTestAllPhases(result, location_in_container, child_point);
+      did_hit = child->HitTestAllPhases(result, location_in_container,
+                                        scrolled_offset);
     } else {
-      did_hit = child->NodeAtPoint(result, location_in_container, child_point,
-                                   child_hit_test);
+      did_hit = child->NodeAtPoint(result, location_in_container,
+                                   scrolled_offset, child_hit_test);
     }
     if (did_hit) {
-      UpdateHitTestResult(
-          result, DeprecatedFlipForWritingMode(ToLayoutPoint(
-                      location_in_container.Point() - accumulated_offset)));
+      UpdateHitTestResult(result,
+                          location_in_container.Point() - accumulated_offset);
       return true;
     }
   }
@@ -1450,6 +1445,11 @@
   offset = DeprecatedFlipForWritingMode(offset);
 }
 
+void LayoutBlock::OffsetForContents(PhysicalOffset& offset) const {
+  if (HasOverflowClip())
+    offset += PhysicalOffset(ScrolledContentOffset());
+}
+
 void LayoutBlock::ScrollbarsChanged(bool horizontal_scrollbar_changed,
                                     bool vertical_scrollbar_changed,
                                     ScrollbarChangeContext context) {
diff --git a/third_party/blink/renderer/core/layout/layout_block.h b/third_party/blink/renderer/core/layout/layout_block.h
index d1081896..42b4f4d 100644
--- a/third_party/blink/renderer/core/layout/layout_block.h
+++ b/third_party/blink/renderer/core/layout/layout_block.h
@@ -425,10 +425,10 @@
 
   bool HitTestOverflowControl(HitTestResult&,
                               const HitTestLocation&,
-                              const LayoutPoint& adjusted_location) override;
+                              const PhysicalOffset& adjusted_location) override;
   bool HitTestChildren(HitTestResult&,
                        const HitTestLocation& location_in_container,
-                       const LayoutPoint& accumulated_offset,
+                       const PhysicalOffset& accumulated_offset,
                        HitTestAction) override;
 
   void StyleWillChange(StyleDifference,
@@ -500,8 +500,8 @@
   bool TryLayoutDoingPositionedMovementOnly();
 
   bool IsPointInOverflowControl(HitTestResult&,
-                                const LayoutPoint& location_in_container,
-                                const LayoutPoint& accumulated_offset) const;
+                                const PhysicalOffset& location_in_container,
+                                const PhysicalOffset& accumulated_offset) const;
 
   void ComputeBlockPreferredLogicalWidths(LayoutUnit& min_logical_width,
                                           LayoutUnit& max_logical_width) const;
@@ -542,6 +542,7 @@
 
   // Adjust from painting offsets to the local coords of this layoutObject
   void OffsetForContents(LayoutPoint&) const;
+  void OffsetForContents(PhysicalOffset&) const;
 
   PositionWithAffinity PositionForPointRespectingEditingBoundaries(
       LineLayoutBox child,
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.cc b/third_party/blink/renderer/core/layout/layout_block_flow.cc
index 5b424ed4..de675302 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.cc
@@ -4176,11 +4176,11 @@
 bool LayoutBlockFlow::HitTestChildren(
     HitTestResult& result,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& accumulated_offset,
+    const PhysicalOffset& accumulated_offset,
     HitTestAction hit_test_action) {
-  LayoutPoint scrolled_offset(HasOverflowClip()
-                                  ? accumulated_offset - ScrolledContentOffset()
-                                  : accumulated_offset);
+  PhysicalOffset scrolled_offset = accumulated_offset;
+  if (HasOverflowClip())
+    scrolled_offset -= PhysicalOffset(ScrolledContentOffset());
 
   if (hit_test_action == kHitTestFloat && !IsLayoutNGObject()) {
     // Hit-test the floats using the FloatingObjects list if we're in legacy
@@ -4194,9 +4194,8 @@
     if (line_boxes_.HitTest(LineLayoutBoxModel(this), result,
                             location_in_container, scrolled_offset,
                             hit_test_action)) {
-      UpdateHitTestResult(
-          result, DeprecatedFlipForWritingMode(ToLayoutPoint(
-                      location_in_container.Point() - accumulated_offset)));
+      UpdateHitTestResult(result,
+                          location_in_container.Point() - accumulated_offset);
       return true;
     }
   } else if (LayoutBlock::HitTestChildren(result, location_in_container,
@@ -4211,7 +4210,7 @@
 bool LayoutBlockFlow::HitTestFloats(
     HitTestResult& result,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& accumulated_offset) {
+    const PhysicalOffset& accumulated_offset) {
   if (!floating_objects_)
     return false;
 
@@ -4223,16 +4222,15 @@
     if (floating_object.ShouldPaint() &&
         // TODO(wangxianzhu): Should this be a DCHECK?
         !floating_object.GetLayoutObject()->HasSelfPaintingLayer()) {
-      LayoutUnit x_offset = XPositionForFloatIncludingMargin(floating_object) -
-                            floating_object.GetLayoutObject()->Location().X();
-      LayoutUnit y_offset = YPositionForFloatIncludingMargin(floating_object) -
-                            floating_object.GetLayoutObject()->Location().Y();
-      LayoutPoint child_point = FlipFloatForWritingModeForChild(
-          floating_object, accumulated_offset + LayoutSize(x_offset, y_offset));
+      PhysicalOffset child_point = accumulated_offset;
+      child_point +=
+          PhysicalOffset(XPositionForFloatIncludingMargin(floating_object),
+                         YPositionForFloatIncludingMargin(floating_object));
+      child_point -= floating_object.GetLayoutObject()->PhysicalLocation();
       if (floating_object.GetLayoutObject()->HitTestAllPhases(
               result, location_in_container, child_point)) {
-        UpdateHitTestResult(
-            result, location_in_container.Point() - ToLayoutSize(child_point));
+        UpdateHitTestResult(result,
+                            location_in_container.Point() - child_point);
         return true;
       }
     }
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.h b/third_party/blink/renderer/core/layout/layout_block_flow.h
index 2253ccb9..c00fd3f1 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.h
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.h
@@ -528,7 +528,7 @@
   Node* NodeForHitTest() const final;
   bool HitTestChildren(HitTestResult&,
                        const HitTestLocation& location_in_container,
-                       const LayoutPoint& accumulated_offset,
+                       const PhysicalOffset& accumulated_offset,
                        HitTestAction) override;
 
   PhysicalOffset AccumulateInFlowPositionOffsets() const override;
@@ -577,7 +577,7 @@
 
   bool HitTestFloats(HitTestResult&,
                      const HitTestLocation& location_in_container,
-                     const LayoutPoint& accumulated_offset);
+                     const PhysicalOffset& accumulated_offset);
 
   void ClearFloats(EClear);
 
diff --git a/third_party/blink/renderer/core/layout/layout_block_test.cc b/third_party/blink/renderer/core/layout/layout_block_test.cc
index 88eabb366..62900aa 100644
--- a/third_party/blink/renderer/core/layout/layout_block_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_test.cc
@@ -25,7 +25,7 @@
   obj->SetModifiedStyleOutsideStyleRecalc(nullptr,
                                           LayoutObject::ApplyStyleChanges::kNo);
   EXPECT_FALSE(obj->Style());
-  EXPECT_THAT(obj->DecoratedName().Ascii().data(),
+  EXPECT_THAT(obj->DecoratedName().Ascii(),
               MatchesRegex("LayoutN?G?BlockFlow \\(anonymous\\)"));
   obj->Destroy();
 }
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 5b1349b..c5c3f81 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -664,8 +664,8 @@
   GetScrollableArea()->ScrollToAbsolutePosition(position, scroll_behavior);
 }
 
-LayoutRect LayoutBox::ScrollRectToVisibleRecursive(
-    const LayoutRect& absolute_rect,
+PhysicalRect LayoutBox::ScrollRectToVisibleRecursive(
+    const PhysicalRect& absolute_rect,
     const WebScrollIntoViewParams& params) {
   DCHECK(params.GetScrollType() == kProgrammaticScroll ||
          params.GetScrollType() == kUserScroll);
@@ -684,7 +684,7 @@
   // Presumably the same issue as in setScrollTop. See crbug.com/343132.
   DisableCompositingQueryAsserts disabler;
 
-  LayoutRect absolute_rect_to_scroll = absolute_rect;
+  PhysicalRect absolute_rect_to_scroll = absolute_rect;
   if (absolute_rect_to_scroll.Width() <= 0)
     absolute_rect_to_scroll.SetWidth(LayoutUnit(1));
   if (absolute_rect_to_scroll.Height() <= 0)
@@ -695,7 +695,7 @@
   if (ContainingBlock())
     parent_box = ContainingBlock();
 
-  LayoutRect absolute_rect_for_parent;
+  PhysicalRect absolute_rect_for_parent;
   if (!IsLayoutView() && HasOverflowClip()) {
     absolute_rect_for_parent =
         GetScrollableArea()->ScrollIntoView(absolute_rect_to_scroll, params);
@@ -714,12 +714,8 @@
         AllowedToPropagateRecursiveScrollToParentFrame(params)) {
       parent_box = owner_element->GetLayoutObject()->EnclosingBox();
       LayoutView* parent_view = owner_element->GetLayoutObject()->View();
-      absolute_rect_for_parent =
-          View()
-              ->LocalToAncestorRect(
-                  PhysicalRectToBeNoop(absolute_rect_for_parent), parent_view,
-                  kTraverseDocumentBoundaries)
-              .ToLayoutRect();
+      absolute_rect_for_parent = View()->LocalToAncestorRect(
+          absolute_rect_for_parent, parent_view, kTraverseDocumentBoundaries);
     }
   } else {
     absolute_rect_for_parent = absolute_rect_to_scroll;
@@ -1024,7 +1020,7 @@
   return node && HasEditableStyle(*node);
 }
 
-void LayoutBox::Autoscroll(const LayoutPoint& position_in_root_frame) {
+void LayoutBox::Autoscroll(const PhysicalOffset& position_in_root_frame) {
   LocalFrame* frame = GetFrame();
   if (!frame)
     return;
@@ -1033,10 +1029,11 @@
   if (!frame_view)
     return;
 
-  LayoutPoint absolute_position =
-      frame_view->ConvertFromRootFrame(LayoutPoint(position_in_root_frame));
+  PhysicalOffset absolute_position =
+      frame_view->ConvertFromRootFrame(position_in_root_frame);
   ScrollRectToVisibleRecursive(
-      LayoutRect(absolute_position, LayoutSize(1, 1)),
+      PhysicalRect(absolute_position,
+                   PhysicalSize(LayoutUnit(1), LayoutUnit(1))),
       WebScrollIntoViewParams(ScrollAlignment::kAlignToEdgeIfNeeded,
                               ScrollAlignment::kAlignToEdgeIfNeeded,
                               kUserScroll));
@@ -1588,7 +1585,7 @@
 // Hit Testing
 bool LayoutBox::HitTestAllPhases(HitTestResult& result,
                                  const HitTestLocation& location_in_container,
-                                 const LayoutPoint& accumulated_offset,
+                                 const PhysicalOffset& accumulated_offset,
                                  HitTestFilter hit_test_filter) {
   // Check if we need to do anything at all.
   // If we have clipping, then we can't have any spillout.
@@ -1604,8 +1601,8 @@
                          : PhysicalVisualOverflowRect();
     }
 
-    LayoutRect adjusted_overflow_box = overflow_box.ToLayoutRect();
-    adjusted_overflow_box.MoveBy(accumulated_offset + Location());
+    PhysicalRect adjusted_overflow_box = overflow_box;
+    adjusted_overflow_box.Move(accumulated_offset + PhysicalLocation());
     if (!location_in_container.Intersects(adjusted_overflow_box))
       return false;
   }
@@ -1615,9 +1612,9 @@
 
 bool LayoutBox::NodeAtPoint(HitTestResult& result,
                             const HitTestLocation& location_in_container,
-                            const LayoutPoint& accumulated_offset,
+                            const PhysicalOffset& accumulated_offset,
                             HitTestAction action) {
-  LayoutPoint adjusted_location = accumulated_offset + Location();
+  PhysicalOffset adjusted_location = accumulated_offset + PhysicalLocation();
 
   bool should_hit_test_self = IsInSelfHitTestingPhase(action);
 
@@ -1636,9 +1633,9 @@
       skip_children = true;
     }
     if (!skip_children && StyleRef().HasBorderRadius()) {
-      LayoutRect bounds_rect(adjusted_location, Size());
+      PhysicalRect bounds_rect(adjusted_location, Size());
       skip_children = !location_in_container.Intersects(
-          StyleRef().GetRoundedInnerBorderFor(bounds_rect));
+          StyleRef().GetRoundedInnerBorderFor(bounds_rect.ToLayoutRect()));
     }
   }
 
@@ -1654,18 +1651,17 @@
   // Now hit test ourselves.
   if (should_hit_test_self &&
       VisibleToHitTestRequest(result.GetHitTestRequest())) {
-    LayoutRect bounds_rect;
+    PhysicalRect bounds_rect;
     if (result.GetHitTestRequest().GetType() &
         HitTestRequest::kHitTestVisualOverflow) {
-      bounds_rect = PhysicalVisualOverflowRectIncludingFilters().ToLayoutRect();
+      bounds_rect = PhysicalVisualOverflowRectIncludingFilters();
     } else {
-      bounds_rect = BorderBoxRect();
+      bounds_rect = PhysicalBorderBoxRect();
     }
-    bounds_rect.Move(ToSize(adjusted_location));
+    bounds_rect.Move(adjusted_location);
     if (location_in_container.Intersects(bounds_rect)) {
-      UpdateHitTestResult(result, DeprecatedFlipForWritingMode(
-                                      location_in_container.Point() -
-                                      ToLayoutSize(adjusted_location)));
+      UpdateHitTestResult(result,
+                          location_in_container.Point() - adjusted_location);
       if (result.AddNodeToListBasedTestResult(NodeForHitTest(),
                                               location_in_container,
                                               bounds_rect) == kStopHitTesting)
@@ -1678,7 +1674,7 @@
 
 bool LayoutBox::HitTestChildren(HitTestResult& result,
                                 const HitTestLocation& location_in_container,
-                                const LayoutPoint& accumulated_offset,
+                                const PhysicalOffset& accumulated_offset,
                                 HitTestAction action) {
   for (LayoutObject* child = SlowLastChild(); child;
        child = child->PreviousSibling()) {
@@ -1694,11 +1690,11 @@
 
 bool LayoutBox::HitTestClippedOutByBorder(
     const HitTestLocation& location_in_container,
-    const LayoutPoint& border_box_location) const {
-  LayoutRect border_rect = BorderBoxRect();
-  border_rect.MoveBy(border_box_location);
+    const PhysicalOffset& border_box_location) const {
+  PhysicalRect border_rect = PhysicalBorderBoxRect();
+  border_rect.Move(border_box_location);
   return !location_in_container.Intersects(
-      StyleRef().GetRoundedBorderFor(border_rect));
+      StyleRef().GetRoundedBorderFor(border_rect.ToLayoutRect()));
 }
 
 void LayoutBox::Paint(const PaintInfo& paint_info) const {
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index e72f5b6..391c62c 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -699,8 +699,8 @@
   void ScrollByRecursively(const ScrollOffset& delta);
   // If makeVisibleInVisualViewport is set, the visual viewport will be scrolled
   // if required to make the rect visible.
-  LayoutRect ScrollRectToVisibleRecursive(const LayoutRect&,
-                                          const WebScrollIntoViewParams&);
+  PhysicalRect ScrollRectToVisibleRecursive(const PhysicalRect&,
+                                            const WebScrollIntoViewParams&);
 
   LayoutRectOutsets MarginBoxOutsets() const { return margin_box_outsets_; }
   LayoutUnit MarginTop() const override { return margin_box_outsets_.Top(); }
@@ -769,11 +769,11 @@
 
   bool HitTestAllPhases(HitTestResult&,
                         const HitTestLocation& location_in_container,
-                        const LayoutPoint& accumulated_offset,
+                        const PhysicalOffset& accumulated_offset,
                         HitTestFilter = kHitTestAll) final;
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) override;
 
   LayoutUnit MinPreferredLogicalWidth() const override;
@@ -1134,7 +1134,7 @@
 
   bool CanBeScrolledAndHasScrollableArea() const;
   virtual bool CanBeProgramaticallyScrolled() const;
-  virtual void Autoscroll(const LayoutPoint&);
+  virtual void Autoscroll(const PhysicalOffset&);
   bool CanAutoscroll() const;
   PhysicalOffset CalculateAutoscrollDirection(
       const FloatPoint& point_in_root_frame) const;
@@ -1320,10 +1320,6 @@
     DCHECK(!IsHorizontalWritingMode());
     return frame_rect_.Width() - (position + width);
   }
-  WARN_UNUSED_RESULT LayoutPoint
-  FlipForWritingMode(const PhysicalOffset& offset) const {
-    return LayoutPoint(FlipForWritingMode(offset.left), offset.top);
-  }
   // Inherit other flipping methods from LayoutObject.
   using LayoutObject::FlipForWritingMode;
 
@@ -1469,8 +1465,9 @@
   void AddCustomLayoutChildIfNeeded();
   void ClearCustomLayoutChild();
 
-  bool HitTestClippedOutByBorder(const HitTestLocation& location_in_container,
-                                 const LayoutPoint& border_box_location) const;
+  bool HitTestClippedOutByBorder(
+      const HitTestLocation& location_in_container,
+      const PhysicalOffset& border_box_location) const;
 
   // Returns true if the box intersects the viewport visible to the user.
   bool IntersectsVisibleViewport() const;
@@ -1618,12 +1615,12 @@
 
   virtual bool HitTestOverflowControl(HitTestResult&,
                                       const HitTestLocation&,
-                                      const LayoutPoint&) {
+                                      const PhysicalOffset&) {
     return false;
   }
   virtual bool HitTestChildren(HitTestResult&,
                                const HitTestLocation& location_in_container,
-                               const LayoutPoint& accumulated_offset,
+                               const PhysicalOffset& accumulated_offset,
                                HitTestAction);
 
   void InvalidatePaint(const PaintInvalidatorContext&) const override;
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_content.cc b/third_party/blink/renderer/core/layout/layout_embedded_content.cc
index abd1370..4f8c294b 100644
--- a/third_party/blink/renderer/core/layout/layout_embedded_content.cc
+++ b/third_party/blink/renderer/core/layout/layout_embedded_content.cc
@@ -141,7 +141,7 @@
 bool LayoutEmbeddedContent::NodeAtPointOverEmbeddedContentView(
     HitTestResult& result,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& accumulated_offset,
+    const PhysicalOffset& accumulated_offset,
     HitTestAction action) {
   bool had_result = result.InnerNode();
   bool inside = LayoutReplaced::NodeAtPoint(result, location_in_container,
@@ -151,8 +151,8 @@
   // just in the border/padding area).
   if ((inside || location_in_container.IsRectBasedTest()) && !had_result &&
       result.InnerNode() == GetNode()) {
-    result.SetIsOverEmbeddedContentView(PhysicalContentBoxRect().Contains(
-        PhysicalOffsetToBeNoop(result.LocalPoint())));
+    result.SetIsOverEmbeddedContentView(
+        PhysicalContentBoxRect().Contains(result.LocalPoint()));
   }
   return inside;
 }
@@ -160,7 +160,7 @@
 bool LayoutEmbeddedContent::NodeAtPoint(
     HitTestResult& result,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& accumulated_offset,
+    const PhysicalOffset& accumulated_offset,
     HitTestAction action) {
   auto* local_frame_view = DynamicTo<LocalFrameView>(ChildFrameView());
   bool skip_contents = (result.GetHitTestRequest().GetStopNode() == this ||
@@ -191,9 +191,10 @@
 
     if (VisibleToHitTestRequest(result.GetHitTestRequest()) &&
         child_layout_view) {
-      LayoutPoint adjusted_location = accumulated_offset + Location();
-      LayoutPoint content_offset =
-          LayoutPoint(BorderLeft() + PaddingLeft(), BorderTop() + PaddingTop());
+      PhysicalOffset adjusted_location =
+          accumulated_offset + PhysicalLocation();
+      PhysicalOffset content_offset(BorderLeft() + PaddingLeft(),
+                                    BorderTop() + PaddingTop());
       HitTestLocation new_hit_test_location(
           location_in_container, -adjusted_location - content_offset);
       HitTestRequest new_hit_test_request(
@@ -289,7 +290,7 @@
     plugin->InvalidatePaint();
 }
 
-CursorDirective LayoutEmbeddedContent::GetCursor(const LayoutPoint& point,
+CursorDirective LayoutEmbeddedContent::GetCursor(const PhysicalOffset& point,
                                                  Cursor& cursor) const {
   if (Plugin()) {
     // A plugin is responsible for setting the cursor when the pointer is over
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_content.h b/third_party/blink/renderer/core/layout/layout_embedded_content.h
index 35801d7..57bab2f 100644
--- a/third_party/blink/renderer/core/layout/layout_embedded_content.h
+++ b/third_party/blink/renderer/core/layout/layout_embedded_content.h
@@ -43,7 +43,7 @@
 
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) override;
 
   void AddRef() { ++ref_count_; }
@@ -74,7 +74,7 @@
   void PaintReplaced(const PaintInfo&,
                      const PhysicalOffset& paint_offset) const override;
   void InvalidatePaint(const PaintInvalidatorContext&) const final;
-  CursorDirective GetCursor(const LayoutPoint&, Cursor&) const final;
+  CursorDirective GetCursor(const PhysicalOffset&, Cursor&) const final;
 
   bool CanBeSelectionLeafInternal() const final { return true; }
 
@@ -87,7 +87,7 @@
   bool NodeAtPointOverEmbeddedContentView(
       HitTestResult&,
       const HitTestLocation& location_in_container,
-      const LayoutPoint& accumulated_offset,
+      const PhysicalOffset& accumulated_offset,
       HitTestAction);
 
   int ref_count_;
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
index b98d260a..6ebdab5 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
@@ -284,29 +284,25 @@
 bool LayoutFlexibleBox::HitTestChildren(
     HitTestResult& result,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& accumulated_offset,
+    const PhysicalOffset& accumulated_offset,
     HitTestAction hit_test_action) {
   if (hit_test_action != kHitTestForeground)
     return false;
 
-  LayoutPoint scrolled_offset(HasOverflowClip()
-                                  ? accumulated_offset - ScrolledContentOffset()
-                                  : accumulated_offset);
+  PhysicalOffset scrolled_offset = accumulated_offset;
+  if (HasOverflowClip())
+    scrolled_offset -= PhysicalOffset(ScrolledContentOffset());
 
   for (LayoutBox* child = LastChildBox(); child;
        child = child->PreviousSiblingBox()) {
     if (child->HasSelfPaintingLayer())
       continue;
 
-    LayoutPoint child_point =
-        FlipForWritingModeForChild(child, scrolled_offset);
-
     bool child_hit =
-        child->HitTestAllPhases(result, location_in_container, child_point);
+        child->HitTestAllPhases(result, location_in_container, scrolled_offset);
     if (child_hit) {
-      UpdateHitTestResult(
-          result, DeprecatedFlipForWritingMode(ToLayoutPoint(
-                      location_in_container.Point() - accumulated_offset)));
+      UpdateHitTestResult(result,
+                          location_in_container.Point() - accumulated_offset);
       return true;
     }
   }
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.h b/third_party/blink/renderer/core/layout/layout_flexible_box.h
index 71339bd6..5bd017e 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.h
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.h
@@ -105,7 +105,7 @@
 
   bool HitTestChildren(HitTestResult&,
                        const HitTestLocation& location_in_container,
-                       const LayoutPoint& accumulated_offset,
+                       const PhysicalOffset& accumulated_offset,
                        HitTestAction) override;
 
   void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
diff --git a/third_party/blink/renderer/core/layout/layout_flow_thread.cc b/third_party/blink/renderer/core/layout/layout_flow_thread.cc
index 4e9869c..2b32aac3 100644
--- a/third_party/blink/renderer/core/layout/layout_flow_thread.cc
+++ b/third_party/blink/renderer/core/layout/layout_flow_thread.cc
@@ -180,7 +180,7 @@
 
 bool LayoutFlowThread::NodeAtPoint(HitTestResult& result,
                                    const HitTestLocation& location_in_container,
-                                   const LayoutPoint& accumulated_offset,
+                                   const PhysicalOffset& accumulated_offset,
                                    HitTestAction hit_test_action) {
   if (hit_test_action == kHitTestBlockBackground)
     return false;
diff --git a/third_party/blink/renderer/core/layout/layout_flow_thread.h b/third_party/blink/renderer/core/layout/layout_flow_thread.h
index 2cc0d54..dd5ae74 100644
--- a/third_party/blink/renderer/core/layout/layout_flow_thread.h
+++ b/third_party/blink/renderer/core/layout/layout_flow_thread.h
@@ -126,7 +126,7 @@
 
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) final;
 
   virtual void AddColumnSetToThread(LayoutMultiColumnSet*) = 0;
diff --git a/third_party/blink/renderer/core/layout/layout_frame_set.cc b/third_party/blink/renderer/core/layout/layout_frame_set.cc
index 42e2961..af24df1 100644
--- a/third_party/blink/renderer/core/layout/layout_frame_set.cc
+++ b/third_party/blink/renderer/core/layout/layout_frame_set.cc
@@ -581,7 +581,7 @@
   return child->IsFrame() || child->IsFrameSet();
 }
 
-CursorDirective LayoutFrameSet::GetCursor(const LayoutPoint& point,
+CursorDirective LayoutFrameSet::GetCursor(const PhysicalOffset& point,
                                           Cursor& cursor) const {
   IntPoint rounded_point = RoundedIntPoint(point);
   if (CanResizeRow(rounded_point)) {
diff --git a/third_party/blink/renderer/core/layout/layout_frame_set.h b/third_party/blink/renderer/core/layout/layout_frame_set.h
index 6e4df71..95cb2c4 100644
--- a/third_party/blink/renderer/core/layout/layout_frame_set.h
+++ b/third_party/blink/renderer/core/layout/layout_frame_set.h
@@ -134,7 +134,7 @@
   void Paint(const PaintInfo&) const override;
   void ComputePreferredLogicalWidths() override;
   bool IsChildAllowed(LayoutObject*, const ComputedStyle&) const override;
-  CursorDirective GetCursor(const LayoutPoint&, Cursor&) const override;
+  CursorDirective GetCursor(const PhysicalOffset&, Cursor&) const override;
 
   void SetIsResizing(bool);
 
diff --git a/third_party/blink/renderer/core/layout/layout_geometry_map.cc b/third_party/blink/renderer/core/layout/layout_geometry_map.cc
index 396e040..8a933f61 100644
--- a/third_party/blink/renderer/core/layout/layout_geometry_map.cc
+++ b/third_party/blink/renderer/core/layout/layout_geometry_map.cc
@@ -139,7 +139,7 @@
           accumulated_offset_.left.ToInt(), accumulated_offset_.top.ToInt());
   for (int i = mapping_.size() - 1; i >= 0; --i) {
     fprintf(stderr, " [%d] %s: offset=%d,%d", i,
-            mapping_[i].layout_object_->DebugName().Ascii().data(),
+            mapping_[i].layout_object_->DebugName().Ascii().c_str(),
             mapping_[i].offset_.left.ToInt(), mapping_[i].offset_.top.ToInt());
     if (mapping_[i].flags_ & kContainsFixedPosition)
       fprintf(stderr, " containsFixedPosition");
diff --git a/third_party/blink/renderer/core/layout/layout_image.cc b/third_party/blink/renderer/core/layout/layout_image.cc
index b5df489..e7746d07 100644
--- a/third_party/blink/renderer/core/layout/layout_image.cc
+++ b/third_party/blink/renderer/core/layout/layout_image.cc
@@ -294,7 +294,7 @@
 
 bool LayoutImage::NodeAtPoint(HitTestResult& result,
                               const HitTestLocation& location_in_container,
-                              const LayoutPoint& accumulated_offset,
+                              const PhysicalOffset& accumulated_offset,
                               HitTestAction hit_test_action) {
   HitTestResult temp_result(result);
   bool inside = LayoutReplaced::NodeAtPoint(
diff --git a/third_party/blink/renderer/core/layout/layout_image.h b/third_party/blink/renderer/core/layout/layout_image.h
index 39bc5ed..3c33ab25 100644
--- a/third_party/blink/renderer/core/layout/layout_image.h
+++ b/third_party/blink/renderer/core/layout/layout_image.h
@@ -126,7 +126,7 @@
   void ImageNotifyFinished(ImageResourceContent*) final;
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) final;
 
   void InvalidatePaintAndMarkForLayoutIfNeeded(CanDeferInvalidation);
diff --git a/third_party/blink/renderer/core/layout/layout_image_test.cc b/third_party/blink/renderer/core/layout/layout_image_test.cc
index 3258c1e..ec88f74 100644
--- a/third_party/blink/renderer/core/layout/layout_image_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_image_test.cc
@@ -18,13 +18,13 @@
   )HTML");
 
   const auto& target = *GetDocument().getElementById("target");
-  HitTestLocation location(LayoutPoint(60, 10));
+  HitTestLocation location(PhysicalOffset(60, 10));
   HitTestResult result(
       HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive |
                      HitTestRequest::kAllowChildFrameContent),
       location);
   GetLayoutView().HitTest(location, result);
-  EXPECT_EQ(LayoutPoint(60, 10), result.PointInInnerNodeFrame());
+  EXPECT_EQ(PhysicalOffset(60, 10), result.PointInInnerNodeFrame());
   EXPECT_EQ(target, result.InnerNode());
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_inline.cc b/third_party/blink/renderer/core/layout/layout_inline.cc
index 6fe0851..3ed1eba 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.cc
+++ b/third_party/blink/renderer/core/layout/layout_inline.cc
@@ -1020,7 +1020,7 @@
 
 bool LayoutInline::NodeAtPoint(HitTestResult& result,
                                const HitTestLocation& location_in_container,
-                               const LayoutPoint& accumulated_offset,
+                               const PhysicalOffset& accumulated_offset,
                                HitTestAction hit_test_action) {
   if (ContainingNGBlockFlow()) {
     // TODO(crbug.com/965976): We should fix the root cause of the missed
@@ -1035,9 +1035,8 @@
          NGPaintFragment::InlineFragmentsFor(this)) {
       // NGBoxFragmentPainter::NodeAtPoint() takes an offset that is accumulated
       // up to the fragment itself. Compute this offset.
-      LayoutPoint adjusted_location =
-          accumulated_offset +
-          fragment->InlineOffsetToContainerBox().ToLayoutPoint();
+      PhysicalOffset adjusted_location =
+          accumulated_offset + fragment->InlineOffsetToContainerBox();
       if (NGBoxFragmentPainter(*fragment).NodeAtPoint(
               result, location_in_container, adjusted_location,
               hit_test_action))
@@ -1054,19 +1053,18 @@
 bool LayoutInline::HitTestCulledInline(
     HitTestResult& result,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& accumulated_offset,
+    const PhysicalOffset& accumulated_offset,
     const NGPaintFragment* container_fragment) {
   DCHECK(container_fragment || !AlwaysCreateLineBoxes());
   if (!VisibleToHitTestRequest(result.GetHitTestRequest()))
     return false;
 
-  HitTestLocation adjusted_location(location_in_container,
-                                    -ToLayoutSize(accumulated_offset));
+  HitTestLocation adjusted_location(location_in_container, -accumulated_offset);
   Region region_result;
   bool intersected = false;
   auto yield = [&adjusted_location, &region_result,
                 &intersected](const PhysicalRect& rect) {
-    if (adjusted_location.Intersects(rect.ToLayoutRect())) {
+    if (adjusted_location.Intersects(rect)) {
       intersected = true;
       region_result.Unite(EnclosingIntRect(rect));
     }
@@ -1478,12 +1476,12 @@
 }
 
 void LayoutInline::UpdateHitTestResult(HitTestResult& result,
-                                       const LayoutPoint& point) const {
+                                       const PhysicalOffset& point) const {
   if (result.InnerNode())
     return;
 
   Node* n = GetNode();
-  LayoutPoint local_point(point);
+  PhysicalOffset local_point = point;
   if (n) {
     if (IsInlineElementContinuation()) {
       // We're in the continuation of a split inline. Adjust our local point to
@@ -1493,7 +1491,8 @@
 
       // Get our containing block.
       LayoutBox* block = ContainingBlock();
-      local_point.MoveBy(block->Location() - first_block->LocationOffset());
+      local_point += block->PhysicalLocation();
+      local_point -= first_block->PhysicalLocation();
     }
 
     result.SetNodeAndPosition(n, local_point);
diff --git a/third_party/blink/renderer/core/layout/layout_inline.h b/third_party/blink/renderer/core/layout/layout_inline.h
index bae52a7..1e1a2bb7 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.h
+++ b/third_party/blink/renderer/core/layout/layout_inline.h
@@ -234,7 +234,7 @@
   // In legacy, |parent_fragment| is always null, and all rects are regenerated.
   bool HitTestCulledInline(HitTestResult&,
                            const HitTestLocation& location_in_container,
-                           const LayoutPoint& accumulated_offset,
+                           const PhysicalOffset& accumulated_offset,
                            const NGPaintFragment* parent_fragment = nullptr);
 
   PhysicalOffset FirstLineBoxTopLeft() const {
@@ -333,7 +333,7 @@
 
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) final;
 
   PaintLayerType LayerTypeRequired() const override;
@@ -383,7 +383,7 @@
 
   void ChildBecameNonInline(LayoutObject* child) final;
 
-  void UpdateHitTestResult(HitTestResult&, const LayoutPoint&) const final;
+  void UpdateHitTestResult(HitTestResult&, const PhysicalOffset&) const final;
 
   void ImageChanged(WrappedImagePtr, CanDeferInvalidation) final;
 
diff --git a/third_party/blink/renderer/core/layout/layout_inline_test.cc b/third_party/blink/renderer/core/layout/layout_inline_test.cc
index 5aa2986..7570ca2 100644
--- a/third_party/blink/renderer/core/layout/layout_inline_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_inline_test.cc
@@ -110,10 +110,10 @@
   HitTestRequest hit_request(HitTestRequest::kTouchEvent |
                              HitTestRequest::kListBased);
 
-  LayoutRect hit_rect(1, 3, 2, 4);
+  PhysicalRect hit_rect(1, 3, 2, 4);
   HitTestLocation location(hit_rect);
   HitTestResult hit_result(hit_request, location);
-  LayoutPoint hit_offset;
+  PhysicalOffset hit_offset;
 
   // The return value of HitTestCulledInline() indicates whether the hit test
   // rect is completely contained by the part of |lots_of_boxes| being hit-
@@ -158,8 +158,8 @@
 
   HitTestRequest hit_request(HitTestRequest::kReadOnly |
                              HitTestRequest::kActive);
-  const LayoutPoint container_offset(8, 8);
-  const LayoutPoint hit_location(18, 15);
+  const PhysicalOffset container_offset(8, 8);
+  const PhysicalOffset hit_location(18, 15);
   HitTestLocation location(hit_location);
 
   Element* div = GetDocument().QuerySelector("div");
@@ -211,11 +211,11 @@
   HitTestRequest hit_request(HitTestRequest::kReadOnly |
                              HitTestRequest::kActive |
                              HitTestRequest::kIgnorePointerEventsNone);
-  const LayoutPoint container_offset(8, 8);
+  const PhysicalOffset container_offset(8, 8);
 
   // Hit test first line
   {
-    LayoutPoint hit_location(13, 13);
+    PhysicalOffset hit_location(13, 13);
     HitTestLocation location(hit_location);
     Node* target = GetElementById("span")->firstChild();
 
@@ -235,7 +235,7 @@
 
   // Hit test second line
   {
-    LayoutPoint hit_location(13, 23);
+    PhysicalOffset hit_location(13, 23);
     HitTestLocation location(hit_location);
     Node* target = GetElementById("line2")->firstChild();
 
@@ -255,7 +255,7 @@
 
   // Hit test image in third line
   {
-    LayoutPoint hit_location(13, 33);
+    PhysicalOffset hit_location(13, 33);
     HitTestLocation location(hit_location);
     Node* target = GetDocument().QuerySelector("img");
 
diff --git a/third_party/blink/renderer/core/layout/layout_list_box.cc b/third_party/blink/renderer/core/layout/layout_list_box.cc
index 687f4bd..d949534 100644
--- a/third_party/blink/renderer/core/layout/layout_list_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_list_box.cc
@@ -131,7 +131,7 @@
     min_logical_width = LayoutUnit();
 }
 
-void LayoutListBox::ScrollToRect(const LayoutRect& absolute_rect) {
+void LayoutListBox::ScrollToRect(const PhysicalRect& absolute_rect) {
   if (HasOverflowClip()) {
     DCHECK(Layer());
     DCHECK(Layer()->GetScrollableArea());
diff --git a/third_party/blink/renderer/core/layout/layout_list_box.h b/third_party/blink/renderer/core/layout/layout_list_box.h
index 10ebb01..91e76c1 100644
--- a/third_party/blink/renderer/core/layout/layout_list_box.h
+++ b/third_party/blink/renderer/core/layout/layout_list_box.h
@@ -45,7 +45,7 @@
   unsigned size() const;
 
   // Unlike scrollRectToVisible this will not scroll parent boxes.
-  void ScrollToRect(const LayoutRect&);
+  void ScrollToRect(const PhysicalRect&);
 
   const char* GetName() const override { return "LayoutListBox"; }
 
diff --git a/third_party/blink/renderer/core/layout/layout_menu_list.cc b/third_party/blink/renderer/core/layout/layout_menu_list.cc
index 9a4396f..fa4f05a 100644
--- a/third_party/blink/renderer/core/layout/layout_menu_list.cc
+++ b/third_party/blink/renderer/core/layout/layout_menu_list.cc
@@ -208,8 +208,10 @@
 }
 
 void LayoutMenuList::UpdateOptionsWidth() const {
-  if (ShouldApplySizeContainment())
+  if (ShouldApplySizeContainment()) {
+    options_width_ = 0;
     return;
+  }
 
   float max_option_width = 0;
 
diff --git a/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.cc b/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.cc
index fafa632..fbce94a 100644
--- a/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.cc
+++ b/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.cc
@@ -168,7 +168,7 @@
 bool LayoutMultiColumnSpannerPlaceholder::NodeAtPoint(
     HitTestResult& result,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& accumulated_offset,
+    const PhysicalOffset& accumulated_offset,
     HitTestAction action) {
   return !layout_object_in_flow_thread_->HasSelfPaintingLayer() &&
          layout_object_in_flow_thread_->NodeAtPoint(
diff --git a/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h b/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h
index 3cff6539..e1e0bf2 100644
--- a/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h
+++ b/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h
@@ -69,7 +69,7 @@
   void Paint(const PaintInfo&) const override;
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) override;
 
  private:
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 9168f52..a70ad771 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -196,8 +196,8 @@
   unsigned bitfields2_;
   unsigned bitfields3_;
   // The following fields are in FragmentData.
-  LayoutRect visual_rect_;
-  LayoutPoint paint_offset_;
+  IntRect visual_rect_;
+  PhysicalOffset paint_offset_;
   std::unique_ptr<int> rare_data_;
   std::unique_ptr<FragmentData> next_fragment_;
 };
@@ -766,8 +766,8 @@
          view->IsHorizontalWritingMode();
 }
 
-LayoutRect LayoutObject::ScrollRectToVisible(
-    const LayoutRect& rect,
+PhysicalRect LayoutObject::ScrollRectToVisible(
+    const PhysicalRect& rect,
     const WebScrollIntoViewParams& params) {
   LayoutBox* enclosing_box = EnclosingBox();
   if (!enclosing_box)
@@ -779,7 +779,7 @@
   WebScrollIntoViewParams new_params(params);
   new_params.is_for_scroll_sequence |=
       params.GetScrollType() == kProgrammaticScroll;
-  LayoutRect new_location =
+  PhysicalRect new_location =
       enclosing_box->ScrollRectToVisibleRecursive(rect, new_params);
   GetDocument().GetFrame()->GetSmoothScrollSequencer().RunQueuedAnimations();
 
@@ -1706,7 +1706,7 @@
 }
 
 HitTestResult LayoutObject::HitTestForOcclusion(
-    const LayoutRect& hit_rect) const {
+    const PhysicalRect& hit_rect) const {
   LocalFrame* frame = GetDocument().GetFrame();
   DCHECK(!frame->View()->NeedsLayout());
   HitTestRequest::HitTestRequestType hit_type =
@@ -1776,7 +1776,7 @@
 
   if (IsText() && ToLayoutText(this)->IsTextFragment()) {
     string_builder.AppendFormat(" \"%s\" ",
-                                ToLayoutText(this)->GetText().Ascii().data());
+                                ToLayoutText(this)->GetText().Ascii().c_str());
   }
 
   if (VirtualContinuation())
@@ -3285,7 +3285,7 @@
 bool LayoutObject::HitTestAllPhases(
     HitTestResult& result,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& accumulated_offset,
+    const PhysicalOffset& accumulated_offset,
     HitTestFilter hit_test_filter) {
   bool inside = false;
   if (hit_test_filter != kHitTestSelf) {
@@ -3328,7 +3328,7 @@
 }
 
 void LayoutObject::UpdateHitTestResult(HitTestResult& result,
-                                       const LayoutPoint& point) const {
+                                       const PhysicalOffset& point) const {
   if (result.InnerNode())
     return;
 
@@ -3338,7 +3338,7 @@
 
 bool LayoutObject::NodeAtPoint(HitTestResult&,
                                const HitTestLocation& /*locationInContainer*/,
-                               const LayoutPoint& /*accumulatedOffset*/,
+                               const PhysicalOffset& /*accumulatedOffset*/,
                                HitTestAction) {
   return false;
 }
@@ -3693,7 +3693,7 @@
   return CreatePositionWithAffinity(0);
 }
 
-CursorDirective LayoutObject::GetCursor(const LayoutPoint&, Cursor&) const {
+CursorDirective LayoutObject::GetCursor(const PhysicalOffset&, Cursor&) const {
   return kSetCursorBasedOnStyle;
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index a33d30d..0304386 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -354,8 +354,8 @@
   // space of the local root frame.
   // TODO(nburris): The returned rect is actually in document coordinates, not
   // root frame coordinates.
-  LayoutRect ScrollRectToVisible(const LayoutRect&,
-                                 const WebScrollIntoViewParams&);
+  PhysicalRect ScrollRectToVisible(const PhysicalRect&,
+                                   const WebScrollIntoViewParams&);
 
   // Convenience function for getting to the nearest enclosing box of a
   // LayoutObject.
@@ -1441,19 +1441,25 @@
   CompositingState GetCompositingState() const;
   virtual CompositingReasons AdditionalCompositingReasons() const;
 
+  // |accumulated_offset| is accumulated physical offset from the same origin as
+  // |location_in_container|, not including the offset of the current object.
+  // The caller just ensures |location_in_container| and |accumulated_offset|
+  // are in the same coordinate space. The implementation should not assume any
+  // specific coordinate space of them.
   virtual bool HitTestAllPhases(HitTestResult&,
                                 const HitTestLocation& location_in_container,
-                                const LayoutPoint& accumulated_offset,
+                                const PhysicalOffset& accumulated_offset,
                                 HitTestFilter = kHitTestAll);
   // Returns the node that is ultimately added to the hit test result. Some
   // objects report a hit testing node that is not their own (such as
   // continuations and some psuedo elements) and it is important that the
   // node be consistent between point- and list-based hit test results.
   virtual Node* NodeForHitTest() const;
-  virtual void UpdateHitTestResult(HitTestResult&, const LayoutPoint&) const;
+  virtual void UpdateHitTestResult(HitTestResult&, const PhysicalOffset&) const;
+  // See HitTestAllPhases for explanation of |accumulated_offset|.
   virtual bool NodeAtPoint(HitTestResult&,
                            const HitTestLocation& location_in_container,
-                           const LayoutPoint& accumulated_offset,
+                           const PhysicalOffset& accumulated_offset,
                            HitTestAction);
 
   virtual PositionWithAffinity PositionForPoint(const LayoutPoint&) const;
@@ -1733,7 +1739,7 @@
     return StyleRef().VisitedDependentColor(color_property);
   }
 
-  virtual CursorDirective GetCursor(const LayoutPoint&, Cursor&) const;
+  virtual CursorDirective GetCursor(const PhysicalOffset&, Cursor&) const;
 
   // Return the LayoutBoxModelObject in the container chain which is responsible
   // for painting this object. The function crosses frames boundaries so the
@@ -1803,9 +1809,9 @@
       VisualRectFlags = kDefaultVisualRectFlags) const;
 
   // Do a rect-based hit test with this object as the stop node.
-  HitTestResult HitTestForOcclusion(const LayoutRect&) const;
+  HitTestResult HitTestForOcclusion(const PhysicalRect&) const;
   HitTestResult HitTestForOcclusion() const {
-    return HitTestForOcclusion(VisualRectInDocument().ToLayoutRect());
+    return HitTestForOcclusion(VisualRectInDocument());
   }
 
   // Return the offset to the column in which the specified point (in
diff --git a/third_party/blink/renderer/core/layout/layout_object_test.cc b/third_party/blink/renderer/core/layout/layout_object_test.cc
index e8b2a8d..9a29a32b 100644
--- a/third_party/blink/renderer/core/layout/layout_object_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_object_test.cc
@@ -58,7 +58,7 @@
   DCHECK(div);
   LayoutObject* obj = div->GetLayoutObject();
   DCHECK(obj);
-  EXPECT_THAT(obj->DecoratedName().Ascii().data(),
+  EXPECT_THAT(obj->DecoratedName().Ascii(),
               MatchesRegex("LayoutN?G?BlockFlow \\(positioned\\)"));
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_table.cc b/third_party/blink/renderer/core/layout/layout_table.cc
index 8e732cb6..2b451f3 100644
--- a/third_party/blink/renderer/core/layout/layout_table.cc
+++ b/third_party/blink/renderer/core/layout/layout_table.cc
@@ -1615,28 +1615,23 @@
 
 bool LayoutTable::NodeAtPoint(HitTestResult& result,
                               const HitTestLocation& location_in_container,
-                              const LayoutPoint& accumulated_offset,
+                              const PhysicalOffset& accumulated_offset,
                               HitTestAction action) {
-  LayoutPoint adjusted_location = accumulated_offset + Location();
+  PhysicalOffset adjusted_location = accumulated_offset + PhysicalLocation();
 
   // Check kids first.
   bool skip_children = (result.GetHitTestRequest().GetStopNode() == this);
   if (!skip_children &&
       (!HasOverflowClip() ||
-       location_in_container.Intersects(
-           OverflowClipRect(PhysicalOffsetToBeNoop(adjusted_location))
-               .ToLayoutRect()))) {
+       location_in_container.Intersects(OverflowClipRect(adjusted_location)))) {
     for (LayoutObject* child = LastChild(); child;
          child = child->PreviousSibling()) {
       if (child->IsBox() && !ToLayoutBox(child)->HasSelfPaintingLayer() &&
           (child->IsTableSection() || child->IsTableCaption())) {
-        LayoutPoint child_point =
-            FlipForWritingModeForChild(ToLayoutBox(child), adjusted_location);
-        if (child->NodeAtPoint(result, location_in_container, child_point,
+        if (child->NodeAtPoint(result, location_in_container, adjusted_location,
                                action)) {
           UpdateHitTestResult(
-              result,
-              ToLayoutPoint(location_in_container.Point() - child_point));
+              result, location_in_container.Point() - adjusted_location);
           return true;
         }
       }
@@ -1644,14 +1639,13 @@
   }
 
   // Check our bounds next.
-  LayoutRect bounds_rect(adjusted_location, Size());
+  PhysicalRect bounds_rect(adjusted_location, Size());
   if (VisibleToHitTestRequest(result.GetHitTestRequest()) &&
       (action == kHitTestBlockBackground ||
        action == kHitTestChildBlockBackground) &&
       location_in_container.Intersects(bounds_rect)) {
-    UpdateHitTestResult(
-        result, DeprecatedFlipForWritingMode(location_in_container.Point() -
-                                             ToLayoutSize(adjusted_location)));
+    UpdateHitTestResult(result,
+                        location_in_container.Point() - adjusted_location);
     if (result.AddNodeToListBasedTestResult(GetNode(), location_in_container,
                                             bounds_rect) == kStopHitTesting)
       return true;
diff --git a/third_party/blink/renderer/core/layout/layout_table.h b/third_party/blink/renderer/core/layout/layout_table.h
index 4b1e853..845e4ac 100644
--- a/third_party/blink/renderer/core/layout/layout_table.h
+++ b/third_party/blink/renderer/core/layout/layout_table.h
@@ -449,7 +449,7 @@
   void ComputePreferredLogicalWidths() override;
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) override;
 
   LayoutUnit BaselinePosition(
diff --git a/third_party/blink/renderer/core/layout/layout_table_row.cc b/third_party/blink/renderer/core/layout/layout_table_row.cc
index a330a4b..7e0641c2 100644
--- a/third_party/blink/renderer/core/layout/layout_table_row.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_row.cc
@@ -228,7 +228,7 @@
 // Hit Testing
 bool LayoutTableRow::NodeAtPoint(HitTestResult& result,
                                  const HitTestLocation& location_in_container,
-                                 const LayoutPoint& accumulated_offset,
+                                 const PhysicalOffset& accumulated_offset,
                                  HitTestAction action) {
   // Table rows cannot ever be hit tested.  Effectively they do not exist.
   // Just forward to our children always.
@@ -238,12 +238,10 @@
     // ever implement a table-specific hit-test method (which we should do for
     // performance reasons anyway), then we can remove this check.
     if (!cell->HasSelfPaintingLayer()) {
-      LayoutPoint cell_point =
-          FlipForWritingModeForChild(cell, accumulated_offset);
-      if (cell->NodeAtPoint(result, location_in_container, cell_point,
+      if (cell->NodeAtPoint(result, location_in_container, accumulated_offset,
                             action)) {
-        UpdateHitTestResult(
-            result, location_in_container.Point() - ToLayoutSize(cell_point));
+        UpdateHitTestResult(result,
+                            location_in_container.Point() - accumulated_offset);
         return true;
       }
     }
diff --git a/third_party/blink/renderer/core/layout/layout_table_row.h b/third_party/blink/renderer/core/layout/layout_table_row.h
index 86d8d15..b69b2f01 100644
--- a/third_party/blink/renderer/core/layout/layout_table_row.h
+++ b/third_party/blink/renderer/core/layout/layout_table_row.h
@@ -101,7 +101,7 @@
 
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) override;
 
   PaginationBreakability GetPaginationBreakability() const final;
diff --git a/third_party/blink/renderer/core/layout/layout_table_section.cc b/third_party/blink/renderer/core/layout/layout_table_section.cc
index 8b6fe37..f58f5756 100644
--- a/third_party/blink/renderer/core/layout/layout_table_section.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_section.cc
@@ -1507,10 +1507,8 @@
 }
 
 LayoutRect LayoutTableSection::LogicalRectForWritingModeAndDirection(
-    const LayoutRect& rect) const {
-  LayoutRect table_aligned_rect(rect);
-
-  DeprecatedFlipForWritingMode(table_aligned_rect);
+    const PhysicalRect& rect) const {
+  LayoutRect table_aligned_rect = FlipForWritingMode(rect);
 
   if (!TableStyle().IsHorizontalWritingMode())
     table_aligned_rect = table_aligned_rect.TransposedRect();
@@ -1791,7 +1789,7 @@
 bool LayoutTableSection::NodeAtPoint(
     HitTestResult& result,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& accumulated_offset,
+    const PhysicalOffset& accumulated_offset,
     HitTestAction action) {
   // If we have no children then we have nothing to do.
   if (!FirstRow())
@@ -1799,7 +1797,7 @@
 
   // Table sections cannot ever be hit tested.  Effectively they do not exist.
   // Just forward to our children always.
-  LayoutPoint adjusted_location = accumulated_offset + Location();
+  PhysicalOffset adjusted_location = accumulated_offset + PhysicalLocation();
 
   if (HasOverflowClip() &&
       !location_in_container.Intersects(OverflowClipRect(adjusted_location)))
@@ -1812,13 +1810,10 @@
       // ever implement a table-specific hit-test method (which we should do for
       // performance reasons anyway), then we can remove this check.
       if (!row->HasSelfPaintingLayer()) {
-        LayoutPoint child_point =
-            FlipForWritingModeForChild(row, adjusted_location);
-        if (row->NodeAtPoint(result, location_in_container, child_point,
+        if (row->NodeAtPoint(result, location_in_container, adjusted_location,
                              action)) {
           UpdateHitTestResult(
-              result,
-              ToLayoutPoint(location_in_container.Point() - child_point));
+              result, location_in_container.Point() - adjusted_location);
           return true;
         }
       }
@@ -1828,8 +1823,8 @@
 
   RecalcCellsIfNeeded();
 
-  LayoutRect hit_test_rect = LayoutRect(location_in_container.BoundingBox());
-  hit_test_rect.MoveBy(-adjusted_location);
+  PhysicalRect hit_test_rect = location_in_container.BoundingBox();
+  hit_test_rect.Move(-adjusted_location);
 
   LayoutRect table_aligned_rect =
       LogicalRectForWritingModeAndDirection(hit_test_rect);
@@ -1851,12 +1846,10 @@
       for (unsigned i = grid_cell.Cells().size(); i;) {
         --i;
         LayoutTableCell* cell = grid_cell.Cells()[i];
-        LayoutPoint cell_point =
-            FlipForWritingModeForChild(cell, adjusted_location);
         if (static_cast<LayoutObject*>(cell)->NodeAtPoint(
-                result, location_in_container, cell_point, action)) {
+                result, location_in_container, adjusted_location, action)) {
           UpdateHitTestResult(
-              result, location_in_container.Point() - ToLayoutSize(cell_point));
+              result, location_in_container.Point() - adjusted_location);
           return true;
         }
       }
diff --git a/third_party/blink/renderer/core/layout/layout_table_section.h b/third_party/blink/renderer/core/layout/layout_table_section.h
index 4085fb5..cf6bec0 100644
--- a/third_party/blink/renderer/core/layout/layout_table_section.h
+++ b/third_party/blink/renderer/core/layout/layout_table_section.h
@@ -229,7 +229,7 @@
 
   // Flip the rect so it aligns with the coordinates used by the rowPos and
   // columnPos vectors.
-  LayoutRect LogicalRectForWritingModeAndDirection(const LayoutRect&) const;
+  LayoutRect LogicalRectForWritingModeAndDirection(const PhysicalRect&) const;
 
   // Sets |rows| and |columns| to cover all cells needing repaint in
   // |damage_rect|.
@@ -297,7 +297,7 @@
   void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) override;
 
  private:
diff --git a/third_party/blink/renderer/core/layout/layout_text.h b/third_party/blink/renderer/core/layout/layout_text.h
index 26ac63e..105eef1 100644
--- a/third_party/blink/renderer/core/layout/layout_text.h
+++ b/third_party/blink/renderer/core/layout/layout_text.h
@@ -365,7 +365,7 @@
   void UpdateLayout() final { NOTREACHED(); }
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation&,
-                   const LayoutPoint&,
+                   const PhysicalOffset&,
                    HitTestAction) final {
     NOTREACHED();
     return false;
diff --git a/third_party/blink/renderer/core/layout/layout_text_control.cc b/third_party/blink/renderer/core/layout/layout_text_control.cc
index 9b095640..887c63c 100644
--- a/third_party/blink/renderer/core/layout/layout_text_control.cc
+++ b/third_party/blink/renderer/core/layout/layout_text_control.cc
@@ -126,19 +126,17 @@
 
 void LayoutTextControl::HitInnerEditorElement(
     HitTestResult& result,
-    const LayoutPoint& point_in_container,
-    const LayoutPoint& accumulated_offset) {
+    const PhysicalOffset& point_in_container,
+    const PhysicalOffset& accumulated_offset) {
   HTMLElement* inner_editor = InnerEditorElement();
   if (!inner_editor->GetLayoutObject())
     return;
 
-  LayoutPoint adjusted_location = accumulated_offset + Location();
-  LayoutPoint local_point =
-      point_in_container -
-      ToLayoutSize(adjusted_location +
-                   inner_editor->GetLayoutBox()->Location());
+  PhysicalOffset adjusted_location = accumulated_offset + PhysicalLocation();
+  PhysicalOffset local_point = point_in_container - adjusted_location -
+                               inner_editor->GetLayoutBox()->PhysicalLocation();
   if (HasOverflowClip())
-    local_point += ScrolledContentOffset();
+    local_point += PhysicalOffset(ScrolledContentOffset());
   result.SetNodeAndPosition(inner_editor, local_point);
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_text_control.h b/third_party/blink/renderer/core/layout/layout_text_control.h
index 1d26fd5..ffe5021 100644
--- a/third_party/blink/renderer/core/layout/layout_text_control.h
+++ b/third_party/blink/renderer/core/layout/layout_text_control.h
@@ -57,8 +57,8 @@
   void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
 
   void HitInnerEditorElement(HitTestResult&,
-                             const LayoutPoint& point_in_container,
-                             const LayoutPoint& accumulated_offset);
+                             const PhysicalOffset& point_in_container,
+                             const PhysicalOffset& accumulated_offset);
 
   int TextBlockLogicalWidth() const;
   int TextBlockLogicalHeight() const;
diff --git a/third_party/blink/renderer/core/layout/layout_text_control_multi_line.cc b/third_party/blink/renderer/core/layout/layout_text_control_multi_line.cc
index 0395634..75f9419 100644
--- a/third_party/blink/renderer/core/layout/layout_text_control_multi_line.cc
+++ b/third_party/blink/renderer/core/layout/layout_text_control_multi_line.cc
@@ -39,7 +39,7 @@
 bool LayoutTextControlMultiLine::NodeAtPoint(
     HitTestResult& result,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& accumulated_offset,
+    const PhysicalOffset& accumulated_offset,
     HitTestAction hit_test_action) {
   if (!LayoutTextControl::NodeAtPoint(result, location_in_container,
                                       accumulated_offset, hit_test_action))
diff --git a/third_party/blink/renderer/core/layout/layout_text_control_multi_line.h b/third_party/blink/renderer/core/layout/layout_text_control_multi_line.h
index 77a522cc..2a5ca716 100644
--- a/third_party/blink/renderer/core/layout/layout_text_control_multi_line.h
+++ b/third_party/blink/renderer/core/layout/layout_text_control_multi_line.h
@@ -41,7 +41,7 @@
 
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) override;
 
   float GetAvgCharWidth(const AtomicString& family) const override;
diff --git a/third_party/blink/renderer/core/layout/layout_text_control_single_line.cc b/third_party/blink/renderer/core/layout/layout_text_control_single_line.cc
index 9173e2241..64542ad 100644
--- a/third_party/blink/renderer/core/layout/layout_text_control_single_line.cc
+++ b/third_party/blink/renderer/core/layout/layout_text_control_single_line.cc
@@ -139,7 +139,7 @@
 bool LayoutTextControlSingleLine::NodeAtPoint(
     HitTestResult& result,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& accumulated_offset,
+    const PhysicalOffset& accumulated_offset,
     HitTestAction hit_test_action) {
   if (!LayoutTextControl::NodeAtPoint(result, location_in_container,
                                       accumulated_offset, hit_test_action))
@@ -153,13 +153,14 @@
   if (result.InnerNode()->IsDescendantOf(InnerEditorElement()) ||
       result.InnerNode() == GetNode() ||
       (container && container == result.InnerNode())) {
-    LayoutPoint point_in_parent = location_in_container.Point();
+    PhysicalOffset point_in_parent = location_in_container.Point();
     if (container && EditingViewPortElement()) {
-      if (EditingViewPortElement()->GetLayoutBox())
+      if (EditingViewPortElement()->GetLayoutBox()) {
         point_in_parent -=
-            ToLayoutSize(EditingViewPortElement()->GetLayoutBox()->Location());
+            EditingViewPortElement()->GetLayoutBox()->PhysicalLocation();
+      }
       if (container->GetLayoutBox())
-        point_in_parent -= ToLayoutSize(container->GetLayoutBox()->Location());
+        point_in_parent -= container->GetLayoutBox()->PhysicalLocation();
     }
     const LayoutObject* stop_node = result.GetHitTestRequest().GetStopNode();
     if (!stop_node || stop_node->NodeForHitTest() != result.InnerNode()) {
@@ -263,7 +264,7 @@
   return line_height + non_content_height;
 }
 
-void LayoutTextControlSingleLine::Autoscroll(const LayoutPoint& position) {
+void LayoutTextControlSingleLine::Autoscroll(const PhysicalOffset& position) {
   LayoutBox* layout_object = InnerEditorElement()->GetLayoutBox();
   if (!layout_object)
     return;
diff --git a/third_party/blink/renderer/core/layout/layout_text_control_single_line.h b/third_party/blink/renderer/core/layout/layout_text_control_single_line.h
index 1f23080..e890670c 100644
--- a/third_party/blink/renderer/core/layout/layout_text_control_single_line.h
+++ b/third_party/blink/renderer/core/layout/layout_text_control_single_line.h
@@ -58,10 +58,10 @@
 
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) final;
 
-  void Autoscroll(const LayoutPoint&) final;
+  void Autoscroll(const PhysicalOffset&) final;
 
   // Subclassed to forward to our inner div.
   LayoutUnit ScrollLeft() const final;
diff --git a/third_party/blink/renderer/core/layout/layout_text_fragment.cc b/third_party/blink/renderer/core/layout/layout_text_fragment.cc
index 834270f..4c58876 100644
--- a/third_party/blink/renderer/core/layout/layout_text_fragment.cc
+++ b/third_party/blink/renderer/core/layout/layout_text_fragment.cc
@@ -186,8 +186,9 @@
   return ToLayoutTextFragment(child);
 }
 
-void LayoutTextFragment::UpdateHitTestResult(HitTestResult& result,
-                                             const LayoutPoint& point) const {
+void LayoutTextFragment::UpdateHitTestResult(
+    HitTestResult& result,
+    const PhysicalOffset& point) const {
   if (result.InnerNode())
     return;
 
diff --git a/third_party/blink/renderer/core/layout/layout_text_fragment.h b/third_party/blink/renderer/core/layout/layout_text_fragment.h
index c401e00..af2fb50 100644
--- a/third_party/blink/renderer/core/layout/layout_text_fragment.h
+++ b/third_party/blink/renderer/core/layout/layout_text_fragment.h
@@ -111,7 +111,8 @@
   LayoutBlock* BlockForAccompanyingFirstLetter() const;
   UChar PreviousCharacter() const override;
 
-  void UpdateHitTestResult(HitTestResult&, const LayoutPoint&) const override;
+  void UpdateHitTestResult(HitTestResult&,
+                           const PhysicalOffset&) const override;
 
   unsigned start_;
   unsigned fragment_length_;
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc
index c25edce..e4cc98ba 100644
--- a/third_party/blink/renderer/core/layout/layout_view.cc
+++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -155,14 +155,14 @@
     result = cache_result;
   } else {
     LocalFrameView* frame_view = GetFrameView();
-    LayoutRect hit_test_area;
+    PhysicalRect hit_test_area;
     if (frame_view) {
       // Start with a rect sized to the frame, to ensure we include the
       // scrollbars.
-      hit_test_area = LayoutRect(LayoutPoint(), LayoutSize(frame_view->Size()));
+      hit_test_area.size = PhysicalSize(frame_view->Size());
       if (result.GetHitTestRequest().IgnoreClipping()) {
         hit_test_area.Unite(
-            frame_view->DocumentToFrame(LayoutRect(DocumentRect())));
+            frame_view->DocumentToFrame(PhysicalRect(DocumentRect())));
       }
     }
 
@@ -783,13 +783,15 @@
 }
 
 void LayoutView::UpdateHitTestResult(HitTestResult& result,
-                                     const LayoutPoint& point) const {
+                                     const PhysicalOffset& point) const {
   if (result.InnerNode())
     return;
 
   Node* node = GetDocument().documentElement();
   if (node) {
-    LayoutPoint adjusted_point = point;
+    PhysicalOffset adjusted_point = point;
+    if (const auto* layout_box = node->GetLayoutBox())
+      adjusted_point -= layout_box->PhysicalLocation();
     OffsetForContents(adjusted_point);
     result.SetNodeAndPosition(node, adjusted_point);
   }
diff --git a/third_party/blink/renderer/core/layout/layout_view.h b/third_party/blink/renderer/core/layout/layout_view.h
index fd7f8d5..1759734 100644
--- a/third_party/blink/renderer/core/layout/layout_view.h
+++ b/third_party/blink/renderer/core/layout/layout_view.h
@@ -173,7 +173,8 @@
 
   LayoutState* GetLayoutState() const { return layout_state_; }
 
-  void UpdateHitTestResult(HitTestResult&, const LayoutPoint&) const override;
+  void UpdateHitTestResult(HitTestResult&,
+                           const PhysicalOffset&) const override;
 
   ViewFragmentationContext* FragmentationContext() const {
     return fragmentation_context_.get();
diff --git a/third_party/blink/renderer/core/layout/layout_view_test.cc b/third_party/blink/renderer/core/layout/layout_view_test.cc
index e5d500f..1ec16e0 100644
--- a/third_party/blink/renderer/core/layout/layout_view_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_view_test.cc
@@ -24,10 +24,12 @@
  protected:
   bool LayoutNG() { return GetParam(); }
   bool LayoutNGOrAndroidOrWindows() {
-    // TODO(crbug.com/966731): Why is the platform difference?
 #if defined(OS_WIN) || defined(OS_ANDROID)
+    // To deal with platform-specific editing behaviors.
     return true;
 #else
+    // TODO(crbug.com/971414): For now LayoutNG always uses Android/Windows
+    // behavior for ShouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom().
     return LayoutNG();
 #endif
   }
@@ -111,52 +113,57 @@
 
   HitTestResult result;
   // In body, but not in any descendants.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(1, 1)), result);
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(1, 1)), result);
   EXPECT_EQ(GetDocument().body(), result.InnerNode());
-  EXPECT_EQ(LayoutPoint(1, 1), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(1, 1), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text1, 0), TextAffinity::kDownstream),
             result.GetPosition());
 
   // Top-left corner of div and span1.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(51, 101)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(51, 101)), result);
   EXPECT_EQ(text1, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(1, 1), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(1, 1), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text1, 0), TextAffinity::kDownstream),
             result.GetPosition());
 
   // Top-right corner (outside) of div.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(251, 101)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(251, 101)), result);
   EXPECT_EQ(GetDocument().documentElement(), result.InnerNode());
-  EXPECT_EQ(LayoutPoint(251, 101), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(251, 101), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text2, 3), TextAffinity::kUpstream),
             result.GetPosition());
 
   // Top-right corner (inside) of div.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(249, 101)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(249, 101)), result);
   EXPECT_EQ(div, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(199, 1), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(199, 1), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text2, 3), TextAffinity::kUpstream),
             result.GetPosition());
 
   // Top-right corner (inside) of span1.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(99, 101)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(99, 101)), result);
   EXPECT_EQ(text1, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(49, 1), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(49, 1), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text1, 5), TextAffinity::kUpstream),
             result.GetPosition());
 
   // Top-right corner (outside) of span1.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(101, 101)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(101, 101)), result);
   EXPECT_EQ(div, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(51, 1), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(51, 1), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text2, 0), TextAffinity::kDownstream),
             result.GetPosition());
 
   // Bottom-left corner (outside) of div.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(51, 181)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(51, 181)), result);
   EXPECT_EQ(GetDocument().documentElement(), result.InnerNode());
-  EXPECT_EQ(LayoutPoint(51, 181), result.LocalPoint());
-  // TODO(crbug.com/966731): Verify if the difference reflects any issue.
+  EXPECT_EQ(PhysicalOffset(51, 181), result.LocalPoint());
   EXPECT_EQ(
       LayoutNGOrAndroidOrWindows()
           ? PositionWithAffinity(Position(text1, 0), TextAffinity::kDownstream)
@@ -164,10 +171,10 @@
       result.GetPosition());
 
   // Bottom-left corner (inside) of div.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(51, 179)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(51, 179)), result);
   EXPECT_EQ(div, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(1, 79), result.LocalPoint());
-  // TODO(crbug.com/966731): Verify if the difference reflects any issue.
+  EXPECT_EQ(PhysicalOffset(1, 79), result.LocalPoint());
   EXPECT_EQ(
       LayoutNGOrAndroidOrWindows()
           ? PositionWithAffinity(Position(text1, 0), TextAffinity::kDownstream)
@@ -175,10 +182,10 @@
       result.GetPosition());
 
   // Bottom-left corner (outside) of span1.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(51, 111)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(51, 111)), result);
   EXPECT_EQ(div, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(1, 11), result.LocalPoint());
-  // TODO(crbug.com/966731): Verify if the difference reflects any issue.
+  EXPECT_EQ(PhysicalOffset(1, 11), result.LocalPoint());
   EXPECT_EQ(
       LayoutNGOrAndroidOrWindows()
           ? PositionWithAffinity(Position(text1, 0), TextAffinity::kDownstream)
@@ -186,9 +193,10 @@
       result.GetPosition());
 
   // Top-left corner of span2.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(101, 131)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(101, 131)), result);
   EXPECT_EQ(text2, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(51, 1), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(51, 1), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text2, 0), TextAffinity::kDownstream),
             result.GetPosition());
 }
@@ -223,24 +231,25 @@
 
   HitTestResult result;
   // In body, but not in any descendants.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(1, 1)), result);
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(1, 1)), result);
   EXPECT_EQ(GetDocument().body(), result.InnerNode());
-  EXPECT_EQ(LayoutPoint(1, 1), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(1, 1), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text1, 0), TextAffinity::kDownstream),
             result.GetPosition());
 
   // Top-left corner of div and span1.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(51, 101)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(51, 101)), result);
   EXPECT_EQ(text1, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(1, 1), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(1, 1), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text1, 0), TextAffinity::kDownstream),
             result.GetPosition());
 
   // Top-right corner (outside) of div.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(251, 101)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(251, 101)), result);
   EXPECT_EQ(GetDocument().documentElement(), result.InnerNode());
-  EXPECT_EQ(LayoutPoint(251, 101), result.LocalPoint());
-  // TODO(crbug.com/966731): Verify if the difference reflects any issue.
+  EXPECT_EQ(PhysicalOffset(251, 101), result.LocalPoint());
   EXPECT_EQ(
       LayoutNGOrAndroidOrWindows()
           ? PositionWithAffinity(Position(text1, 0), TextAffinity::kDownstream)
@@ -248,10 +257,10 @@
       result.GetPosition());
 
   // Top-right corner (inside) of div.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(249, 101)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(249, 101)), result);
   EXPECT_EQ(div, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(199, 1), result.LocalPoint());
-  // TODO(crbug.com/966731): Verify if the difference reflects any issue.
+  EXPECT_EQ(PhysicalOffset(199, 1), result.LocalPoint());
   EXPECT_EQ(
       LayoutNGOrAndroidOrWindows()
           ? PositionWithAffinity(Position(text1, 0), TextAffinity::kDownstream)
@@ -259,17 +268,18 @@
       result.GetPosition());
 
   // Top-right corner (inside) of span1.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(59, 101)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(59, 101)), result);
   EXPECT_EQ(text1, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(9, 1), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(9, 1), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text1, 0), TextAffinity::kDownstream),
             result.GetPosition());
 
   // Top-right corner (outside) of span1.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(61, 101)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(61, 101)), result);
   EXPECT_EQ(div, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(11, 1), result.LocalPoint());
-  // TODO(crbug.com/966731): Verify if the difference reflects any issue.
+  EXPECT_EQ(PhysicalOffset(11, 1), result.LocalPoint());
   EXPECT_EQ(
       LayoutNGOrAndroidOrWindows()
           ? PositionWithAffinity(Position(text1, 0), TextAffinity::kDownstream)
@@ -277,23 +287,26 @@
       result.GetPosition());
 
   // Bottom-left corner (outside) of span1.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(51, 181)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(51, 181)), result);
   EXPECT_EQ(GetDocument().documentElement(), result.InnerNode());
-  EXPECT_EQ(LayoutPoint(51, 181), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(51, 181), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text2, 3), TextAffinity::kUpstream),
             result.GetPosition());
 
   // Bottom-left corner (inside) of span1.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(51, 179)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(51, 179)), result);
   EXPECT_EQ(div, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(1, 79), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(1, 79), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text2, 3), TextAffinity::kUpstream),
             result.GetPosition());
 
   // Top-left corner of span2.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(81, 151)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(81, 151)), result);
   EXPECT_EQ(text2, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(1, 51), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(1, 51), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text2, 0), TextAffinity::kDownstream),
             result.GetPosition());
 }
@@ -328,17 +341,17 @@
 
   HitTestResult result;
   // In body, but not in any descendants.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(1, 1)), result);
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(1, 1)), result);
   EXPECT_EQ(GetDocument().body(), result.InnerNode());
-  EXPECT_EQ(LayoutPoint(1, 1), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(1, 1), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text1, 0), TextAffinity::kDownstream),
             result.GetPosition());
 
   // Top-left corner of div.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(51, 101)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(51, 101)), result);
   EXPECT_EQ(div, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(LayoutNG() ? 1 : 199, 1), result.LocalPoint());
-  // TODO(crbug.com/966731): Verify if the difference reflects any issue.
+  EXPECT_EQ(PhysicalOffset(1, 1), result.LocalPoint());
   EXPECT_EQ(
       LayoutNGOrAndroidOrWindows()
           ? PositionWithAffinity(Position(text1, 0), TextAffinity::kDownstream)
@@ -346,10 +359,10 @@
       result.GetPosition());
 
   // Top-right corner (outside) of div.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(251, 101)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(251, 101)), result);
   EXPECT_EQ(GetDocument().documentElement(), result.InnerNode());
-  EXPECT_EQ(LayoutPoint(251, 101), result.LocalPoint());
-  // TODO(crbug.com/966731): Verify if the difference reflects any issue.
+  EXPECT_EQ(PhysicalOffset(251, 101), result.LocalPoint());
   EXPECT_EQ(
       LayoutNGOrAndroidOrWindows()
           ? PositionWithAffinity(Position(text1, 0), TextAffinity::kDownstream)
@@ -357,38 +370,42 @@
       result.GetPosition());
 
   // Top-right corner (inside) of div and span1.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(249, 101)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(249, 101)), result);
   EXPECT_EQ(text1, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(LayoutNG() ? 199 : 1, 1), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(199, 1), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text1, 0), TextAffinity::kDownstream),
             result.GetPosition());
 
   // Bottom-right corner (inside) of span1.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(249, 149)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(249, 149)), result);
   EXPECT_EQ(text1, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(LayoutNG() ? 199 : 1, 49), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(199, 49), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text1, 5), TextAffinity::kUpstream),
             result.GetPosition());
 
   // Bottom-right corner (outside) of span1 but inside of div.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(249, 151)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(249, 151)), result);
   EXPECT_EQ(div, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(LayoutNG() ? 199 : 1, 51), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(199, 51), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text2, 0), TextAffinity::kDownstream),
             result.GetPosition());
 
   // Bottom-left corner (outside) of div.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(51, 181)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(51, 181)), result);
   EXPECT_EQ(GetDocument().documentElement(), result.InnerNode());
-  EXPECT_EQ(LayoutPoint(51, 181), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(51, 181), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text2, 3), TextAffinity::kUpstream),
             result.GetPosition());
 
   // Bottom-left corner (inside) of div.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(51, 179)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(51, 179)), result);
   EXPECT_EQ(div, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(LayoutNG() ? 1 : 199, 79), result.LocalPoint());
-  // TODO(crbug.com/966731): Verify if the difference reflects any issue.
+  EXPECT_EQ(PhysicalOffset(1, 79), result.LocalPoint());
   EXPECT_EQ(
       LayoutNGOrAndroidOrWindows()
           ? PositionWithAffinity(Position(text2, 3), TextAffinity::kUpstream)
@@ -396,18 +413,109 @@
       result.GetPosition());
 
   // Bottom-left corner (outside) of span1.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(241, 151)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(241, 151)), result);
   EXPECT_EQ(div, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(LayoutNG() ? 191 : 9, 51), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(191, 51), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text2, 0), TextAffinity::kDownstream),
             result.GetPosition());
 
   // Top-right corner (inside) of span2.
-  GetLayoutView().HitTest(HitTestLocation(LayoutPoint(219, 151)), result);
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(219, 151)), result);
   EXPECT_EQ(text2, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(LayoutNG() ? 199 : 1, 51), result.LocalPoint());
+  EXPECT_EQ(PhysicalOffset(199, 51), result.LocalPoint());
   EXPECT_EQ(PositionWithAffinity(Position(text2, 0), TextAffinity::kDownstream),
             result.GetPosition());
 }
 
+TEST_P(LayoutViewTest, HitTestVerticalRLRoot) {
+  LoadAhem();
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      html { writing-mode: vertical-rl; }
+      body { margin: 0 }
+    </style>
+    <div id="div" style="font: 10px/10px Ahem; width: 200px; height: 80px">
+      <span id="span">ABCDE</span>
+    </div>
+  )HTML");
+
+  // (0,0)     (600, 0)         (800, 0)
+  // +----...----+---------------+
+  // |           |              A|
+  // |           |              B|
+  // |           |              C|
+  // |           |     (div)    D|
+  // | (screen)  |              E|
+  // |           |               |
+  // |           |               |
+  // |           +---------------+ (800, 80)
+  // |       (600, 80)           |
+  // .                           .
+  // +----...--------------------+ (800, 600)
+
+  auto* div = GetDocument().getElementById("div");
+  auto* text = GetDocument().getElementById("span")->firstChild();
+  HitTestResult result;
+  // Not in any element. Should fallback to documentElement.
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(1, 1)), result);
+  EXPECT_EQ(GetDocument().documentElement(), result.InnerNode());
+  EXPECT_EQ(PhysicalOffset(-599, 1), result.LocalPoint());
+  EXPECT_EQ(
+      LayoutNGOrAndroidOrWindows()
+          ? PositionWithAffinity(Position(text, 0), TextAffinity::kDownstream)
+          : PositionWithAffinity(Position(text, 5), TextAffinity::kDownstream),
+      result.GetPosition());
+
+  // Top-left corner (inside) of div.
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(601, 1)), result);
+  EXPECT_EQ(div, result.InnerNode());
+  EXPECT_EQ(PhysicalOffset(1, 1), result.LocalPoint());
+  EXPECT_EQ(
+      LayoutNGOrAndroidOrWindows()
+          ? PositionWithAffinity(Position(text, 0), TextAffinity::kDownstream)
+          : PositionWithAffinity(Position(text, 5), TextAffinity::kDownstream),
+      result.GetPosition());
+
+  // Top-right corner (outside) of div. Should fallback to documentElement.
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(801, 1)), result);
+  EXPECT_EQ(GetDocument().documentElement(), result.InnerNode());
+  EXPECT_EQ(PhysicalOffset(201, 1), result.LocalPoint());
+  EXPECT_EQ(
+      LayoutNGOrAndroidOrWindows()
+          ? PositionWithAffinity(Position(text, 0), TextAffinity::kDownstream)
+          : PositionWithAffinity(Position(text, 0), TextAffinity::kDownstream),
+      result.GetPosition());
+
+  // Top-right corner (inside) of div and span.
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(799, 1)), result);
+  EXPECT_EQ(text, result.InnerNode());
+  EXPECT_EQ(PhysicalOffset(199, 1), result.LocalPoint());
+  EXPECT_EQ(PositionWithAffinity(Position(text, 0), TextAffinity::kDownstream),
+            result.GetPosition());
+
+  // Bottom-right corner (outside) of span1 but inside of div.
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(799, 51)), result);
+  EXPECT_EQ(div, result.InnerNode());
+  EXPECT_EQ(PhysicalOffset(199, 51), result.LocalPoint());
+  EXPECT_EQ(PositionWithAffinity(Position(text, 5), TextAffinity::kUpstream),
+            result.GetPosition());
+
+  // Bottom-left corner (outside) of div.
+  result = HitTestResult();
+  GetLayoutView().HitTest(HitTestLocation(PhysicalOffset(599, 81)), result);
+  EXPECT_EQ(GetDocument().documentElement(), result.InnerNode());
+  EXPECT_EQ(PhysicalOffset(-1, 81), result.LocalPoint());
+  EXPECT_EQ(
+      LayoutNGOrAndroidOrWindows()
+          ? PositionWithAffinity(Position(text, 5), TextAffinity::kUpstream)
+          : PositionWithAffinity(Position(text, 5), TextAffinity::kDownstream),
+      result.GetPosition());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/line/ellipsis_box.cc b/third_party/blink/renderer/core/layout/line/ellipsis_box.cc
index 416e27913..d3844258 100644
--- a/third_party/blink/renderer/core/layout/line/ellipsis_box.cc
+++ b/third_party/blink/renderer/core/layout/line/ellipsis_box.cc
@@ -50,20 +50,16 @@
 
 bool EllipsisBox::NodeAtPoint(HitTestResult& result,
                               const HitTestLocation& location_in_container,
-                              const LayoutPoint& accumulated_offset,
+                              const PhysicalOffset& accumulated_offset,
                               LayoutUnit line_top,
                               LayoutUnit line_bottom) {
-  LayoutPoint adjusted_location = accumulated_offset + Location();
-
-  LayoutPoint box_origin = PhysicalLocation();
-  box_origin.MoveBy(accumulated_offset);
-  LayoutRect bounds_rect(box_origin, Size());
+  PhysicalOffset adjusted_location = accumulated_offset + PhysicalLocation();
+  PhysicalRect bounds_rect(adjusted_location, Size());
   if (VisibleToHitTestRequest(result.GetHitTestRequest()) &&
       bounds_rect.Intersects(
           HitTestLocation::RectForPoint(location_in_container.Point()))) {
     GetLineLayoutItem().UpdateHitTestResult(
-        result,
-        location_in_container.Point() - ToLayoutSize(adjusted_location));
+        result, location_in_container.Point() - adjusted_location);
     if (result.AddNodeToListBasedTestResult(GetLineLayoutItem().GetNode(),
                                             location_in_container,
                                             bounds_rect) == kStopHitTesting)
diff --git a/third_party/blink/renderer/core/layout/line/ellipsis_box.h b/third_party/blink/renderer/core/layout/line/ellipsis_box.h
index d5f4370..41e1c80 100644
--- a/third_party/blink/renderer/core/layout/line/ellipsis_box.h
+++ b/third_party/blink/renderer/core/layout/line/ellipsis_box.h
@@ -58,7 +58,7 @@
              LayoutUnit line_bottom) const override;
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    LayoutUnit line_top,
                    LayoutUnit line_bottom) override;
   IntRect SelectionRect() const;
diff --git a/third_party/blink/renderer/core/layout/line/inline_box.cc b/third_party/blink/renderer/core/layout/line/inline_box.cc
index 3baa2cb9..af27b8f0 100644
--- a/third_party/blink/renderer/core/layout/line/inline_box.cc
+++ b/third_party/blink/renderer/core/layout/line/inline_box.cc
@@ -142,7 +142,7 @@
     string_inlinebox.Append(' ');
   string_inlinebox.AppendFormat(
       "\t%s %p {pos=%g,%g size=%g,%g} baseline=%i/%i",
-      GetLineLayoutItem().DecoratedName().Ascii().data(),
+      GetLineLayoutItem().DecoratedName().Ascii().c_str(),
       GetLineLayoutItem().DebugPointer(), X().ToFloat(), Y().ToFloat(),
       Width().ToFloat(), Height().ToFloat(),
       BaselinePosition(kAlphabeticBaseline).ToInt(),
@@ -240,21 +240,14 @@
 
 bool InlineBox::NodeAtPoint(HitTestResult& result,
                             const HitTestLocation& location_in_container,
-                            const LayoutPoint& accumulated_offset,
+                            const PhysicalOffset& accumulated_offset,
                             LayoutUnit /* lineTop */,
                             LayoutUnit /* lineBottom */) {
   // Hit test all phases of replaced elements atomically, as though the replaced
   // element established its own stacking context. (See Appendix E.2, section
   // 6.4 on inline block/table elements in the CSS2.1 specification.)
-  LayoutPoint child_point = accumulated_offset;
-  // Faster than calling containingBlock().
-  if (Parent()->GetLineLayoutItem().HasFlippedBlocksWritingMode())
-    child_point =
-        GetLineLayoutItem().ContainingBlock().FlipForWritingModeForChild(
-            LineLayoutBox(GetLineLayoutItem()), child_point);
-
   return GetLineLayoutItem().HitTestAllPhases(result, location_in_container,
-                                              child_point);
+                                              accumulated_offset);
 }
 
 const RootInlineBox& InlineBox::Root() const {
@@ -336,10 +329,10 @@
     Parent()->ClearKnownToHaveNoOverflow();
 }
 
-LayoutPoint InlineBox::PhysicalLocation() const {
+PhysicalOffset InlineBox::PhysicalLocation() const {
   LayoutRect rect(Location(), Size());
   FlipForWritingMode(rect);
-  return rect.Location();
+  return PhysicalOffset(rect.Location());
 }
 
 void InlineBox::FlipForWritingMode(LayoutRect& rect) const {
diff --git a/third_party/blink/renderer/core/layout/line/inline_box.h b/third_party/blink/renderer/core/layout/line/inline_box.h
index 9618198..8d5f587 100644
--- a/third_party/blink/renderer/core/layout/line/inline_box.h
+++ b/third_party/blink/renderer/core/layout/line/inline_box.h
@@ -103,7 +103,7 @@
                      LayoutUnit line_bottom) const;
   virtual bool NodeAtPoint(HitTestResult&,
                            const HitTestLocation& location_in_container,
-                           const LayoutPoint& accumulated_offset,
+                           const PhysicalOffset& accumulated_offset,
                            LayoutUnit line_top,
                            LayoutUnit line_bottom);
 
@@ -353,7 +353,7 @@
 
   // Physical location of the top-left corner of the box in the containing
   // block.
-  LayoutPoint PhysicalLocation() const;
+  PhysicalOffset PhysicalLocation() const;
 
   // TODO(szager): The Rect versions should return a rect, not modify the
   // argument.
diff --git a/third_party/blink/renderer/core/layout/line/inline_flow_box.cc b/third_party/blink/renderer/core/layout/line/inline_flow_box.cc
index f976727..61c9dc82 100644
--- a/third_party/blink/renderer/core/layout/line/inline_flow_box.cc
+++ b/third_party/blink/renderer/core/layout/line/inline_flow_box.cc
@@ -1355,12 +1355,12 @@
 
 bool InlineFlowBox::NodeAtPoint(HitTestResult& result,
                                 const HitTestLocation& location_in_container,
-                                const LayoutPoint& accumulated_offset,
+                                const PhysicalOffset& accumulated_offset,
                                 LayoutUnit line_top,
                                 LayoutUnit line_bottom) {
-  LayoutRect overflow_rect(VisualOverflowRect(line_top, line_bottom));
-  FlipForWritingMode(overflow_rect);
-  overflow_rect.MoveBy(accumulated_offset);
+  PhysicalRect overflow_rect =
+      PhysicalVisualOverflowRect(line_top, line_bottom);
+  overflow_rect.Move(accumulated_offset);
   if (!location_in_container.Intersects(overflow_rect))
     return false;
 
@@ -1380,8 +1380,7 @@
       if (curr->NodeAtPoint(result, location_in_container, accumulated_offset,
                             line_top, line_bottom)) {
         GetLineLayoutItem().UpdateHitTestResult(
-            result,
-            location_in_container.Point() - ToLayoutSize(accumulated_offset));
+            result, location_in_container.Point() - accumulated_offset);
         return true;
       }
     }
@@ -1423,12 +1422,13 @@
   if (GetLineLayoutItem().IsBox() &&
       ToLayoutBox(LineLayoutAPIShim::LayoutObjectFrom(GetLineLayoutItem()))
           ->HitTestClippedOutByBorder(location_in_container,
-                                      overflow_rect.Location()))
+                                      overflow_rect.offset))
     return false;
 
   if (GetLineLayoutItem().StyleRef().HasBorderRadius()) {
+    // TODO(layout-dev): LogicalFrameRect() seems incorrect.
     LayoutRect border_rect = LogicalFrameRect();
-    border_rect.MoveBy(accumulated_offset);
+    border_rect.MoveBy(accumulated_offset.ToLayoutPoint());
     FloatRoundedRect border =
         GetLineLayoutItem().StyleRef().GetRoundedBorderFor(
             border_rect, IncludeLogicalLeftEdge(), IncludeLogicalRightEdge());
@@ -1437,21 +1437,20 @@
   }
 
   // Now check ourselves.
-  LayoutRect rect =
+  LayoutRect layout_rect =
       InlineFlowBoxPainter(*this).FrameRectClampedToLineTopAndBottomIfNeeded();
-
-  FlipForWritingMode(rect);
-  rect.MoveBy(accumulated_offset);
+  FlipForWritingMode(layout_rect);
+  PhysicalRect rect(layout_rect);
+  rect.Move(accumulated_offset);
 
   // Pixel snap hit testing.
-  rect = LayoutRect(PixelSnappedIntRect(rect));
+  rect = PhysicalRect(PixelSnappedIntRect(rect));
   if (VisibleToHitTestRequest(result.GetHitTestRequest()) &&
       location_in_container.Intersects(rect)) {
     // Don't add in m_topLeft here, we want coords in the containing block's
     // coordinate space.
     GetLineLayoutItem().UpdateHitTestResult(
-        result, FlipForWritingMode(location_in_container.Point() -
-                                   ToLayoutSize(accumulated_offset)));
+        result, location_in_container.Point() - accumulated_offset);
     if (result.AddNodeToListBasedTestResult(GetLineLayoutItem().GetNode(),
                                             location_in_container,
                                             rect) == kStopHitTesting)
diff --git a/third_party/blink/renderer/core/layout/line/inline_flow_box.h b/third_party/blink/renderer/core/layout/line/inline_flow_box.h
index c8b322d..587ddd1 100644
--- a/third_party/blink/renderer/core/layout/line/inline_flow_box.h
+++ b/third_party/blink/renderer/core/layout/line/inline_flow_box.h
@@ -130,7 +130,7 @@
              LayoutUnit line_bottom) const override;
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    LayoutUnit line_top,
                    LayoutUnit line_bottom) override;
 
@@ -318,6 +318,12 @@
                ? overflow_->visual_overflow->VisualOverflowRect()
                : FrameRectIncludingLineHeight(line_top, line_bottom);
   }
+  PhysicalRect PhysicalVisualOverflowRect(LayoutUnit line_top,
+                                          LayoutUnit line_bottom) const {
+    LayoutRect rect = VisualOverflowRect(line_top, line_bottom);
+    FlipForWritingMode(rect);
+    return PhysicalRect(rect);
+  }
   LayoutUnit LogicalLeftVisualOverflow() const {
     return VisualOverflowIsSet()
                ? (IsHorizontal()
diff --git a/third_party/blink/renderer/core/layout/line/inline_text_box.cc b/third_party/blink/renderer/core/layout/line/inline_text_box.cc
index 7b5efac..d7bba44a 100644
--- a/third_party/blink/renderer/core/layout/line/inline_text_box.cc
+++ b/third_party/blink/renderer/core/layout/line/inline_text_box.cc
@@ -431,20 +431,19 @@
 
 bool InlineTextBox::NodeAtPoint(HitTestResult& result,
                                 const HitTestLocation& location_in_container,
-                                const LayoutPoint& accumulated_offset,
+                                const PhysicalOffset& accumulated_offset,
                                 LayoutUnit /* lineTop */,
                                 LayoutUnit /*lineBottom*/) {
   if (IsLineBreak() || truncation_ == kCFullTruncation)
     return false;
 
-  LayoutPoint box_origin = PhysicalLocation();
-  box_origin.MoveBy(accumulated_offset);
-  LayoutRect rect(box_origin, Size());
+  PhysicalOffset box_origin = PhysicalLocation();
+  box_origin += accumulated_offset;
+  PhysicalRect rect(box_origin, Size());
   if (VisibleToHitTestRequest(result.GetHitTestRequest()) &&
       location_in_container.Intersects(rect)) {
     GetLineLayoutItem().UpdateHitTestResult(
-        result, FlipForWritingMode(location_in_container.Point() -
-                                   ToLayoutSize(accumulated_offset)));
+        result, location_in_container.Point() - accumulated_offset);
     if (result.AddNodeToListBasedTestResult(GetLineLayoutItem().GetNode(),
                                             location_in_container,
                                             rect) == kStopHitTesting)
diff --git a/third_party/blink/renderer/core/layout/line/inline_text_box.h b/third_party/blink/renderer/core/layout/line/inline_text_box.h
index 0c97dfe..15796a2 100644
--- a/third_party/blink/renderer/core/layout/line/inline_text_box.h
+++ b/third_party/blink/renderer/core/layout/line/inline_text_box.h
@@ -160,7 +160,7 @@
              LayoutUnit line_bottom) const override;
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    LayoutUnit line_top,
                    LayoutUnit line_bottom) override;
 
diff --git a/third_party/blink/renderer/core/layout/line/line_box_list.cc b/third_party/blink/renderer/core/layout/line/line_box_list.cc
index de4cd29..777999a0 100644
--- a/third_party/blink/renderer/core/layout/line/line_box_list.cc
+++ b/third_party/blink/renderer/core/layout/line/line_box_list.cc
@@ -145,7 +145,7 @@
                                       LayoutUnit logical_top,
                                       LayoutUnit logical_bottom,
                                       const CullRect& cull_rect,
-                                      const LayoutPoint& offset) const {
+                                      const PhysicalOffset& offset) const {
   LineLayoutBox block;
   if (layout_object.IsBox())
     block = LineLayoutBox(layout_object);
@@ -157,11 +157,11 @@
   physical_start = std::min(physical_start, physical_end);
 
   if (layout_object.StyleRef().IsHorizontalWritingMode()) {
-    physical_start += offset.Y();
+    physical_start += offset.top;
     return cull_rect.IntersectsVerticalRange(physical_start,
                                              physical_start + physical_extent);
   } else {
-    physical_start += offset.X();
+    physical_start += offset.left;
     return cull_rect.IntersectsHorizontalRange(
         physical_start, physical_start + physical_extent);
   }
@@ -169,7 +169,7 @@
 
 bool LineBoxList::AnyLineIntersectsRect(LineLayoutBoxModel layout_object,
                                         const CullRect& cull_rect,
-                                        const LayoutPoint& offset) const {
+                                        const PhysicalOffset& offset) const {
   // We can check the first box and last box and avoid painting/hit testing if
   // we don't intersect. This is a quick short-circuit that we can take to avoid
   // walking any lines.
@@ -190,7 +190,7 @@
 bool LineBoxList::LineIntersectsDirtyRect(LineLayoutBoxModel layout_object,
                                           InlineFlowBox* box,
                                           const CullRect& cull_rect,
-                                          const LayoutPoint& offset) const {
+                                          const PhysicalOffset& offset) const {
   RootInlineBox& root = box->Root();
   LayoutUnit logical_top = std::min<LayoutUnit>(
       box->LogicalTopVisualOverflow(root.LineTop()), root.SelectionTop());
@@ -204,7 +204,7 @@
 bool LineBoxList::HitTest(LineLayoutBoxModel layout_object,
                           HitTestResult& result,
                           const HitTestLocation& location_in_container,
-                          const LayoutPoint& accumulated_offset,
+                          const PhysicalOffset& accumulated_offset,
                           HitTestAction hit_test_action) const {
   if (hit_test_action != kHitTestForeground)
     return false;
@@ -217,14 +217,14 @@
   if (!First())
     return false;
 
-  const LayoutPoint& point = location_in_container.Point();
+  const PhysicalOffset& point = location_in_container.Point();
   IntRect hit_search_bounding_box = location_in_container.EnclosingIntRect();
 
   CullRect cull_rect(
       First()->IsHorizontal()
-          ? IntRect(point.X().ToInt(), hit_search_bounding_box.Y(), 1,
+          ? IntRect(point.left.ToInt(), hit_search_bounding_box.Y(), 1,
                     hit_search_bounding_box.Height())
-          : IntRect(hit_search_bounding_box.X(), point.Y().ToInt(),
+          : IntRect(hit_search_bounding_box.X(), point.top.ToInt(),
                     hit_search_bounding_box.Width(), 1));
 
   if (!AnyLineIntersectsRect(layout_object, cull_rect, accumulated_offset))
@@ -244,8 +244,7 @@
                             root.LineTop(), root.LineBottom());
       if (inside) {
         layout_object.UpdateHitTestResult(
-            result,
-            location_in_container.Point() - ToLayoutSize(accumulated_offset));
+            result, location_in_container.Point() - accumulated_offset);
         return true;
       }
     }
diff --git a/third_party/blink/renderer/core/layout/line/line_box_list.h b/third_party/blink/renderer/core/layout/line/line_box_list.h
index 749752d..49f7d23 100644
--- a/third_party/blink/renderer/core/layout/line/line_box_list.h
+++ b/third_party/blink/renderer/core/layout/line/line_box_list.h
@@ -41,10 +41,10 @@
 class HitTestResult;
 class InlineFlowBox;
 class InlineTextBox;
-class LayoutPoint;
 class LayoutUnit;
 class LineLayoutBoxModel;
 class LineLayoutItem;
+struct PhysicalOffset;
 
 template <typename InlineBoxType>
 class InlineBoxList {
@@ -155,22 +155,22 @@
   bool HitTest(LineLayoutBoxModel,
                HitTestResult&,
                const HitTestLocation& location_in_container,
-               const LayoutPoint& accumulated_offset,
+               const PhysicalOffset& accumulated_offset,
                HitTestAction) const;
   bool AnyLineIntersectsRect(LineLayoutBoxModel,
                              const CullRect&,
-                             const LayoutPoint&) const;
+                             const PhysicalOffset&) const;
   bool LineIntersectsDirtyRect(LineLayoutBoxModel,
                                InlineFlowBox*,
                                const CullRect&,
-                               const LayoutPoint&) const;
+                               const PhysicalOffset&) const;
 
  private:
   bool RangeIntersectsRect(LineLayoutBoxModel,
                            LayoutUnit logical_top,
                            LayoutUnit logical_bottom,
                            const CullRect&,
-                           const LayoutPoint&) const;
+                           const PhysicalOffset&) const;
 };
 
 class CORE_EXPORT InlineTextBoxList : public InlineBoxList<InlineTextBox> {
diff --git a/third_party/blink/renderer/core/layout/line/root_inline_box.cc b/third_party/blink/renderer/core/layout/line/root_inline_box.cc
index 173c2525..205cbc4 100644
--- a/third_party/blink/renderer/core/layout/line/root_inline_box.cc
+++ b/third_party/blink/renderer/core/layout/line/root_inline_box.cc
@@ -182,7 +182,7 @@
 
 bool RootInlineBox::NodeAtPoint(HitTestResult& result,
                                 const HitTestLocation& location_in_container,
-                                const LayoutPoint& accumulated_offset,
+                                const PhysicalOffset& accumulated_offset,
                                 LayoutUnit line_top,
                                 LayoutUnit line_bottom) {
   if (HasEllipsisBox() && VisibleToHitTestRequest(result.GetHitTestRequest())) {
@@ -190,8 +190,7 @@
                                       accumulated_offset, line_top,
                                       line_bottom)) {
       GetLineLayoutItem().UpdateHitTestResult(
-          result,
-          location_in_container.Point() - ToLayoutSize(accumulated_offset));
+          result, location_in_container.Point() - accumulated_offset);
       return true;
     }
   }
diff --git a/third_party/blink/renderer/core/layout/line/root_inline_box.h b/third_party/blink/renderer/core/layout/line/root_inline_box.h
index a5660d7..02812a2 100644
--- a/third_party/blink/renderer/core/layout/line/root_inline_box.h
+++ b/third_party/blink/renderer/core/layout/line/root_inline_box.h
@@ -140,7 +140,7 @@
              LayoutUnit line_bottom) const override;
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    LayoutUnit line_top,
                    LayoutUnit line_bottom) override;
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc
index 987d5b5c..b661a5b7 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc
@@ -174,7 +174,7 @@
 String NGInlineItem::ToString() const {
   return String::Format("NGInlineItem. Type: '%s'. LayoutObject: '%s'",
                         NGInlineItemTypeToString(Type()),
-                        GetLayoutObject()->DebugName().Ascii().data());
+                        GetLayoutObject()->DebugName().Ascii().c_str());
 }
 
 // Split |items[index]| to 2 items at |offset|.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index 4007a0c..70b6bd74 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -85,6 +85,13 @@
   // https://drafts.csswg.org/css2/visudet.html#line-height
   if (!quirks_mode_ || !item.IsEmptyItem())
     box->ComputeTextMetrics(*item.Style(), baseline_type_);
+
+  if (item.Style()->HasMask()) {
+    // Layout may change the bounding box, which affects MaskClip.
+    if (LayoutObject* object = item.GetLayoutObject())
+      object->SetNeedsPaintPropertyUpdate();
+  }
+
   return box;
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
index c9e200b..49241555 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
@@ -280,25 +280,23 @@
 bool LayoutNGMixin<Base>::NodeAtPoint(
     HitTestResult& result,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& accumulated_offset,
+    const PhysicalOffset& accumulated_offset,
     HitTestAction action) {
   const NGPaintFragment* paint_fragment = PaintFragment();
   if (!paint_fragment) {
     return LayoutBlockFlow::NodeAtPoint(result, location_in_container,
                                         accumulated_offset, action);
   }
-  // In LayoutBox::NodeAtPoint() and subclass overrides, it is guaranteed that
-  // |accumulated_offset + Location()| equals the physical offset of the current
-  // LayoutBox in the paint layer, regardless of writing mode or whether the box
-  // was placed by NG or legacy.
-  const LayoutPoint physical_offset = accumulated_offset + Base::Location();
+
+  const PhysicalOffset physical_offset =
+      accumulated_offset + Base::PhysicalLocation();
   if (!this->IsEffectiveRootScroller()) {
     // Check if we need to do anything at all.
     // If we have clipping, then we can't have any spillout.
-    LayoutRect overflow_box = Base::HasOverflowClip()
-                                  ? Base::BorderBoxRect()
-                                  : Base::VisualOverflowRect();
-    overflow_box.MoveBy(physical_offset);
+    PhysicalRect overflow_box = Base::HasOverflowClip()
+                                    ? Base::PhysicalBorderBoxRect()
+                                    : Base::PhysicalVisualOverflowRect();
+    overflow_box.Move(physical_offset);
     if (!location_in_container.Intersects(overflow_box))
       return false;
   }
@@ -327,8 +325,10 @@
   if (!PaintFragment())
     return Base::CreatePositionWithAffinity(0);
 
+  // Flip because |point| is in flipped physical coordinates while
+  // NGPaintFragment::PositionForPoint() requires pure physical coordinates.
   const PositionWithAffinity ng_position =
-      PaintFragment()->PositionForPoint(PhysicalOffset(point));
+      PaintFragment()->PositionForPoint(Base::FlipForWritingMode(point));
   if (ng_position.IsNotNull())
     return ng_position;
   return Base::CreatePositionWithAffinity(0);
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
index 7f45d582..ff3f720e2 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
@@ -45,7 +45,7 @@
 
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) final;
 
   PositionWithAffinity PositionForPoint(const LayoutPoint&) const override;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 774b9f0..9f22728b 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -621,7 +621,7 @@
 
 String NGBlockNode::ToString() const {
   return String::Format("NGBlockNode: '%s'",
-                        GetLayoutBox()->DebugName().Ascii().data());
+                        GetLayoutBox()->DebugName().Ascii().c_str());
 }
 
 void NGBlockNode::CopyFragmentDataToLayoutBox(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc
index bc8d8bb..6b7084e 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc
@@ -102,12 +102,12 @@
 
 String NGConstraintSpace::ToString() const {
   return String::Format("Offset: %s,%s Size: %sx%s Clearance: %s",
-                        bfc_offset_.line_offset.ToString().Ascii().data(),
-                        bfc_offset_.block_offset.ToString().Ascii().data(),
-                        AvailableSize().inline_size.ToString().Ascii().data(),
-                        AvailableSize().block_size.ToString().Ascii().data(),
+                        bfc_offset_.line_offset.ToString().Ascii().c_str(),
+                        bfc_offset_.block_offset.ToString().Ascii().c_str(),
+                        AvailableSize().inline_size.ToString().Ascii().c_str(),
+                        AvailableSize().block_size.ToString().Ascii().c_str(),
                         HasClearanceOffset()
-                            ? ClearanceOffset().ToString().Ascii().data()
+                            ? ClearanceOffset().ToString().Ascii().c_str()
                             : "none");
 }
 
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 38ae5af7..606cbace 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
@@ -474,12 +474,12 @@
 String NGPhysicalFragment::ToString() const {
   StringBuilder output;
   output.AppendFormat("Type: '%d' Size: '%s'", Type(),
-                      Size().ToString().Ascii().data());
+                      Size().ToString().Ascii().c_str());
   switch (Type()) {
     case kFragmentBox:
     case kFragmentRenderedLegend:
       output.AppendFormat(", BoxType: '%s'",
-                          StringForBoxType(*this).Ascii().data());
+                          StringForBoxType(*this).Ascii().c_str());
       break;
     case kFragmentText: {
       const auto& text = To<NGPhysicalTextFragment>(*this);
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_block.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_block.cc
index 934918e..3d6fea79 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_block.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_block.cc
@@ -131,7 +131,7 @@
 
 bool LayoutSVGBlock::NodeAtPoint(HitTestResult&,
                                  const HitTestLocation&,
-                                 const LayoutPoint&,
+                                 const PhysicalOffset&,
                                  HitTestAction) {
   NOTREACHED();
   return false;
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_block.h b/third_party/blink/renderer/core/layout/svg/layout_svg_block.h
index 739c40a..2297aa1 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_block.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_block.h
@@ -80,7 +80,7 @@
 
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) override;
 
   // The inherited version doesn't check for SVG effects.
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc
index fed5ecf..fdec0f16 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc
@@ -177,9 +177,9 @@
 bool LayoutSVGContainer::NodeAtPoint(
     HitTestResult& result,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& accumulated_offset,
+    const PhysicalOffset& accumulated_offset,
     HitTestAction hit_test_action) {
-  DCHECK_EQ(accumulated_offset, LayoutPoint());
+  DCHECK_EQ(accumulated_offset, PhysicalOffset());
   TransformedHitTestLocation local_location(location_in_container,
                                             LocalToSVGParentTransform());
   if (!local_location)
@@ -199,9 +199,8 @@
     // containers.
     if (IsObjectBoundingBoxValid() &&
         local_location->Intersects(ObjectBoundingBox())) {
-      const LayoutPoint& local_layout_point =
-          LayoutPoint(local_location->TransformedPoint());
-      UpdateHitTestResult(result, local_layout_point);
+      UpdateHitTestResult(result, PhysicalOffset::FromFloatPointRound(
+                                      local_location->TransformedPoint()));
       if (result.AddNodeToListBasedTestResult(GetElement(), *local_location) ==
           kStopHitTesting)
         return true;
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_container.h b/third_party/blink/renderer/core/layout/svg/layout_svg_container.h
index a12b7cf..13f46a3 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_container.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_container.h
@@ -84,7 +84,7 @@
 
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) override;
 
   // Called during layout to update the local transform.
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc
index cd3401b..a6da9a007 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc
@@ -128,9 +128,9 @@
 bool LayoutSVGForeignObject::NodeAtPointFromSVG(
     HitTestResult& result,
     const HitTestLocation& location_in_parent,
-    const LayoutPoint& accumulated_offset,
+    const PhysicalOffset& accumulated_offset,
     HitTestAction) {
-  DCHECK_EQ(accumulated_offset, LayoutPoint());
+  DCHECK_EQ(accumulated_offset, PhysicalOffset());
   TransformedHitTestLocation local_location(location_in_parent,
                                             LocalSVGTransform());
   if (!local_location)
@@ -138,11 +138,10 @@
 
   // |local_location| already includes the offset of the <foreignObject>
   // element, but PaintLayer::HitTestLayer assumes it has not been.
-  HitTestLocation local_without_offset(
-      *local_location, -ToLayoutSize(Layer()->LayoutBoxLocation()));
+  HitTestLocation local_without_offset(*local_location, -PhysicalLocation());
   HitTestResult layer_result(result.GetHitTestRequest(), local_without_offset);
   bool retval = Layer()->HitTest(local_without_offset, layer_result,
-                                 LayoutRect(LayoutRect::InfiniteIntRect()));
+                                 PhysicalRect(PhysicalRect::InfiniteIntRect()));
 
   // Preserve the "point in inner node frame" from the original request,
   // since |layer_result| is a hit test rooted at the <foreignObject> element,
@@ -150,7 +149,7 @@
   // |point_in_foreign_object| as its "point in inner node frame".
   // TODO(chrishtr): refactor the PaintLayer and HitTestResults code around
   // this, to better support hit tests that don't start at frame boundaries.
-  LayoutPoint original_point_in_inner_node_frame =
+  PhysicalOffset original_point_in_inner_node_frame =
       result.PointInInnerNodeFrame();
   if (result.GetHitTestRequest().ListBased())
     result.Append(layer_result);
@@ -163,7 +162,7 @@
 bool LayoutSVGForeignObject::NodeAtPoint(
     HitTestResult& result,
     const HitTestLocation& location_in_parent,
-    const LayoutPoint& accumulated_offset,
+    const PhysicalOffset& accumulated_offset,
     HitTestAction hit_test_action) {
   // Skip LayoutSVGBlock's override.
   return LayoutBlockFlow::NodeAtPoint(result, location_in_parent,
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h
index 6d11b42..838ac43a 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h
@@ -63,7 +63,7 @@
 
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation&,
-                   const LayoutPoint&,
+                   const PhysicalOffset&,
                    HitTestAction) override;
 
   // A method to call when recursively hit testing from an SVG parent.
@@ -72,7 +72,7 @@
   // on this object. This is why there are two methods.
   bool NodeAtPointFromSVG(HitTestResult&,
                           const HitTestLocation&,
-                          const LayoutPoint&,
+                          const PhysicalOffset&,
                           HitTestAction);
 
   bool IsOfType(LayoutObjectType type) const override {
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc
index f680a1ba..b804814 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc
@@ -64,7 +64,7 @@
   EXPECT_EQ(svg, HitTest(450, 350));
 
   // Rect based hit testing
-  auto results = RectBasedHitTest(LayoutRect(0, 0, 300, 300));
+  auto results = RectBasedHitTest(PhysicalRect(0, 0, 300, 300));
   int count = 0;
   EXPECT_EQ(3u, results.size());
   for (auto result : results) {
@@ -138,7 +138,7 @@
   EXPECT_EQ(svg, HitTest(450, 400));
 
   // Rect based hit testing
-  auto results = RectBasedHitTest(LayoutRect(0, 0, 300, 300));
+  auto results = RectBasedHitTest(PhysicalRect(0, 0, 300, 300));
   int count = 0;
   EXPECT_EQ(7u, results.size());
   for (auto result : results) {
@@ -194,7 +194,7 @@
   EXPECT_EQ(div, HitTest(290, 290));
 
   // Rect based hit testing
-  auto results = RectBasedHitTest(LayoutRect(0, 0, 300, 300));
+  auto results = RectBasedHitTest(PhysicalRect(0, 0, 300, 300));
   int count = 0;
   EXPECT_EQ(3u, results.size());
   for (auto result : results) {
@@ -298,11 +298,11 @@
   EXPECT_EQ(foreignObject, GetDocument().ElementFromPoint(205, 255));
 
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestLocation location((LayoutPoint(206, 206)));
+  HitTestLocation location((PhysicalOffset(206, 206)));
   HitTestResult result(request, location);
   GetDocument().GetLayoutView()->HitTest(location, result);
   EXPECT_EQ(target, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(206, 206), result.PointInInnerNodeFrame());
+  EXPECT_EQ(PhysicalOffset(206, 206), result.PointInInnerNodeFrame());
 }
 
 TEST_F(LayoutSVGForeignObjectTest,
@@ -332,11 +332,11 @@
   EXPECT_EQ(foreign_object, GetDocument().ElementFromPoint(235, 255));
 
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestLocation location((LayoutPoint(236, 206)));
+  HitTestLocation location((PhysicalOffset(236, 206)));
   HitTestResult result(request, location);
   GetDocument().GetLayoutView()->HitTest(location, result);
   EXPECT_EQ(target, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(236, 206), result.PointInInnerNodeFrame());
+  EXPECT_EQ(PhysicalOffset(236, 206), result.PointInInnerNodeFrame());
 }
 
 TEST_F(LayoutSVGForeignObjectTest, HitTestUnderScrollingAncestor) {
@@ -362,11 +362,11 @@
   EXPECT_EQ(target, GetDocument().ElementFromPoint(450, 450));
 
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestLocation location((LayoutPoint(450, 450)));
+  HitTestLocation location((PhysicalOffset(450, 450)));
   HitTestResult result(request, location);
   GetDocument().GetLayoutView()->HitTest(location, result);
   EXPECT_EQ(target, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(450, 450), result.PointInInnerNodeFrame());
+  EXPECT_EQ(PhysicalOffset(450, 450), result.PointInInnerNodeFrame());
 
   scroller.setScrollTop(3000);
 
@@ -374,7 +374,7 @@
 
   GetDocument().GetLayoutView()->HitTest(location, result);
   EXPECT_EQ(target, result.InnerNode());
-  EXPECT_EQ(LayoutPoint(450, 450), result.PointInInnerNodeFrame());
+  EXPECT_EQ(PhysicalOffset(450, 450), result.PointInInnerNodeFrame());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_hidden_container.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_hidden_container.cc
index 40afa1ffd..c2c57a7 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_hidden_container.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_hidden_container.cc
@@ -45,7 +45,7 @@
 
 bool LayoutSVGHiddenContainer::NodeAtPoint(HitTestResult&,
                                            const HitTestLocation&,
-                                           const LayoutPoint&,
+                                           const PhysicalOffset&,
                                            HitTestAction) {
   return false;
 }
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_hidden_container.h b/third_party/blink/renderer/core/layout/svg/layout_svg_hidden_container.h
index 4d2bc9bc..ad8d49f 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_hidden_container.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_hidden_container.h
@@ -59,7 +59,7 @@
 
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) final;
 };
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
index 5fd87cc0..fd21b64 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
@@ -176,9 +176,9 @@
 
 bool LayoutSVGImage::NodeAtPoint(HitTestResult& result,
                                  const HitTestLocation& location_in_container,
-                                 const LayoutPoint& accumulated_offset,
+                                 const PhysicalOffset& accumulated_offset,
                                  HitTestAction hit_test_action) {
-  DCHECK_EQ(accumulated_offset, LayoutPoint());
+  DCHECK_EQ(accumulated_offset, PhysicalOffset());
   // We only draw in the forground phase, so we only hit-test then.
   if (hit_test_action != kHitTestForeground)
     return false;
@@ -200,9 +200,8 @@
 
   if (hit_rules.can_hit_fill || hit_rules.can_hit_bounding_box) {
     if (local_location->Intersects(object_bounding_box_)) {
-      const LayoutPoint& local_layout_point =
-          LayoutPoint(local_location->TransformedPoint());
-      UpdateHitTestResult(result, local_layout_point);
+      UpdateHitTestResult(result, PhysicalOffset::FromFloatPointRound(
+                                      local_location->TransformedPoint()));
       if (result.AddNodeToListBasedTestResult(GetElement(), *local_location) ==
           kStopHitTesting)
         return true;
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_image.h b/third_party/blink/renderer/core/layout/svg/layout_svg_image.h
index 863c6ff..a8d0ba62 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_image.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_image.h
@@ -71,7 +71,7 @@
 
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_parent,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) override;
 
   AffineTransform LocalSVGTransform() const override {
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
index f1797c6..fcbd0e1 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
@@ -257,7 +257,7 @@
     DCHECK(!layout_object->IsBoxModelObject() ||
            !ToLayoutBoxModelObject(layout_object)->HasSelfPaintingLayer());
 
-    if (layout_object->NodeAtPoint(result, *local_location, LayoutPoint(),
+    if (layout_object->NodeAtPoint(result, *local_location, PhysicalOffset(),
                                    kHitTestForeground))
       return true;
   }
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
index 6716313..b83ddd9 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
@@ -465,12 +465,12 @@
 
 bool LayoutSVGRoot::NodeAtPoint(HitTestResult& result,
                                 const HitTestLocation& location_in_container,
-                                const LayoutPoint& accumulated_offset,
+                                const PhysicalOffset& accumulated_offset,
                                 HitTestAction hit_test_action) {
-  LayoutPoint adjusted_location = accumulated_offset + Location();
+  PhysicalOffset adjusted_location = accumulated_offset + PhysicalLocation();
 
   HitTestLocation local_border_box_location(location_in_container,
-                                            ToLayoutSize(-adjusted_location));
+                                            -adjusted_location);
 
   // Only test SVG content if the point is in our content box, or in case we
   // don't clip to the viewport, the visual overflow rect.
@@ -478,14 +478,13 @@
   // supported by nodeAtFloatPoint.
   bool skip_children = (result.GetHitTestRequest().GetStopNode() == this);
   if (!skip_children &&
-      (local_border_box_location.Intersects(
-           PhysicalContentBoxRect().ToLayoutRect()) ||
+      (local_border_box_location.Intersects(PhysicalContentBoxRect()) ||
        (!ShouldApplyViewportClip() &&
-        local_border_box_location.Intersects(VisualOverflowRect())))) {
+        local_border_box_location.Intersects(PhysicalVisualOverflowRect())))) {
     TransformedHitTestLocation local_location(local_border_box_location,
                                               LocalToBorderBoxTransform());
     if (local_location) {
-      LayoutPoint accumulated_offset_for_children;
+      PhysicalOffset accumulated_offset_for_children;
       if (SVGLayoutSupport::HitTestChildren(
               LastChild(), result, *local_location,
               accumulated_offset_for_children, hit_test_action))
@@ -506,7 +505,7 @@
     // detect hits on the background of a <div> element.
     // If we'd return true here in the 'Foreground' phase, we are not able to
     // detect these hits anymore.
-    LayoutRect bounds_rect(accumulated_offset + Location(), Size());
+    PhysicalRect bounds_rect(accumulated_offset + PhysicalLocation(), Size());
     if (location_in_container.Intersects(bounds_rect)) {
       UpdateHitTestResult(result, local_border_box_location.Point());
       if (result.AddNodeToListBasedTestResult(GetNode(), location_in_container,
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_root.h b/third_party/blink/renderer/core/layout/svg/layout_svg_root.h
index ff3fc5a5..c1306d7 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_root.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_root.h
@@ -139,7 +139,7 @@
 
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) override;
 
   void MapLocalToAncestor(const LayoutBoxModelObject* ancestor,
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc
index b6f99da..9e21398 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc
@@ -143,7 +143,7 @@
 
   // The center of this rect does not overlap the SVG element, but the
   // rect itself does.
-  auto results = RectBasedHitTest(LayoutRect(0, 0, 300, 300));
+  auto results = RectBasedHitTest(PhysicalRect(0, 0, 300, 300));
   int count = 0;
   EXPECT_EQ(2u, results.size());
   for (auto result : results) {
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc
index 11d4e5d..21f5f2c 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc
@@ -350,9 +350,9 @@
 
 bool LayoutSVGShape::NodeAtPoint(HitTestResult& result,
                                  const HitTestLocation& location_in_parent,
-                                 const LayoutPoint& accumulated_offset,
+                                 const PhysicalOffset& accumulated_offset,
                                  HitTestAction hit_test_action) {
-  DCHECK_EQ(accumulated_offset, LayoutPoint());
+  DCHECK_EQ(accumulated_offset, PhysicalOffset());
   // We only draw in the foreground phase, so we only hit-test then.
   if (hit_test_action != kHitTestForeground)
     return false;
@@ -374,8 +374,8 @@
     return false;
 
   if (HitTestShape(result.GetHitTestRequest(), *local_location, hit_rules)) {
-    const LayoutPoint local_layout_point(local_location->TransformedPoint());
-    UpdateHitTestResult(result, local_layout_point);
+    UpdateHitTestResult(result, PhysicalOffset::FromFloatPointRound(
+                                    local_location->TransformedPoint()));
     if (result.AddNodeToListBasedTestResult(GetElement(), *local_location) ==
         kStopHitTesting)
       return true;
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_shape.h b/third_party/blink/renderer/core/layout/svg/layout_svg_shape.h
index b105d77..c944680 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_shape.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_shape.h
@@ -161,7 +161,7 @@
 
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_parent,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) final;
   bool HitTestShape(const HitTestRequest&,
                     const HitTestLocation&,
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
index 334d7644..0284720 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
@@ -318,9 +318,9 @@
 
 bool LayoutSVGText::NodeAtPoint(HitTestResult& result,
                                 const HitTestLocation& location_in_parent,
-                                const LayoutPoint& accumulated_offset,
+                                const PhysicalOffset& accumulated_offset,
                                 HitTestAction hit_test_action) {
-  DCHECK_EQ(accumulated_offset, LayoutPoint());
+  DCHECK_EQ(accumulated_offset, PhysicalOffset());
   // We only draw in the foreground phase, so we only hit-test then.
   if (hit_test_action != kHitTestForeground)
     return false;
@@ -341,9 +341,8 @@
   if (StyleRef().PointerEvents() == EPointerEvents::kBoundingBox) {
     if (IsObjectBoundingBoxValid() &&
         local_location->Intersects(ObjectBoundingBox())) {
-      const LayoutPoint& local_layout_point =
-          LayoutPoint(local_location->TransformedPoint());
-      UpdateHitTestResult(result, local_layout_point);
+      UpdateHitTestResult(result, PhysicalOffset::FromFloatPointRound(
+                                      local_location->TransformedPoint()));
       if (result.AddNodeToListBasedTestResult(GetElement(), *local_location) ==
           kStopHitTesting)
         return true;
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_text.h b/third_party/blink/renderer/core/layout/svg/layout_svg_text.h
index 852d90e20..9a68bfb 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_text.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_text.h
@@ -76,7 +76,7 @@
   void Paint(const PaintInfo&) const override;
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_parent,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) override;
   PositionWithAffinity PositionForPoint(const LayoutPoint&) const override;
 
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_text_test.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_text_test.cc
index e295cb8..a177ec70 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_text_test.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_text_test.cc
@@ -28,7 +28,7 @@
   const auto& text = *GetDocument().getElementById("text")->firstChild();
 
   // Rect based hit testing
-  auto results = RectBasedHitTest(LayoutRect(0, 0, 300, 300));
+  auto results = RectBasedHitTest(PhysicalRect(0, 0, 300, 300));
   int count = 0;
   EXPECT_EQ(2u, results.size());
   for (auto result : results) {
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.cc
index 99fd0f0..000a274e 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.cc
@@ -78,7 +78,7 @@
 bool LayoutSVGViewportContainer::NodeAtPoint(
     HitTestResult& result,
     const HitTestLocation& location_in_parent,
-    const LayoutPoint& accumulated_offset,
+    const PhysicalOffset& accumulated_offset,
     HitTestAction action) {
   // Respect the viewport clip which is in parent coordinates.
   if (SVGLayoutSupport::IsOverflowHidden(*this)) {
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.h b/third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.h
index 40bc375..6116679a 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.h
@@ -58,7 +58,7 @@
 
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_parent,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    HitTestAction) final;
 
   void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
diff --git a/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.cc b/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.cc
index a01d167..66cef56 100644
--- a/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.cc
+++ b/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.cc
@@ -297,7 +297,7 @@
 
 bool SVGInlineTextBox::NodeAtPoint(HitTestResult& result,
                                    const HitTestLocation& location_in_container,
-                                   const LayoutPoint& accumulated_offset,
+                                   const PhysicalOffset& accumulated_offset,
                                    LayoutUnit,
                                    LayoutUnit) {
   // FIXME: integrate with InlineTextBox::nodeAtPoint better.
@@ -315,13 +315,13 @@
        (style.SvgStyle().HasStroke() || !hit_rules.require_stroke)) ||
       (hit_rules.can_hit_fill &&
        (style.SvgStyle().HasFill() || !hit_rules.require_fill))) {
-    LayoutRect rect(Location(), Size());
-    rect.MoveBy(accumulated_offset);
+    // Currently SVGInlineTextBox doesn't flip in blocks direction.
+    PhysicalRect rect{PhysicalOffset(Location()), PhysicalSize(Size())};
+    rect.Move(accumulated_offset);
     if (location_in_container.Intersects(rect)) {
       if (HitTestFragments(location_in_container)) {
         line_layout_item.UpdateHitTestResult(
-            result,
-            location_in_container.Point() - ToLayoutSize(accumulated_offset));
+            result, location_in_container.Point() - accumulated_offset);
         if (result.AddNodeToListBasedTestResult(line_layout_item.GetNode(),
                                                 location_in_container,
                                                 rect) == kStopHitTesting)
diff --git a/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.h b/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.h
index 320ea2c6..f61d9b3 100644
--- a/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.h
+++ b/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.h
@@ -100,7 +100,7 @@
   bool HitTestFragments(const HitTestLocation& location_in_container) const;
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    LayoutUnit line_top,
                    LayoutUnit line_bottom) override;
 
diff --git a/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.cc b/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.cc
index 7e61e2b2..926a0f5 100644
--- a/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.cc
+++ b/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.cc
@@ -196,7 +196,7 @@
 
 bool SVGRootInlineBox::NodeAtPoint(HitTestResult& result,
                                    const HitTestLocation& location_in_container,
-                                   const LayoutPoint& accumulated_offset,
+                                   const PhysicalOffset& accumulated_offset,
                                    LayoutUnit line_top,
                                    LayoutUnit line_bottom) {
   // Iterate the text boxes in reverse so that the top-most node will be considered first.
diff --git a/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.h b/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.h
index d5a7e3dd..addb9bc 100644
--- a/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.h
+++ b/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.h
@@ -49,7 +49,7 @@
 
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& accumulated_offset,
+                   const PhysicalOffset& accumulated_offset,
                    LayoutUnit line_top,
                    LayoutUnit line_bottom) final;
 
diff --git a/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc b/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc
index 22bb64f..40000a9c 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc
+++ b/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc
@@ -438,7 +438,7 @@
 bool SVGLayoutSupport::HitTestChildren(LayoutObject* last_child,
                                        HitTestResult& result,
                                        const HitTestLocation& location,
-                                       const LayoutPoint& accumulated_offset,
+                                       const PhysicalOffset& accumulated_offset,
                                        HitTestAction hit_test_action) {
   for (LayoutObject* child = last_child; child;
        child = child->PreviousSibling()) {
diff --git a/third_party/blink/renderer/core/layout/svg/svg_layout_support.h b/third_party/blink/renderer/core/layout/svg/svg_layout_support.h
index ddd5082..3a7921d 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_layout_support.h
+++ b/third_party/blink/renderer/core/layout/svg/svg_layout_support.h
@@ -83,7 +83,7 @@
   static bool HitTestChildren(LayoutObject* last_child,
                               HitTestResult&,
                               const HitTestLocation&,
-                              const LayoutPoint& accumulated_offset,
+                              const PhysicalOffset& accumulated_offset,
                               HitTestAction);
 
   static void ComputeContainerBoundingBoxes(const LayoutObject* container,
diff --git a/third_party/blink/renderer/core/loader/previews_resource_loading_hints.cc b/third_party/blink/renderer/core/loader/previews_resource_loading_hints.cc
index 5d6b963..ab75eff 100644
--- a/third_party/blink/renderer/core/loader/previews_resource_loading_hints.cc
+++ b/third_party/blink/renderer/core/loader/previews_resource_loading_hints.cc
@@ -64,7 +64,7 @@
     // should be either "true" or "false".
     block_resource_type_[i] = base::GetFieldTrialParamByFeatureAsBool(
         features::kPreviewsResourceLoadingHintsSpecificResourceTypes,
-        String::Format("block_resource_type_%d", i).Ascii().data(),
+        String::Format("block_resource_type_%d", i).Ascii(),
         block_resource_type_[i]);
   }
 
diff --git a/third_party/blink/renderer/core/page/autoscroll_controller.cc b/third_party/blink/renderer/core/page/autoscroll_controller.cc
index 9e59ff8..5296c51 100644
--- a/third_party/blink/renderer/core/page/autoscroll_controller.cc
+++ b/third_party/blink/renderer/core/page/autoscroll_controller.cc
@@ -48,7 +48,9 @@
 
 static const int kNoMiddleClickAutoscrollRadius = 15;
 
-static const Cursor& MiddleClickAutoscrollCursor(const FloatSize& velocity) {
+static const Cursor& MiddleClickAutoscrollCursor(const FloatSize& velocity,
+                                                 bool scroll_vert,
+                                                 bool scroll_horiz) {
   // At the original click location we draw a 4 arrowed icon. Over this icon
   // there won't be any scroll, So don't change the cursor over this area.
   bool east = velocity.Width() < 0;
@@ -56,24 +58,32 @@
   bool north = velocity.Height() > 0;
   bool south = velocity.Height() < 0;
 
-  if (north) {
-    if (east)
-      return NorthEastPanningCursor();
-    if (west)
-      return NorthWestPanningCursor();
+  if (north && scroll_vert) {
+    if (scroll_horiz) {
+      if (east)
+        return NorthEastPanningCursor();
+      if (west)
+        return NorthWestPanningCursor();
+    }
     return NorthPanningCursor();
   }
-  if (south) {
-    if (east)
-      return SouthEastPanningCursor();
-    if (west)
-      return SouthWestPanningCursor();
+  if (south && scroll_vert) {
+    if (scroll_horiz) {
+      if (east)
+        return SouthEastPanningCursor();
+      if (west)
+        return SouthWestPanningCursor();
+    }
     return SouthPanningCursor();
   }
-  if (east)
+  if (east && scroll_horiz)
     return EastPanningCursor();
-  if (west)
+  if (west && scroll_horiz)
     return WestPanningCursor();
+  if (scroll_vert && !scroll_horiz)
+    return MiddlePanningVerticalCursor();
+  if (scroll_horiz && !scroll_vert)
+    return MiddlePanningHorizontalCursor();
   return MiddlePanningCursor();
 }
 
@@ -185,15 +195,15 @@
     return;
   }
 
-  LayoutSize offset =
-      scrollable->CalculateAutoscrollDirection(event_position).ToLayoutSize();
+  PhysicalOffset offset =
+      scrollable->CalculateAutoscrollDirection(event_position);
   if (offset.IsZero()) {
     StopAutoscroll();
     return;
   }
 
   drag_and_drop_autoscroll_reference_position_ =
-      LayoutPoint(event_position) + offset;
+      PhysicalOffset::FromFloatPointRound(event_position) + offset;
 
   if (autoscroll_type_ == kNoAutoscroll) {
     autoscroll_type_ = kAutoscrollForDragAndDrop;
@@ -241,7 +251,8 @@
     if (middle_click_mode_ == kMiddleClickInitial)
       middle_click_mode_ = kMiddleClickHolding;
     page_->GetChromeClient().SetCursorOverridden(false);
-    view->SetCursor(MiddleClickAutoscrollCursor(velocity));
+    view->SetCursor(MiddleClickAutoscrollCursor(
+        velocity, can_scroll_vertically_, can_scroll_horizontally_));
     page_->GetChromeClient().SetCursorOverridden(true);
     page_->GetChromeClient().AutoscrollFling(velocity, frame);
   }
@@ -280,7 +291,9 @@
 void AutoscrollController::StartMiddleClickAutoscroll(
     LocalFrame* frame,
     const FloatPoint& position,
-    const FloatPoint& position_global) {
+    const FloatPoint& position_global,
+    bool scroll_vert,
+    bool scroll_horiz) {
   DCHECK(RuntimeEnabledFeatures::MiddleClickAutoscrollEnabled());
   // We don't want to trigger the autoscroll or the middleClickAutoscroll if
   // it's already active.
@@ -290,14 +303,18 @@
   autoscroll_type_ = kAutoscrollForMiddleClick;
   middle_click_mode_ = kMiddleClickInitial;
   middle_click_autoscroll_start_pos_global_ = position_global;
+  can_scroll_vertically_ = scroll_vert;
+  can_scroll_horizontally_ = scroll_horiz;
 
   UseCounter::Count(frame->GetDocument(),
                     WebFeature::kMiddleClickAutoscrollStart);
 
   last_velocity_ = FloatSize();
 
-  if (LocalFrameView* view = frame->View())
-    view->SetCursor(MiddleClickAutoscrollCursor(last_velocity_));
+  if (LocalFrameView* view = frame->View()) {
+    view->SetCursor(MiddleClickAutoscrollCursor(
+        last_velocity_, can_scroll_vertically_, can_scroll_horizontally_));
+  }
   page_->GetChromeClient().SetCursorOverridden(true);
   page_->GetChromeClient().AutoscrollStart(
       position.ScaledBy(1 / frame->DevicePixelRatio()), frame);
@@ -315,12 +332,13 @@
 
   EventHandler& event_handler =
       autoscroll_layout_object_->GetFrame()->GetEventHandler();
-  LayoutSize offset = autoscroll_layout_object_
-                          ->CalculateAutoscrollDirection(
-                              event_handler.LastKnownMousePositionInRootFrame())
-                          .ToLayoutSize();
-  LayoutPoint selection_point =
-      LayoutPoint(event_handler.LastKnownMousePositionInRootFrame()) + offset;
+  PhysicalOffset offset =
+      autoscroll_layout_object_->CalculateAutoscrollDirection(
+          event_handler.LastKnownMousePositionInRootFrame());
+  PhysicalOffset selection_point =
+      PhysicalOffset::FromFloatPointRound(
+          event_handler.LastKnownMousePositionInRootFrame()) +
+      offset;
   switch (autoscroll_type_) {
     case kAutoscrollForDragAndDrop:
       ScheduleMainThreadAnimation();
diff --git a/third_party/blink/renderer/core/page/autoscroll_controller.h b/third_party/blink/renderer/core/page/autoscroll_controller.h
index 3cf3249..77752f54 100644
--- a/third_party/blink/renderer/core/page/autoscroll_controller.h
+++ b/third_party/blink/renderer/core/page/autoscroll_controller.h
@@ -28,9 +28,9 @@
 
 #include "base/gtest_prod_util.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
 #include "third_party/blink/renderer/platform/geometry/float_point.h"
 #include "third_party/blink/renderer/platform/geometry/float_size.h"
-#include "third_party/blink/renderer/platform/geometry/layout_point.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
 
@@ -86,7 +86,9 @@
   // Middle-click autoscroll.
   void StartMiddleClickAutoscroll(LocalFrame*,
                                   const FloatPoint& position,
-                                  const FloatPoint& position_global);
+                                  const FloatPoint& position_global,
+                                  bool scroll_vert,
+                                  bool scroll_horiz);
   void HandleMouseMoveForMiddleClickAutoscroll(
       LocalFrame*,
       const FloatPoint& position_global,
@@ -107,13 +109,15 @@
   void ScheduleMainThreadAnimation();
   LayoutBox* autoscroll_layout_object_ = nullptr;
   LayoutBox* pressed_layout_object_ = nullptr;
-  LayoutPoint drag_and_drop_autoscroll_reference_position_;
+  PhysicalOffset drag_and_drop_autoscroll_reference_position_;
   TimeTicks drag_and_drop_autoscroll_start_time_;
 
   // Middle-click autoscroll.
   FloatPoint middle_click_autoscroll_start_pos_global_;
   FloatSize last_velocity_;
   MiddleClickMode middle_click_mode_ = kMiddleClickInitial;
+  bool can_scroll_vertically_ = false;
+  bool can_scroll_horizontally_ = false;
 
   FRIEND_TEST_ALL_PREFIXES(AutoscrollControllerTest,
                            CrashWhenLayoutStopAnimationBeforeScheduleAnimation);
diff --git a/third_party/blink/renderer/core/page/autoscroll_controller_test.cc b/third_party/blink/renderer/core/page/autoscroll_controller_test.cc
index a55cd6a..f73bdc2 100644
--- a/third_party/blink/renderer/core/page/autoscroll_controller_test.cc
+++ b/third_party/blink/renderer/core/page/autoscroll_controller_test.cc
@@ -85,7 +85,8 @@
 
   EXPECT_FALSE(controller.IsAutoscrolling());
 
-  controller.StartMiddleClickAutoscroll(frame, FloatPoint(), FloatPoint());
+  controller.StartMiddleClickAutoscroll(frame, FloatPoint(), FloatPoint(),
+                                        false, false);
 
   EXPECT_TRUE(controller.IsAutoscrolling());
 
diff --git a/third_party/blink/renderer/core/page/chrome_client.h b/third_party/blink/renderer/core/page/chrome_client.h
index bd67d49..53baa47 100644
--- a/third_party/blink/renderer/core/page/chrome_client.h
+++ b/third_party/blink/renderer/core/page/chrome_client.h
@@ -42,6 +42,7 @@
 #include "third_party/blink/renderer/core/frame/sandbox_flags.h"
 #include "third_party/blink/renderer/core/html/forms/external_date_time_chooser.h"
 #include "third_party/blink/renderer/core/html/forms/popup_menu.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
 #include "third_party/blink/renderer/core/loader/frame_loader.h"
 #include "third_party/blink/renderer/core/loader/navigation_policy.h"
 #include "third_party/blink/renderer/core/scroll/scroll_types.h"
@@ -471,7 +472,7 @@
   void SetToolTip(LocalFrame&, const HitTestLocation&, const HitTestResult&);
 
   WeakMember<Node> last_mouse_over_node_;
-  LayoutPoint last_tool_tip_point_;
+  PhysicalOffset last_tool_tip_point_;
   String last_tool_tip_text_;
 
   FRIEND_TEST_ALL_PREFIXES(ChromeClientTest, SetToolTipFlood);
diff --git a/third_party/blink/renderer/core/page/chrome_client_test.cc b/third_party/blink/renderer/core/page/chrome_client_test.cc
index 250db63..360f45f 100644
--- a/third_party/blink/renderer/core/page/chrome_client_test.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_test.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
 #include "third_party/blink/renderer/core/loader/empty_clients.h"
+#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 
 namespace blink {
@@ -40,7 +41,7 @@
 TEST_F(ChromeClientTest, SetToolTipFlood) {
   ChromeClientToolTipLogger logger;
   ChromeClient* client = &logger;
-  HitTestLocation location(LayoutPoint(10, 20));
+  HitTestLocation location(PhysicalOffset(10, 20));
   HitTestResult result(HitTestRequest(HitTestRequest::kMove), location);
   auto* doc = MakeGarbageCollected<Document>();
   auto* element = MakeGarbageCollected<HTMLElement>(html_names::kDivTag, *doc);
@@ -73,7 +74,7 @@
 
 TEST_F(ChromeClientTest, SetToolTipEmptyString) {
   ChromeClient* client = MakeGarbageCollected<EmptyChromeClient>();
-  HitTestLocation location(LayoutPoint(10, 20));
+  HitTestLocation location(PhysicalOffset(10, 20));
   HitTestResult result(HitTestRequest(HitTestRequest::kMove), location);
   auto& doc = *MakeGarbageCollected<Document>();
   auto& input_element =
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.cc b/third_party/blink/renderer/core/page/context_menu_controller.cc
index 0d69fdd..de4ba86 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller.cc
@@ -96,7 +96,8 @@
 void ContextMenuController::HandleContextMenuEvent(MouseEvent* mouse_event) {
   DCHECK(mouse_event->type() == event_type_names::kContextmenu);
   LocalFrame* frame = mouse_event->target()->ToNode()->GetDocument().GetFrame();
-  LayoutPoint location(mouse_event->AbsoluteLocation());
+  PhysicalOffset location = PhysicalOffset::FromFloatPointRound(
+      FloatPoint(mouse_event->AbsoluteLocation()));
   if (ShowContextMenu(frame, location, mouse_event->GetMenuSourceType()))
     mouse_event->SetDefaultHandled();
 }
@@ -107,7 +108,8 @@
     float y,
     ContextMenuProvider* menu_provider) {
   menu_provider_ = menu_provider;
-  if (!ShowContextMenu(frame, LayoutPoint(x, y), kMenuSourceNone))
+  if (!ShowContextMenu(frame, PhysicalOffset(LayoutUnit(x), LayoutUnit(y)),
+                       kMenuSourceNone))
     ClearContextMenu();
 }
 
@@ -200,7 +202,7 @@
 }
 
 bool ContextMenuController::ShowContextMenu(LocalFrame* frame,
-                                            const LayoutPoint& point,
+                                            const PhysicalOffset& point,
                                             WebMenuSourceType source_type) {
   // Displaying the context menu in this function is a big hack as we don't
   // have context, i.e. whether this is being invoked via a script or in
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.h b/third_party/blink/renderer/core/page/context_menu_controller.h
index 93f48a4..df28bc8 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.h
+++ b/third_party/blink/renderer/core/page/context_menu_controller.h
@@ -68,7 +68,7 @@
   friend class ContextMenuControllerTest;
 
   // Returns whether a Context Menu was actually shown.
-  bool ShowContextMenu(LocalFrame*, const LayoutPoint&, WebMenuSourceType);
+  bool ShowContextMenu(LocalFrame*, const PhysicalOffset&, WebMenuSourceType);
   bool ShouldShowContextMenuFromTouch(const WebContextMenuData&);
 
   Member<Page> page_;
diff --git a/third_party/blink/renderer/core/page/context_menu_controller_test.cc b/third_party/blink/renderer/core/page/context_menu_controller_test.cc
index 49a2c8d..1a2d7aa4b 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller_test.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller_test.cc
@@ -72,7 +72,8 @@
         WebWidget::LifecycleUpdateReason::kTest);
   }
 
-  bool ShowContextMenu(const LayoutPoint& location, WebMenuSourceType source) {
+  bool ShowContextMenu(const PhysicalOffset& location,
+                       WebMenuSourceType source) {
     return web_view_helper_.GetWebView()
         ->GetPage()
         ->GetContextMenuController()
@@ -125,8 +126,8 @@
       .WillRepeatedly(Return(false));
 
   DOMRect* rect = video->getBoundingClientRect();
-  LayoutPoint location((rect->left() + rect->right()) / 2,
-                       (rect->top() + rect->bottom()) / 2);
+  PhysicalOffset location(LayoutUnit((rect->left() + rect->right()) / 2),
+                          LayoutUnit((rect->top() + rect->bottom()) / 2));
   EXPECT_TRUE(ShowContextMenu(location, kMenuSourceMouse));
 
   // Context menu info are sent to the WebLocalFrameClient.
@@ -186,8 +187,8 @@
       .WillRepeatedly(Return(true));
 
   DOMRect* rect = video->getBoundingClientRect();
-  LayoutPoint location((rect->left() + rect->right()) / 2,
-                       (rect->top() + rect->bottom()) / 2);
+  PhysicalOffset location(LayoutUnit((rect->left() + rect->right()) / 2),
+                          LayoutUnit((rect->top() + rect->bottom()) / 2));
   EXPECT_TRUE(ShowContextMenu(location, kMenuSourceMouse));
 
   // Context menu info are sent to the WebLocalFrameClient.
@@ -243,8 +244,8 @@
       .WillRepeatedly(Return(true));
 
   DOMRect* rect = video->getBoundingClientRect();
-  LayoutPoint location((rect->left() + rect->right()) / 2,
-                       (rect->top() + rect->bottom()) / 2);
+  PhysicalOffset location(LayoutUnit((rect->left() + rect->right()) / 2),
+                          LayoutUnit((rect->top() + rect->bottom()) / 2));
   EXPECT_TRUE(ShowContextMenu(location, kMenuSourceMouse));
 
   // Context menu info are sent to the WebLocalFrameClient.
@@ -300,8 +301,8 @@
       .WillRepeatedly(Return(true));
 
   DOMRect* rect = video->getBoundingClientRect();
-  LayoutPoint location((rect->left() + rect->right()) / 2,
-                       (rect->top() + rect->bottom()) / 2);
+  PhysicalOffset location(LayoutUnit((rect->left() + rect->right()) / 2),
+                          LayoutUnit((rect->top() + rect->bottom()) / 2));
   EXPECT_TRUE(ShowContextMenu(location, kMenuSourceMouse));
 
   // Context menu info are sent to the WebLocalFrameClient.
@@ -359,8 +360,8 @@
       .WillRepeatedly(Return(true));
 
   DOMRect* rect = video->getBoundingClientRect();
-  LayoutPoint location((rect->left() + rect->right()) / 2,
-                       (rect->top() + rect->bottom()) / 2);
+  PhysicalOffset location(LayoutUnit((rect->left() + rect->right()) / 2),
+                          LayoutUnit((rect->top() + rect->bottom()) / 2));
   EXPECT_TRUE(ShowContextMenu(location, kMenuSourceMouse));
 
   // Context menu info are sent to the WebLocalFrameClient.
@@ -421,8 +422,8 @@
   DurationChanged(video.Get());
 
   DOMRect* rect = video->getBoundingClientRect();
-  LayoutPoint location((rect->left() + rect->right()) / 2,
-                       (rect->top() + rect->bottom()) / 2);
+  PhysicalOffset location(LayoutUnit((rect->left() + rect->right()) / 2),
+                          LayoutUnit((rect->top() + rect->bottom()) / 2));
   EXPECT_TRUE(ShowContextMenu(location, kMenuSourceMouse));
 
   // Context menu info are sent to the WebLocalFrameClient.
diff --git a/third_party/blink/renderer/core/page/drag_controller.cc b/third_party/blink/renderer/core/page/drag_controller.cc
index c3ffeee..42286ef 100644
--- a/third_party/blink/renderer/core/page/drag_controller.cc
+++ b/third_party/blink/renderer/core/page/drag_controller.cc
@@ -238,8 +238,8 @@
 
 void DragController::PerformDrag(DragData* drag_data, LocalFrame& local_root) {
   DCHECK(drag_data);
-  document_under_mouse_ =
-      local_root.DocumentAtPoint(LayoutPoint(drag_data->ClientPosition()));
+  document_under_mouse_ = local_root.DocumentAtPoint(
+      PhysicalOffset::FromFloatPointRound(drag_data->ClientPosition()));
   std::unique_ptr<UserGestureIndicator> gesture =
       LocalFrame::NotifyUserActivation(
           document_under_mouse_ ? document_under_mouse_->GetFrame() : nullptr,
@@ -261,7 +261,7 @@
         // When drop target is plugin element and it can process drag, we
         // should prevent default behavior.
         const HitTestLocation location(local_root.View()->ConvertFromRootFrame(
-            LayoutPoint(drag_data->ClientPosition())));
+            PhysicalOffset::FromFloatPointRound(drag_data->ClientPosition())));
         const HitTestResult result =
             event_handler.HitTestResultAtLocation(location);
         prevented_default |=
@@ -332,8 +332,8 @@
                                                    LocalFrame& local_root) {
   DCHECK(drag_data);
 
-  MouseMovedIntoDocument(
-      local_root.DocumentAtPoint(LayoutPoint(drag_data->ClientPosition())));
+  MouseMovedIntoDocument(local_root.DocumentAtPoint(
+      PhysicalOffset::FromFloatPointRound(drag_data->ClientPosition())));
 
   // TODO(esprehn): Replace acceptsLoadDrops with a Setting used in core.
   drag_destination_action_ =
@@ -363,7 +363,7 @@
 
 // This can return null if an empty document is loaded.
 static Element* ElementUnderMouse(Document* document_under_mouse,
-                                  const LayoutPoint& point) {
+                                  const PhysicalOffset& point) {
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
   HitTestLocation location(point);
   HitTestResult result(request, location);
@@ -416,8 +416,8 @@
 
   if ((action_mask & kDragDestinationActionEdit) &&
       CanProcessDrag(drag_data, local_root)) {
-    LayoutPoint point = frame_view->ConvertFromRootFrame(
-        LayoutPoint(drag_data->ClientPosition()));
+    PhysicalOffset point = frame_view->ConvertFromRootFrame(
+        PhysicalOffset::FromFloatPointRound(drag_data->ClientPosition()));
     Element* element = ElementUnderMouse(document_under_mouse_.Get(), point);
     if (!element)
       return false;
@@ -466,8 +466,8 @@
 DragOperation DragController::OperationForLoad(DragData* drag_data,
                                                LocalFrame& local_root) {
   DCHECK(drag_data);
-  Document* doc =
-      local_root.DocumentAtPoint(LayoutPoint(drag_data->ClientPosition()));
+  Document* doc = local_root.DocumentAtPoint(
+      PhysicalOffset::FromFloatPointRound(drag_data->ClientPosition()));
 
   if (doc &&
       (did_initiate_drag_ || doc->IsPluginDocument() || HasEditableStyle(*doc)))
@@ -481,7 +481,7 @@
 static bool SetSelectionToDragCaret(LocalFrame* frame,
                                     const SelectionInDOMTree& drag_caret,
                                     Range*& range,
-                                    const LayoutPoint& point) {
+                                    const PhysicalOffset& point) {
   frame->Selection().SetSelectionAndEndTyping(drag_caret);
   // TODO(editing-dev): The use of
   // UpdateStyleAndLayout
@@ -541,8 +541,8 @@
   if (!document_under_mouse_)
     return false;
 
-  LayoutPoint point = document_under_mouse_->View()->ConvertFromRootFrame(
-      LayoutPoint(drag_data->ClientPosition()));
+  PhysicalOffset point = document_under_mouse_->View()->ConvertFromRootFrame(
+      PhysicalOffset::FromFloatPointRound(drag_data->ClientPosition()));
   Element* element = ElementUnderMouse(document_under_mouse_.Get(), point);
   if (!element)
     return false;
@@ -708,8 +708,8 @@
   if (!local_root.ContentLayoutObject())
     return false;
 
-  LayoutPoint point = local_root.View()->ConvertFromRootFrame(
-      LayoutPoint(drag_data->ClientPosition()));
+  PhysicalOffset point = local_root.View()->ConvertFromRootFrame(
+      PhysicalOffset::FromFloatPointRound(drag_data->ClientPosition()));
 
   HitTestLocation location(point);
   HitTestResult result =
@@ -814,7 +814,7 @@
                                     const IntPoint& drag_origin,
                                     SelectionDragPolicy selection_drag_policy,
                                     DragSourceAction& drag_type) const {
-  if (src->Selection().Contains(drag_origin)) {
+  if (src->Selection().Contains(PhysicalOffset(drag_origin))) {
     drag_type = kDragSourceActionSelection;
     if (selection_drag_policy == kImmediateSelectionDragResolution)
       return start_node;
diff --git a/third_party/blink/renderer/core/page/event_with_hit_test_results.h b/third_party/blink/renderer/core/page/event_with_hit_test_results.h
index e31396a..839dbd9 100644
--- a/third_party/blink/renderer/core/page/event_with_hit_test_results.h
+++ b/third_party/blink/renderer/core/page/event_with_hit_test_results.h
@@ -43,7 +43,7 @@
 
   const EventType& Event() const { return event_; }
   const HitTestResult& GetHitTestResult() const { return hit_test_result_; }
-  LayoutPoint LocalPoint() const { return hit_test_result_.LocalPoint(); }
+  PhysicalOffset LocalPoint() const { return hit_test_result_.LocalPoint(); }
   Scrollbar* GetScrollbar() const { return hit_test_result_.GetScrollbar(); }
   bool IsOverLink() const { return hit_test_result_.IsOverLink(); }
   bool IsOverEmbeddedContentView() const {
diff --git a/third_party/blink/renderer/core/page/scrolling/scroll_into_view_test.cc b/third_party/blink/renderer/core/page/scrolling/scroll_into_view_test.cc
index 3ae7c50..e3f98e4 100644
--- a/third_party/blink/renderer/core/page/scrolling/scroll_into_view_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scroll_into_view_test.cc
@@ -589,7 +589,7 @@
       ScrollAlignment::kAlignLeftAlways, ScrollAlignment::kAlignTopAlways,
       kProgrammaticScroll, false, kScrollBehaviorInstant);
   params.stop_at_main_frame_layout_viewport = true;
-  target->ScrollRectToVisible(LayoutRect(target->AbsoluteBoundingBoxRect()),
+  target->ScrollRectToVisible(PhysicalRect(target->AbsoluteBoundingBoxRect()),
                               params);
 
   ScrollableArea* root_scroller =
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
index 3bb0a9a..505907e0 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
@@ -155,7 +155,7 @@
   if (first_match_needs_scroll_) {
     first_match_needs_scroll_ = false;
 
-    LayoutRect bounding_box = LayoutRect(ComputeTextRect(range));
+    PhysicalRect bounding_box(ComputeTextRect(range));
 
     // Set the bounding box height to zero because we want to center the top of
     // the text range.
@@ -167,7 +167,7 @@
 
     DCHECK(node.GetLayoutObject());
 
-    LayoutRect scrolled_bounding_box =
+    PhysicalRect scrolled_bounding_box =
         node.GetLayoutObject()->ScrollRectToVisible(
             bounding_box,
             WebScrollIntoViewParams(ScrollAlignment::kAlignCenterIfNeeded,
@@ -183,7 +183,7 @@
     // actually in document coordinates and therefore does not change with a
     // main document scroll.
     if (!frame_->View()->GetScrollableArea()->GetScrollOffset().IsZero() ||
-        scrolled_bounding_box.Location() != bounding_box.Location()) {
+        scrolled_bounding_box.offset != bounding_box.offset) {
       metrics_->DidNonZeroScroll();
     }
   }
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc
index ad10da3..2fe53ff 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc
@@ -397,9 +397,9 @@
 
 // Test matching a text range within the same element
 TEST_F(TextFragmentAnchorTest, SameElementTextRange) {
-  SimRequest request("https://example.com/test.html#targetText=this,page",
+  SimRequest request("https://example.com/test.html#targetText=This,page",
                      "text/html");
-  LoadURL("https://example.com/test.html#targetText=this,page");
+  LoadURL("https://example.com/test.html#targetText=This,page");
   request.Complete(R"HTML(
     <!DOCTYPE html>
     <style>
@@ -619,9 +619,9 @@
 // Test a text range with both context terms in the same element.
 TEST_F(TextFragmentAnchorTest, TextRangeWithContext) {
   SimRequest request(
-      "https://example.com/test.html#targetText=this-,is,test,-page",
+      "https://example.com/test.html#targetText=This-,is,test,-page",
       "text/html");
-  LoadURL("https://example.com/test.html#targetText=this-,is,test,-page");
+  LoadURL("https://example.com/test.html#targetText=This-,is,test,-page");
   request.Complete(R"HTML(
     <!DOCTYPE html>
     <p id="text">This is a test page</p>
@@ -661,9 +661,9 @@
 // Ensure that we do not match a text range if the suffix is not found.
 TEST_F(TextFragmentAnchorTest, SuffixNotFound) {
   SimRequest request(
-      "https://example.com/test.html#targetText=this-,is,test,-suffix",
+      "https://example.com/test.html#targetText=This-,is,test,-suffix",
       "text/html");
-  LoadURL("https://example.com/test.html#targetText=this-,is,test,-suffix");
+  LoadURL("https://example.com/test.html#targetText=This-,is,test,-suffix");
   request.Complete(R"HTML(
     <!DOCTYPE html>
     <p id="text">This is a test page</p>
@@ -678,11 +678,11 @@
 // Test a text range with context terms in different elements
 TEST_F(TextFragmentAnchorTest, TextRangeWithCrossElementContext) {
   SimRequest request(
-      "https://example.com/test.html#targetText=header%202-,a,text,-footer%201",
+      "https://example.com/test.html#targetText=Header%202-,A,text,-Footer%201",
       "text/html");
   LoadURL(
       "https://example.com/"
-      "test.html#targetText=header%202-,a,text,-footer%201");
+      "test.html#targetText=Header%202-,A,text,-Footer%201");
   request.Complete(R"HTML(
     <!DOCTYPE html>
     <h1>Header 1</h1>
@@ -714,11 +714,11 @@
 TEST_F(TextFragmentAnchorTest, CrossElementAndWhitespaceContext) {
   SimRequest request(
       "https://example.com/"
-      "test.html#targetText=list%202-,cat,-good%20cat",
+      "test.html#targetText=List%202-,Cat,-Good%20cat",
       "text/html");
   LoadURL(
       "https://example.com/"
-      "test.html#targetText=list%202-,cat,-good%20cat");
+      "test.html#targetText=List%202-,Cat,-Good%20cat");
   request.Complete(R"HTML(
     <!DOCTYPE html>
     <h1> List 1 </h1>
@@ -791,9 +791,9 @@
 // Ensure we scroll to text when its prefix and suffix are out of view.
 TEST_F(TextFragmentAnchorTest, DistantElementContext) {
   SimRequest request(
-      "https://example.com/test.html#targetText=prefix-,cats,-suffix",
+      "https://example.com/test.html#targetText=Prefix-,Cats,-Suffix",
       "text/html");
-  LoadURL("https://example.com/test.html#targetText=prefix-,cats,-suffix");
+  LoadURL("https://example.com/test.html#targetText=Prefix-,Cats,-Suffix");
   request.Complete(R"HTML(
     <!DOCTYPE html>
     <style>
@@ -1010,6 +1010,31 @@
   EXPECT_EQ(ScrollOffset(), LayoutViewport()->GetScrollOffset());
 }
 
+// Make sure matching is case sensitive.
+TEST_F(TextFragmentAnchorTest, CaseSensitive) {
+  SimRequest request("https://example.com/test.html#targetText=Test",
+                     "text/html");
+  LoadURL("https://example.com/test.html#targetText=Test");
+  request.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+      body {
+        height: 1200px;
+      }
+      p {
+        position: absolute;
+        top: 1000px;
+      }
+    </style>
+    <p id="text">test</p>
+  )HTML");
+  Compositor().BeginFrame();
+  RunAsyncMatchingTasks();
+
+  EXPECT_EQ(ScrollOffset(), LayoutViewport()->GetScrollOffset());
+  EXPECT_TRUE(GetDocument().Markers().Markers().IsEmpty());
+}
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.cc
index 864c63fc..93ef54e 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.cc
@@ -24,9 +24,9 @@
 EphemeralRangeInFlatTree FindMatchInRange(String search_text,
                                           PositionInFlatTree search_start,
                                           PositionInFlatTree search_end) {
-  const FindOptions find_options = kCaseInsensitive;
   const EphemeralRangeInFlatTree search_range(search_start, search_end);
-  return FindBuffer::FindMatchInRange(search_range, search_text, find_options);
+  return FindBuffer::FindMatchInRange(search_range, search_text,
+                                      /*find_options=*/0);
 }
 
 PositionInFlatTree NextTextPosition(PositionInFlatTree position,
@@ -56,10 +56,9 @@
     return EphemeralRangeInFlatTree();
 
   FindBuffer buffer(EphemeralRangeInFlatTree(search_start, search_end));
-  const FindOptions find_options = kCaseInsensitive;
 
   std::unique_ptr<FindBuffer::Results> match_results =
-      buffer.FindMatches(search_text, find_options);
+      buffer.FindMatches(search_text, /*find_options=*/0);
 
   if (!match_results->IsEmpty() && match_results->front().start == 0u) {
     FindBuffer::BufferMatchResult match = match_results->front();
diff --git a/third_party/blink/renderer/core/page/spatial_navigation.cc b/third_party/blink/renderer/core/page/spatial_navigation.cc
index 614ca481..e215896a 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation.cc
+++ b/third_party/blink/renderer/core/page/spatial_navigation.cc
@@ -56,7 +56,7 @@
 
 constexpr int kFudgeFactor = 2;
 
-static void DeflateIfOverlapped(LayoutRect&, LayoutRect&);
+static void DeflateIfOverlapped(PhysicalRect&, PhysicalRect&);
 
 FocusCandidate::FocusCandidate(Node* node, SpatialNavigationDirection direction)
     : visible_node(nullptr), focusable_node(nullptr), is_offscreen(true) {
@@ -88,15 +88,15 @@
 }
 
 static bool RectsIntersectOnOrthogonalAxis(SpatialNavigationDirection direction,
-                                           const LayoutRect& a,
-                                           const LayoutRect& b) {
+                                           const PhysicalRect& a,
+                                           const PhysicalRect& b) {
   switch (direction) {
     case SpatialNavigationDirection::kLeft:
     case SpatialNavigationDirection::kRight:
-      return a.MaxY() > b.Y() && a.Y() < b.MaxY();
+      return a.Bottom() > b.Y() && a.Y() < b.Bottom();
     case SpatialNavigationDirection::kUp:
     case SpatialNavigationDirection::kDown:
-      return a.MaxX() > b.X() && a.X() < b.MaxX();
+      return a.Right() > b.X() && a.X() < b.Right();
     default:
       NOTREACHED();
       return false;
@@ -106,22 +106,22 @@
 // Return true if rect |a| is below |b|. False otherwise.
 // For overlapping rects, |a| is considered to be below |b|
 // if both edges of |a| are below the respective ones of |b|.
-static inline bool Below(const LayoutRect& a, const LayoutRect& b) {
-  return a.Y() >= b.MaxY() || (a.Y() >= b.Y() && a.MaxY() > b.MaxY() &&
-                               a.X() < b.MaxX() && a.MaxX() > b.X());
+static inline bool Below(const PhysicalRect& a, const PhysicalRect& b) {
+  return a.Y() >= b.Bottom() || (a.Y() >= b.Y() && a.Bottom() > b.Bottom() &&
+                                 a.X() < b.Right() && a.Right() > b.X());
 }
 
 // Return true if rect |a| is on the right of |b|. False otherwise.
 // For overlapping rects, |a| is considered to be on the right of |b|
 // if both edges of |a| are on the right of the respective ones of |b|.
-static inline bool RightOf(const LayoutRect& a, const LayoutRect& b) {
-  return a.X() >= b.MaxX() || (a.X() >= b.X() && a.MaxX() > b.MaxX() &&
-                               a.Y() < b.MaxY() && a.MaxY() > b.Y());
+static inline bool RightOf(const PhysicalRect& a, const PhysicalRect& b) {
+  return a.X() >= b.Right() || (a.X() >= b.X() && a.Right() > b.Right() &&
+                                a.Y() < b.Bottom() && a.Bottom() > b.Y());
 }
 
 static bool IsRectInDirection(SpatialNavigationDirection direction,
-                              const LayoutRect& cur_rect,
-                              const LayoutRect& target_rect) {
+                              const PhysicalRect& cur_rect,
+                              const PhysicalRect& target_rect) {
   switch (direction) {
     case SpatialNavigationDirection::kLeft:
       return RightOf(cur_rect, target_rect);
@@ -148,7 +148,7 @@
   if (!object)
     return FloatRect();
 
-  LayoutRect rect_in_root_frame = NodeRectInRootFrame(&node);
+  PhysicalRect rect_in_root_frame = NodeRectInRootFrame(&node);
 
   // Convert to the visual viewport which will account for pinch zoom.
   VisualViewport& visual_viewport =
@@ -207,9 +207,9 @@
   if (candidate.visible_node->IsMediaElement())
     return true;
 
-  LayoutRect viewport_rect = LayoutRect(
+  PhysicalRect viewport_rect(
       local_main_frame->GetPage()->GetVisualViewport().VisibleContentRect());
-  LayoutRect interesting_rect =
+  PhysicalRect interesting_rect =
       Intersection(candidate.rect_in_root_frame, viewport_rect);
 
   if (interesting_rect.IsEmpty())
@@ -294,7 +294,7 @@
   return true;
 }
 
-static void DeflateIfOverlapped(LayoutRect& a, LayoutRect& b) {
+static void DeflateIfOverlapped(PhysicalRect& a, PhysicalRect& b) {
   if (!a.Intersects(b) || a.Contains(b) || b.Contains(a))
     return;
 
@@ -405,7 +405,7 @@
   ScrollableArea* scrollable_area = frame->View()->GetScrollableArea();
   LayoutSize size(scrollable_area->ContentsSize());
   LayoutSize offset(scrollable_area->ScrollOffsetInt());
-  LayoutRect rect(scrollable_area->VisibleContentRect(kIncludeScrollbars));
+  PhysicalRect rect(scrollable_area->VisibleContentRect(kIncludeScrollbars));
 
   switch (direction) {
     case SpatialNavigationDirection::kLeft:
@@ -422,7 +422,7 @@
   }
 }
 
-LayoutRect NodeRectInRootFrame(const Node* node) {
+PhysicalRect NodeRectInRootFrame(const Node* node) {
   DCHECK(node);
   DCHECK(node->GetLayoutObject());
   DCHECK(!node->GetDocument().View()->NeedsLayout());
@@ -441,7 +441,7 @@
                      LayoutUnit(object->StyleRef().BorderLeftWidth()));
 
   object->MapToVisualRectInAncestorSpace(/*ancestor=*/nullptr, rect);
-  return rect.ToLayoutRect();
+  return rect;
 }
 
 // This method calculates the exitPoint from the startingRect and the entryPoint
@@ -449,38 +449,38 @@
 // distance between the 2 rects.  Takes care of overlapping rects, defining
 // points so that the distance between them is zero where necessary.
 void EntryAndExitPointsForDirection(SpatialNavigationDirection direction,
-                                    const LayoutRect& starting_rect,
-                                    const LayoutRect& potential_rect,
+                                    const PhysicalRect& starting_rect,
+                                    const PhysicalRect& potential_rect,
                                     LayoutPoint& exit_point,
                                     LayoutPoint& entry_point) {
   switch (direction) {
     case SpatialNavigationDirection::kLeft:
       exit_point.SetX(starting_rect.X());
-      if (potential_rect.MaxX() < starting_rect.X())
-        entry_point.SetX(potential_rect.MaxX());
+      if (potential_rect.Right() < starting_rect.X())
+        entry_point.SetX(potential_rect.Right());
       else
         entry_point.SetX(starting_rect.X());
       break;
     case SpatialNavigationDirection::kUp:
       exit_point.SetY(starting_rect.Y());
-      if (potential_rect.MaxY() < starting_rect.Y())
-        entry_point.SetY(potential_rect.MaxY());
+      if (potential_rect.Bottom() < starting_rect.Y())
+        entry_point.SetY(potential_rect.Bottom());
       else
         entry_point.SetY(starting_rect.Y());
       break;
     case SpatialNavigationDirection::kRight:
-      exit_point.SetX(starting_rect.MaxX());
-      if (potential_rect.X() > starting_rect.MaxX())
+      exit_point.SetX(starting_rect.Right());
+      if (potential_rect.X() > starting_rect.Right())
         entry_point.SetX(potential_rect.X());
       else
-        entry_point.SetX(starting_rect.MaxX());
+        entry_point.SetX(starting_rect.Right());
       break;
     case SpatialNavigationDirection::kDown:
-      exit_point.SetY(starting_rect.MaxY());
-      if (potential_rect.Y() > starting_rect.MaxY())
+      exit_point.SetY(starting_rect.Bottom());
+      if (potential_rect.Y() > starting_rect.Bottom())
         entry_point.SetY(potential_rect.Y());
       else
-        entry_point.SetY(starting_rect.MaxY());
+        entry_point.SetY(starting_rect.Bottom());
       break;
     default:
       NOTREACHED();
@@ -491,16 +491,16 @@
     case SpatialNavigationDirection::kRight:
       if (Below(starting_rect, potential_rect)) {
         exit_point.SetY(starting_rect.Y());
-        if (potential_rect.MaxY() < starting_rect.Y())
-          entry_point.SetY(potential_rect.MaxY());
+        if (potential_rect.Bottom() < starting_rect.Y())
+          entry_point.SetY(potential_rect.Bottom());
         else
           entry_point.SetY(starting_rect.Y());
       } else if (Below(potential_rect, starting_rect)) {
-        exit_point.SetY(starting_rect.MaxY());
-        if (potential_rect.Y() > starting_rect.MaxY())
+        exit_point.SetY(starting_rect.Bottom());
+        if (potential_rect.Y() > starting_rect.Bottom())
           entry_point.SetY(potential_rect.Y());
         else
-          entry_point.SetY(starting_rect.MaxY());
+          entry_point.SetY(starting_rect.Bottom());
       } else {
         exit_point.SetY(max(starting_rect.Y(), potential_rect.Y()));
         entry_point.SetY(exit_point.Y());
@@ -510,16 +510,16 @@
     case SpatialNavigationDirection::kDown:
       if (RightOf(starting_rect, potential_rect)) {
         exit_point.SetX(starting_rect.X());
-        if (potential_rect.MaxX() < starting_rect.X())
-          entry_point.SetX(potential_rect.MaxX());
+        if (potential_rect.Right() < starting_rect.X())
+          entry_point.SetX(potential_rect.Right());
         else
           entry_point.SetX(starting_rect.X());
       } else if (RightOf(potential_rect, starting_rect)) {
-        exit_point.SetX(starting_rect.MaxX());
-        if (potential_rect.X() > starting_rect.MaxX())
+        exit_point.SetX(starting_rect.Right());
+        if (potential_rect.X() > starting_rect.Right())
           entry_point.SetX(potential_rect.X());
         else
-          entry_point.SetX(starting_rect.MaxX());
+          entry_point.SetX(starting_rect.Right());
       } else {
         exit_point.SetX(max(starting_rect.X(), potential_rect.X()));
         entry_point.SetX(exit_point.X());
@@ -531,8 +531,8 @@
 }
 
 double ProjectedOverlap(SpatialNavigationDirection direction,
-                        LayoutRect current,
-                        LayoutRect candidate) {
+                        PhysicalRect current,
+                        PhysicalRect candidate) {
   switch (direction) {
     case SpatialNavigationDirection::kLeft:
     case SpatialNavigationDirection::kRight:
@@ -553,8 +553,8 @@
 }
 
 double Alignment(SpatialNavigationDirection direction,
-                 LayoutRect current,
-                 LayoutRect candidate) {
+                 PhysicalRect current,
+                 PhysicalRect candidate) {
   // The formula and constants for "alignment" are experimental and
   // come from https://drafts.csswg.org/css-nav-1/#heuristics.
   const int kAlignWeight = 5;
@@ -578,8 +578,8 @@
                                   const FocusCandidate& candidate) {
   double distance = 0.0;
   double overlap = 0.0;
-  LayoutRect node_rect = candidate.rect_in_root_frame;
-  LayoutRect current_rect = current_interest.rect_in_root_frame;
+  PhysicalRect node_rect = candidate.rect_in_root_frame;
+  PhysicalRect current_rect = current_interest.rect_in_root_frame;
   if (node_rect.Contains(current_rect)) {
     // When leaving an "insider", don't focus its underlaying container box.
     // Go directly to the outside world. This avoids focus from being trapped
@@ -605,7 +605,7 @@
     return kMaxDistance;
   } else {
     DeflateIfOverlapped(current_rect, node_rect);
-    LayoutRect intersection_rect = Intersection(current_rect, node_rect);
+    PhysicalRect intersection_rect = Intersection(current_rect, node_rect);
     overlap =
         (intersection_rect.Width() * intersection_rect.Height()).ToDouble();
   }
@@ -666,28 +666,28 @@
 // Returns a thin rectangle that represents one of |box|'s edges.
 // To not intersect elements that are positioned inside |box|, we add one
 // LayoutUnit of margin that puts the returned slice "just outside" |box|.
-LayoutRect OppositeEdge(SpatialNavigationDirection side,
-                        const LayoutRect& box,
-                        LayoutUnit thickness) {
-  LayoutRect thin_rect = box;
+PhysicalRect OppositeEdge(SpatialNavigationDirection side,
+                          const PhysicalRect& box,
+                          LayoutUnit thickness) {
+  PhysicalRect thin_rect = box;
   switch (side) {
     case SpatialNavigationDirection::kLeft:
-      thin_rect.SetX(thin_rect.MaxX() - thickness);
+      thin_rect.SetX(thin_rect.Right() - thickness);
       thin_rect.SetWidth(thickness);
-      thin_rect.Move(1, 0);
+      thin_rect.offset.left += 1;
       break;
     case SpatialNavigationDirection::kRight:
       thin_rect.SetWidth(thickness);
-      thin_rect.Move(-1, 0);
+      thin_rect.offset.left -= 1;
       break;
     case SpatialNavigationDirection::kDown:
       thin_rect.SetHeight(thickness);
-      thin_rect.Move(0, -1);
+      thin_rect.offset.top -= 1;
       break;
     case SpatialNavigationDirection::kUp:
-      thin_rect.SetY(thin_rect.MaxY() - thickness);
+      thin_rect.SetY(thin_rect.Bottom() - thickness);
       thin_rect.SetHeight(thickness);
-      thin_rect.Move(0, 1);
+      thin_rect.offset.top += 1;
       break;
     default:
       NOTREACHED();
@@ -696,13 +696,13 @@
   return thin_rect;
 }
 
-LayoutRect StartEdgeForAreaElement(const HTMLAreaElement& area,
-                                   SpatialNavigationDirection direction) {
+PhysicalRect StartEdgeForAreaElement(const HTMLAreaElement& area,
+                                     SpatialNavigationDirection direction) {
   DCHECK(area.ImageElement());
   // Area elements tend to overlap more than other focusable elements. We
   // flatten the rect of the area elements to minimize the effect of overlapping
   // areas.
-  LayoutRect rect = OppositeEdge(
+  PhysicalRect rect = OppositeEdge(
       direction,
       area.GetDocument().GetFrame()->View()->ConvertToRootFrame(
           area.ComputeAbsoluteRect(area.ImageElement()->GetLayoutObject())),
@@ -715,8 +715,8 @@
 }
 
 // The visual viewport's rect (given in the root frame's coordinate space).
-LayoutRect RootViewport(const LocalFrame* current_frame) {
-  return LayoutRect(
+PhysicalRect RootViewport(const LocalFrame* current_frame) {
+  return PhysicalRect::EnclosingRect(
       current_frame->GetPage()->GetVisualViewport().VisibleRect());
 }
 
@@ -727,9 +727,9 @@
 // scroller’s scroller ... all the way up until the root frame's document.
 // The root frame's document is a good base case because it's, per definition,
 // a visible scrollable area.
-LayoutRect SearchOrigin(const LayoutRect viewport_rect_of_root_frame,
-                        Node* focus_node,
-                        const SpatialNavigationDirection direction) {
+PhysicalRect SearchOrigin(const PhysicalRect& viewport_rect_of_root_frame,
+                          Node* focus_node,
+                          const SpatialNavigationDirection direction) {
   if (!focus_node) {
     // Search from one of the visual viewport's edges towards the navigated
     // direction. For example, UP makes spatnav search upwards, starting at the
@@ -745,7 +745,7 @@
     if (area_element)
       return StartEdgeForAreaElement(*area_element, direction);
 
-    LayoutRect box_in_root_frame = NodeRectInRootFrame(focus_node);
+    PhysicalRect box_in_root_frame = NodeRectInRootFrame(focus_node);
     return Intersection(box_in_root_frame, viewport_rect_of_root_frame);
   }
 
@@ -753,7 +753,7 @@
   while (container) {
     if (!IsOffscreen(container)) {
       // The first scroller that encloses focus and is [partially] visible.
-      LayoutRect box_in_root_frame = NodeRectInRootFrame(container);
+      PhysicalRect box_in_root_frame = NodeRectInRootFrame(container);
       return OppositeEdge(direction, Intersection(box_in_root_frame,
                                                   viewport_rect_of_root_frame));
     }
diff --git a/third_party/blink/renderer/core/page/spatial_navigation.h b/third_party/blink/renderer/core/page/spatial_navigation.h
index 7112a4d..75bba97a 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation.h
+++ b/third_party/blink/renderer/core/page/spatial_navigation.h
@@ -24,7 +24,7 @@
 #include "third_party/blink/public/platform/web_focus_type.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/node.h"
-#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
 
 #include <limits>
 
@@ -60,7 +60,7 @@
   // visibleNode and focusableNode are one and the same.
   Member<Node> visible_node;
   Member<Node> focusable_node;
-  LayoutRect rect_in_root_frame;
+  PhysicalRect rect_in_root_frame;
   bool is_offscreen;
 };
 
@@ -78,17 +78,17 @@
 double ComputeDistanceDataForNode(SpatialNavigationDirection,
                                   const FocusCandidate& current_interest,
                                   const FocusCandidate& candidate);
-CORE_EXPORT LayoutRect NodeRectInRootFrame(const Node*);
-CORE_EXPORT LayoutRect OppositeEdge(SpatialNavigationDirection side,
-                                    const LayoutRect& box,
-                                    LayoutUnit thickness = LayoutUnit());
-CORE_EXPORT LayoutRect RootViewport(const LocalFrame*);
-LayoutRect StartEdgeForAreaElement(const HTMLAreaElement&,
-                                   SpatialNavigationDirection);
+CORE_EXPORT PhysicalRect NodeRectInRootFrame(const Node*);
+CORE_EXPORT PhysicalRect OppositeEdge(SpatialNavigationDirection side,
+                                      const PhysicalRect& box,
+                                      LayoutUnit thickness = LayoutUnit());
+CORE_EXPORT PhysicalRect RootViewport(const LocalFrame*);
+PhysicalRect StartEdgeForAreaElement(const HTMLAreaElement&,
+                                     SpatialNavigationDirection);
 HTMLFrameOwnerElement* FrameOwnerElement(const FocusCandidate&);
-CORE_EXPORT LayoutRect SearchOrigin(const LayoutRect,
-                                    Node*,
-                                    const SpatialNavigationDirection);
+CORE_EXPORT PhysicalRect SearchOrigin(const PhysicalRect&,
+                                      Node*,
+                                      const SpatialNavigationDirection);
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/core/page/spatial_navigation_controller.cc b/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
index e398bd1..782045c 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
+++ b/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
@@ -232,15 +232,16 @@
 
   Node* container = ScrollableAreaOrDocumentOf(interest_node);
 
-  const LayoutRect visible_rect(page_->GetVisualViewport().VisibleRect());
-  const LayoutRect start_box =
+  const PhysicalRect visible_rect =
+      PhysicalRect::EnclosingRect(page_->GetVisualViewport().VisibleRect());
+  const PhysicalRect start_box =
       SearchOrigin(visible_rect, interest_node, direction);
 
   if (IsScrollableAreaOrDocument(interest_node) &&
       !IsOffscreen(interest_node)) {
     // A visible scroller has interest. Search inside of it from one of its
     // edges.
-    LayoutRect edge = OppositeEdge(direction, start_box);
+    PhysicalRect edge = OppositeEdge(direction, start_box);
     if (AdvanceWithinContainer(*interest_node, edge, direction, nullptr))
       return true;
   }
@@ -273,7 +274,7 @@
 
 FocusCandidate SpatialNavigationController::FindNextCandidateInContainer(
     Node& container,
-    const LayoutRect& starting_rect_in_root_frame,
+    const PhysicalRect& starting_rect_in_root_frame,
     SpatialNavigationDirection direction,
     Node* interest_child_in_container) {
   Element* element = ElementTraversal::FirstWithin(container);
@@ -312,7 +313,7 @@
 
 bool SpatialNavigationController::AdvanceWithinContainer(
     Node& container,
-    const LayoutRect& starting_rect_in_root_frame,
+    const PhysicalRect& starting_rect_in_root_frame,
     SpatialNavigationDirection direction,
     Node* interest_child_in_container) {
   DCHECK(IsScrollableAreaOrDocument(&container));
diff --git a/third_party/blink/renderer/core/page/spatial_navigation_controller.h b/third_party/blink/renderer/core/page/spatial_navigation_controller.h
index 05ee39d..f6026b0 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation_controller.h
+++ b/third_party/blink/renderer/core/page/spatial_navigation_controller.h
@@ -14,9 +14,9 @@
 
 struct FocusCandidate;
 class KeyboardEvent;
-class LayoutRect;
 class Node;
 class Page;
+struct PhysicalRect;
 
 // Encapsulates logic and state related to "spatial navigation". Spatial
 // Navigation is used to move and interact with a page in a purely directional
@@ -65,14 +65,14 @@
    *                               may be in a nested container.
    */
   bool AdvanceWithinContainer(Node& container,
-                              const LayoutRect& starting_rect_in_root_frame,
+                              const PhysicalRect& starting_rect_in_root_frame,
                               SpatialNavigationDirection direction,
                               Node* interest_child_in_container);
 
   // Parameters have same meanings as method above.
   FocusCandidate FindNextCandidateInContainer(
       Node& container,
-      const LayoutRect& starting_rect_in_root_frame,
+      const PhysicalRect& starting_rect_in_root_frame,
       SpatialNavigationDirection direction,
       Node* interest_child_in_container);
 
diff --git a/third_party/blink/renderer/core/page/spatial_navigation_test.cc b/third_party/blink/renderer/core/page/spatial_navigation_test.cc
index a7416224..63a57e5 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation_test.cc
+++ b/third_party/blink/renderer/core/page/spatial_navigation_test.cc
@@ -24,30 +24,30 @@
   SpatialNavigationTest()
       : RenderingTest(MakeGarbageCollected<SingleChildLocalFrameClient>()) {}
 
-  LayoutRect TopOfVisualViewport() {
-    LayoutRect visual_viewport = RootViewport(&GetFrame());
+  PhysicalRect TopOfVisualViewport() {
+    PhysicalRect visual_viewport = RootViewport(&GetFrame());
     visual_viewport.SetY(visual_viewport.Y() - 1);
     visual_viewport.SetHeight(LayoutUnit(0));
     return visual_viewport;
   }
 
-  LayoutRect BottomOfVisualViewport() {
-    LayoutRect visual_viewport = RootViewport(&GetFrame());
-    visual_viewport.SetY(visual_viewport.MaxY() + 1);
+  PhysicalRect BottomOfVisualViewport() {
+    PhysicalRect visual_viewport = RootViewport(&GetFrame());
+    visual_viewport.SetY(visual_viewport.Bottom() + 1);
     visual_viewport.SetHeight(LayoutUnit(0));
     return visual_viewport;
   }
 
-  LayoutRect LeftSideOfVisualViewport() {
-    LayoutRect visual_viewport = RootViewport(&GetFrame());
+  PhysicalRect LeftSideOfVisualViewport() {
+    PhysicalRect visual_viewport = RootViewport(&GetFrame());
     visual_viewport.SetX(visual_viewport.X() - 1);
     visual_viewport.SetWidth(LayoutUnit(0));
     return visual_viewport;
   }
 
-  LayoutRect RightSideOfVisualViewport() {
-    LayoutRect visual_viewport = RootViewport(&GetFrame());
-    visual_viewport.SetX(visual_viewport.MaxX() + 1);
+  PhysicalRect RightSideOfVisualViewport() {
+    PhysicalRect visual_viewport = RootViewport(&GetFrame());
+    visual_viewport.SetX(visual_viewport.Right() + 1);
     visual_viewport.SetWidth(LayoutUnit(0));
     return visual_viewport;
   }
@@ -80,11 +80,11 @@
   visual_viewport.SetLocation(FloatPoint(200, 200));
 
   LocalFrameView* root_frame_view = GetFrame().LocalFrameRoot().View();
-  const LayoutRect roots_visible_doc_rect(
+  const PhysicalRect roots_visible_doc_rect(
       root_frame_view->GetScrollableArea()->VisibleContentRect());
   // Convert the root frame's visible rect from document space -> frame space.
   // For the root frame, frame space == root frame space, obviously.
-  LayoutRect viewport_rect_of_root_frame =
+  PhysicalRect viewport_rect_of_root_frame =
       root_frame_view->DocumentToFrame(roots_visible_doc_rect);
 
   EXPECT_EQ(viewport_rect_of_root_frame, RootViewport(&GetFrame()));
@@ -187,11 +187,11 @@
 }
 
 TEST_F(SpatialNavigationTest, RootViewportRespectsVisibleSize) {
-  EXPECT_EQ(RootViewport(&GetFrame()), LayoutRect(0, 0, 800, 600));
+  EXPECT_EQ(RootViewport(&GetFrame()), PhysicalRect(0, 0, 800, 600));
 
   VisualViewport& visual_viewport = GetFrame().GetPage()->GetVisualViewport();
   visual_viewport.SetSize({123, 123});
-  EXPECT_EQ(RootViewport(&GetFrame()), LayoutRect(0, 0, 123, 123));
+  EXPECT_EQ(RootViewport(&GetFrame()), PhysicalRect(0, 0, 123, 123));
 }
 
 TEST_F(SpatialNavigationTest, StartAtVisibleFocusedElement) {
@@ -247,7 +247,7 @@
 }
 
 TEST_F(SpatialNavigationTest, StartAtTopWhenGoingDownwardsWithoutFocus) {
-  EXPECT_EQ(LayoutRect(0, -1, 111, 0),
+  EXPECT_EQ(PhysicalRect(0, -1, 111, 0),
             SearchOrigin({0, 0, 111, 222}, nullptr,
                          SpatialNavigationDirection::kDown));
 
@@ -258,7 +258,7 @@
 
 TEST_F(SpatialNavigationTest, StartAtBottomWhenGoingUpwardsWithoutFocus) {
   EXPECT_EQ(
-      LayoutRect(0, 222 + 1, 111, 0),
+      PhysicalRect(0, 222 + 1, 111, 0),
       SearchOrigin({0, 0, 111, 222}, nullptr, SpatialNavigationDirection::kUp));
 
   EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), nullptr,
@@ -267,7 +267,7 @@
 }
 
 TEST_F(SpatialNavigationTest, StartAtLeftSideWhenGoingEastWithoutFocus) {
-  EXPECT_EQ(LayoutRect(-1, 0, 0, 222),
+  EXPECT_EQ(PhysicalRect(-1, 0, 0, 222),
             SearchOrigin({0, 0, 111, 222}, nullptr,
                          SpatialNavigationDirection::kRight));
 
@@ -277,7 +277,7 @@
 }
 
 TEST_F(SpatialNavigationTest, StartAtRightSideWhenGoingWestWithoutFocus) {
-  EXPECT_EQ(LayoutRect(111 + 1, 0, 0, 222),
+  EXPECT_EQ(PhysicalRect(111 + 1, 0, 0, 222),
             SearchOrigin({0, 0, 111, 222}, nullptr,
                          SpatialNavigationDirection::kLeft));
 
@@ -319,7 +319,7 @@
 
   Element* b = GetDocument().getElementById("b");
   const Element* container = GetDocument().getElementById("container");
-  const LayoutRect container_box = NodeRectInRootFrame(container);
+  const PhysicalRect container_box = NodeRectInRootFrame(container);
 
   // TODO(crbug.com/889840):
   // VisibleBoundsInVisualViewport does not (yet) take div-clipping into
@@ -330,7 +330,7 @@
   EXPECT_TRUE(IsOffscreen(b));
 
   // Go down.
-  LayoutRect container_top_edge = container_box;
+  PhysicalRect container_top_edge = container_box;
   container_top_edge.SetHeight(LayoutUnit(0));
   container_top_edge.SetY(container_top_edge.Y() - 1);
   EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b,
@@ -338,15 +338,15 @@
             container_top_edge);
 
   // Go up.
-  LayoutRect container_bottom_edge = container_box;
+  PhysicalRect container_bottom_edge = container_box;
   container_bottom_edge.SetHeight(LayoutUnit(0));
-  container_bottom_edge.SetY(container_bottom_edge.MaxX() + 1);
+  container_bottom_edge.SetY(container_bottom_edge.Right() + 1);
   EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b,
                          SpatialNavigationDirection::kUp),
             container_bottom_edge);
 
   // Go right.
-  LayoutRect container_leftmost_edge = container_box;
+  PhysicalRect container_leftmost_edge = container_box;
   container_leftmost_edge.SetWidth(LayoutUnit(0));
   container_leftmost_edge.SetX(container_leftmost_edge.X() - 1);
   EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b,
@@ -354,8 +354,8 @@
             container_leftmost_edge);
 
   // Go left.
-  LayoutRect container_rightmost_edge = container_box;
-  container_rightmost_edge.SetX(container_bottom_edge.MaxX() + 1);
+  PhysicalRect container_rightmost_edge = container_box;
+  container_rightmost_edge.SetX(container_bottom_edge.Right() + 1);
   container_rightmost_edge.SetWidth(LayoutUnit(0));
   EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b,
                          SpatialNavigationDirection::kLeft),
@@ -437,7 +437,7 @@
 
   EXPECT_FALSE(IsOffscreen(b));  // <button> is not completely offscreen.
 
-  LayoutRect button_in_root_frame = NodeRectInRootFrame(b);
+  PhysicalRect button_in_root_frame = NodeRectInRootFrame(b);
 
   EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b,
                          SpatialNavigationDirection::kUp),
@@ -446,7 +446,7 @@
   // Do some scrolling.
   ScrollableArea* root_scroller = GetDocument().View()->GetScrollableArea();
   root_scroller->SetScrollOffset(ScrollOffset(0, 600), kProgrammaticScroll);
-  LayoutRect button_after_scroll = NodeRectInRootFrame(b);
+  PhysicalRect button_after_scroll = NodeRectInRootFrame(b);
   ASSERT_NE(button_in_root_frame,
             button_after_scroll);  // As we scrolled, the
                                    // <button>'s position in
@@ -563,7 +563,7 @@
   EXPECT_TRUE(IsOffscreen(child_element));         // Completely offscreen.
   EXPECT_FALSE(IsOffscreen(enclosing_container));  // Partially visible.
 
-  LayoutRect iframe = NodeRectInRootFrame(enclosing_container);
+  PhysicalRect iframe = NodeRectInRootFrame(enclosing_container);
 
   // When searching downwards we start at activeElement's
   // container's (here: the iframe's) topmost visible edge.
@@ -595,8 +595,8 @@
 }
 
 TEST_F(SpatialNavigationTest, BottomOfPinchedViewport) {
-  LayoutRect origin = SearchOrigin(RootViewport(&GetFrame()), nullptr,
-                                   SpatialNavigationDirection::kUp);
+  PhysicalRect origin = SearchOrigin(RootViewport(&GetFrame()), nullptr,
+                                     SpatialNavigationDirection::kUp);
   EXPECT_EQ(origin.Height(), 0);
   EXPECT_EQ(origin.Width(), GetFrame().View()->Width());
   EXPECT_EQ(origin.X(), 0);
@@ -617,8 +617,8 @@
 }
 
 TEST_F(SpatialNavigationTest, TopOfPinchedViewport) {
-  LayoutRect origin = SearchOrigin(RootViewport(&GetFrame()), nullptr,
-                                   SpatialNavigationDirection::kDown);
+  PhysicalRect origin = SearchOrigin(RootViewport(&GetFrame()), nullptr,
+                                     SpatialNavigationDirection::kDown);
   EXPECT_EQ(origin.Height(), 0);
   EXPECT_EQ(origin.Width(), GetFrame().View()->Width());
   EXPECT_EQ(origin.X(), 0);
diff --git a/third_party/blink/renderer/core/paint/block_painter.cc b/third_party/blink/renderer/core/paint/block_painter.cc
index f0ecc52..c31bdb4a 100644
--- a/third_party/blink/renderer/core/paint/block_painter.cc
+++ b/third_party/blink/renderer/core/paint/block_painter.cc
@@ -293,7 +293,7 @@
     ObjectPainter(layout_block_).PaintInlineChildrenOutlines(paint_info);
   } else {
     LineBoxListPainter(To<LayoutBlockFlow>(layout_block_).LineBoxes())
-        .Paint(layout_block_, paint_info, paint_offset.ToLayoutPoint());
+        .Paint(layout_block_, paint_info, paint_offset);
   }
 
   // If we don't have any floats to paint, or we're in the wrong paint phase,
diff --git a/third_party/blink/renderer/core/paint/clip_rect.cc b/third_party/blink/renderer/core/paint/clip_rect.cc
index b72162b..f4f80c1 100644
--- a/third_party/blink/renderer/core/paint/clip_rect.cc
+++ b/third_party/blink/renderer/core/paint/clip_rect.cc
@@ -77,7 +77,7 @@
 bool ClipRect::Intersects(const HitTestLocation& hit_test_location) const {
   if (is_infinite_)
     return true;
-  return hit_test_location.Intersects(rect_.ToLayoutRect());
+  return hit_test_location.Intersects(rect_);
 }
 
 void ClipRect::Reset() {
diff --git a/third_party/blink/renderer/core/paint/ellipsis_box_painter.cc b/third_party/blink/renderer/core/paint/ellipsis_box_painter.cc
index 1278e8fd..1ac6cb2 100644
--- a/third_party/blink/renderer/core/paint/ellipsis_box_painter.cc
+++ b/third_party/blink/renderer/core/paint/ellipsis_box_painter.cc
@@ -35,7 +35,7 @@
                                        LayoutUnit line_top,
                                        LayoutUnit line_bottom,
                                        const ComputedStyle& style) {
-  LayoutPoint box_origin = ellipsis_box_.PhysicalLocation();
+  LayoutPoint box_origin = ellipsis_box_.PhysicalLocation().ToLayoutPoint();
   box_origin.MoveBy(paint_offset);
 
   GraphicsContext& context = paint_info.context;
diff --git a/third_party/blink/renderer/core/paint/image_element_timing.cc b/third_party/blink/renderer/core/paint/image_element_timing.cc
index adc60ca..6494cfc 100644
--- a/third_party/blink/renderer/core/paint/image_element_timing.cc
+++ b/third_party/blink/renderer/core/paint/image_element_timing.cc
@@ -32,6 +32,11 @@
 // static
 const char ImageElementTiming::kSupplementName[] = "ImageElementTiming";
 
+AtomicString ImagePaintString() {
+  DEFINE_STATIC_LOCAL(const AtomicString, kImagePaint, ("image-paint"));
+  return kImagePaint;
+}
+
 // static
 ImageElementTiming& ImageElementTiming::From(LocalDOMWindow& window) {
   ImageElementTiming* timing =
@@ -109,7 +114,7 @@
          performance->ShouldBufferEntries())) {
       // Create an entry with a |startTime| of 0.
       performance->AddElementTiming(
-          url.GetString(), intersection_rect, TimeTicks(),
+          ImagePaintString(), url.GetString(), intersection_rect, TimeTicks(),
           cached_image.LoadResponseEnd(), attr,
           cached_image.IntrinsicSize(kDoNotRespectImageOrientation), id,
           element);
@@ -200,8 +205,8 @@
                       performance->ShouldBufferEntries())) {
     for (const auto& element_timing : element_timings_) {
       performance->AddElementTiming(
-          element_timing->url, element_timing->rect, timestamp,
-          element_timing->response_end, element_timing->identifier,
+          ImagePaintString(), element_timing->url, element_timing->rect,
+          timestamp, element_timing->response_end, element_timing->identifier,
           element_timing->intrinsic_size, element_timing->id,
           element_timing->element);
     }
diff --git a/third_party/blink/renderer/core/paint/inline_painter.cc b/third_party/blink/renderer/core/paint/inline_painter.cc
index d21a475f..7a5e1101 100644
--- a/third_party/blink/renderer/core/paint/inline_painter.cc
+++ b/third_party/blink/renderer/core/paint/inline_painter.cc
@@ -80,7 +80,7 @@
   }
 
   LineBoxListPainter(*layout_inline_.LineBoxes())
-      .Paint(layout_inline_, local_paint_info, paint_offset.ToLayoutPoint());
+      .Paint(layout_inline_, local_paint_info, paint_offset);
 }
 
 }  // namespace blink
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 70ff636..2d17c819 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
@@ -168,7 +168,8 @@
       inline_text_box_.GetLineLayoutItem().StyleRef(
           inline_text_box_.IsFirstLineStyle());
 
-  LayoutPoint box_origin(inline_text_box_.PhysicalLocation() + paint_offset);
+  LayoutPoint box_origin(inline_text_box_.PhysicalLocation().ToLayoutPoint() +
+                         paint_offset);
 
   // We round the y-axis to ensure consistent line heights.
   if (inline_text_box_.IsHorizontal()) {
diff --git a/third_party/blink/renderer/core/paint/line_box_list_painter.cc b/third_party/blink/renderer/core/paint/line_box_list_painter.cc
index 577aa56..58aa23f 100644
--- a/third_party/blink/renderer/core/paint/line_box_list_painter.cc
+++ b/third_party/blink/renderer/core/paint/line_box_list_painter.cc
@@ -19,14 +19,13 @@
 static void AddPDFURLRectsForInlineChildrenRecursively(
     const LayoutObject& layout_object,
     const PaintInfo& paint_info,
-    const LayoutPoint& paint_offset) {
+    const PhysicalOffset& paint_offset) {
   for (LayoutObject* child = layout_object.SlowFirstChild(); child;
        child = child->NextSibling()) {
     if (!child->IsLayoutInline() ||
         ToLayoutBoxModelObject(child)->HasSelfPaintingLayer())
       continue;
-    ObjectPainter(*child).AddPDFURLRectIfNeeded(
-        paint_info, PhysicalOffsetToBeNoop(paint_offset));
+    ObjectPainter(*child).AddPDFURLRectIfNeeded(paint_info, paint_offset);
     AddPDFURLRectsForInlineChildrenRecursively(*child, paint_info,
                                                paint_offset);
   }
@@ -34,7 +33,7 @@
 
 void LineBoxListPainter::Paint(const LayoutBoxModelObject& layout_object,
                                const PaintInfo& paint_info,
-                               const LayoutPoint& paint_offset) const {
+                               const PhysicalOffset& paint_offset) const {
   DCHECK(!ShouldPaintSelfOutline(paint_info.phase) &&
          !ShouldPaintDescendantOutlines(paint_info.phase));
 
@@ -83,7 +82,8 @@
                 const_cast<LayoutBoxModelObject*>(&layout_object)),
             curr, paint_info.GetCullRect(), paint_offset)) {
       RootInlineBox& root = curr->Root();
-      curr->Paint(paint_info, paint_offset, root.LineTop(), root.LineBottom());
+      curr->Paint(paint_info, paint_offset.ToLayoutPoint(), root.LineTop(),
+                  root.LineBottom());
     }
   }
 }
diff --git a/third_party/blink/renderer/core/paint/line_box_list_painter.h b/third_party/blink/renderer/core/paint/line_box_list_painter.h
index d2d4b46..7044c2b 100644
--- a/third_party/blink/renderer/core/paint/line_box_list_painter.h
+++ b/third_party/blink/renderer/core/paint/line_box_list_painter.h
@@ -10,10 +10,10 @@
 
 namespace blink {
 
-class LayoutPoint;
-struct PaintInfo;
 class LayoutBoxModelObject;
 class LineBoxList;
+struct PaintInfo;
+struct PhysicalOffset;
 
 class LineBoxListPainter {
   STACK_ALLOCATED();
@@ -24,7 +24,7 @@
 
   void Paint(const LayoutBoxModelObject&,
              const PaintInfo&,
-             const LayoutPoint& paint_offset) const;
+             const PhysicalOffset& paint_offset) const;
 
  private:
   const LineBoxList& line_box_list_;
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
index 3be22c9..2215aa6 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
@@ -69,14 +69,14 @@
                                   const NGPaintFragment& fragment,
                                   const NGPaintFragment* previous_sibling,
                                   const HitTestLocation& location_in_container,
-                                  const LayoutPoint& physical_offset) {
+                                  const PhysicalOffset& physical_offset) {
   DCHECK(fragment.Parent());
   DCHECK(fragment.PhysicalFragment().IsInline());
   const NGPaintFragment& parent = *fragment.Parent();
   // To be passed as |accumulated_offset| to LayoutInline::HitTestCulledInline,
   // where it equals the physical offset of the containing block in paint layer.
-  const LayoutPoint fallback_accumulated_offset =
-      physical_offset - fragment.InlineOffsetToContainerBox().ToLayoutSize();
+  const PhysicalOffset fallback_accumulated_offset =
+      physical_offset - fragment.InlineOffsetToContainerBox();
   const LayoutObject* limit_layout_object =
       parent.PhysicalFragment().IsLineBox() ? parent.Parent()->GetLayoutObject()
                                             : parent.GetLayoutObject();
@@ -192,8 +192,9 @@
   PaintOverflowControlsIfNeeded(info, paint_offset);
 }
 
-void NGBoxFragmentPainter::RecordHitTestData(const PaintInfo& paint_info,
-                                             const LayoutPoint& paint_offset) {
+void NGBoxFragmentPainter::RecordHitTestData(
+    const PaintInfo& paint_info,
+    const PhysicalOffset& paint_offset) {
   const NGPhysicalFragment& physical_fragment = PhysicalFragment();
   // TODO(pdr): If we are painting the background into the scrolling contents
   // layer, we need to use the overflow rect instead of the border box rect. We
@@ -203,7 +204,7 @@
   PhysicalRect border_box = physical_fragment.LocalRect();
   if (physical_fragment.IsInline())
     border_box.offset += box_fragment_.InlineOffsetToContainerBox();
-  border_box.offset += PhysicalOffsetToBeNoop(paint_offset);
+  border_box.offset += paint_offset;
   HitTestDisplayItem::Record(
       paint_info.context, box_fragment_,
       HitTestRect(border_box.ToLayoutRect(),
@@ -212,10 +213,10 @@
 
 void NGBoxFragmentPainter::RecordHitTestDataForLine(
     const PaintInfo& paint_info,
-    const LayoutPoint& paint_offset,
+    const PhysicalOffset& paint_offset,
     const NGPaintFragment& line) {
   PhysicalRect border_box = line.PhysicalFragment().LocalRect();
-  border_box.offset += PhysicalOffsetToBeNoop(paint_offset);
+  border_box.offset += paint_offset;
   HitTestDisplayItem::Record(
       paint_info.context, line,
       HitTestRect(border_box.ToLayoutRect(),
@@ -237,7 +238,7 @@
 
     if (NGFragmentPainter::ShouldRecordHitTestData(paint_info,
                                                    physical_box_fragment))
-      RecordHitTestData(paint_info, paint_offset.ToLayoutPoint());
+      RecordHitTestData(paint_info, paint_offset);
 
     // Record the scroll hit test after the background so background squashing
     // is not affected. Hit test order would be equivalent if this were
@@ -761,8 +762,7 @@
     if (paint_info.phase == PaintPhase::kForeground) {
       if (NGFragmentPainter::ShouldRecordHitTestData(paint_info,
                                                      PhysicalFragment())) {
-        RecordHitTestDataForLine(paint_info, child_offset.ToLayoutPoint(),
-                                 *line);
+        RecordHitTestDataForLine(paint_info, child_offset, *line);
       }
 
       // Line boxes don't paint anything, except when its ::first-line style has
@@ -793,9 +793,8 @@
       continue;
 
     // Skip if this child does not intersect with CullRect.
-    if (!paint_info.GetCullRect().Intersects(
-            child->InkOverflow().ToLayoutRect(),
-            (paint_offset + child->Offset()).ToLayoutPoint()) &&
+    if (!paint_info.IntersectsCullRect(child->InkOverflow(),
+                                       paint_offset + child->Offset()) &&
         // Don't skip empty size text in order to paint selection for <br>.
         !(child_fragment.IsText() && child_fragment.Size().IsEmpty()))
       continue;
@@ -986,10 +985,9 @@
 bool NGBoxFragmentPainter::NodeAtPoint(
     HitTestResult& result,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& physical_offset,
+    const PhysicalOffset& physical_offset,
     HitTestAction action) {
-  // TODO(eae): Switch to using NG geometry types.
-  LayoutSize size(box_fragment_.Size().width, box_fragment_.Size().height);
+  const PhysicalSize& size = box_fragment_.Size();
   const ComputedStyle& style = box_fragment_.Style();
 
   bool hit_test_self = IsInSelfHitTestingPhase(action);
@@ -1007,27 +1005,25 @@
     // foreground rect for intersection if a layer is self painting,
     // so only do the overflow clip check here for non-self-painting layers.
     if (!box_fragment_.HasSelfPaintingLayer() &&
-        !location_in_container.Intersects(
-            PhysicalFragment()
-                .OverflowClipRect(PhysicalOffsetToBeNoop(physical_offset),
-                                  kExcludeOverlayScrollbarSizeForHitTesting)
-                .ToLayoutRect())) {
+        !location_in_container.Intersects(PhysicalFragment().OverflowClipRect(
+            physical_offset, kExcludeOverlayScrollbarSizeForHitTesting))) {
       skip_children = true;
     }
     if (!skip_children && style.HasBorderRadius()) {
-      LayoutRect bounds_rect(physical_offset, size);
+      PhysicalRect bounds_rect(physical_offset, size);
       skip_children = !location_in_container.Intersects(
-          style.GetRoundedInnerBorderFor(bounds_rect));
+          style.GetRoundedInnerBorderFor(bounds_rect.ToLayoutRect()));
     }
   }
 
   if (!skip_children) {
-    const IntSize scrolled_offset =
-        box_fragment_.HasOverflowClip()
-            ? PhysicalFragment().ScrolledContentOffset()
-            : IntSize();
+    PhysicalOffset scrolled_offset = physical_offset;
+    if (box_fragment_.HasOverflowClip()) {
+      scrolled_offset -=
+          PhysicalOffset(PhysicalFragment().ScrolledContentOffset());
+    }
     if (HitTestChildren(result, box_fragment_.Children(), location_in_container,
-                        physical_offset - scrolled_offset, action)) {
+                        scrolled_offset, action)) {
       return true;
     }
   }
@@ -1038,17 +1034,16 @@
 
   // Now hit test ourselves.
   if (hit_test_self && VisibleToHitTestRequest(result.GetHitTestRequest())) {
-    LayoutRect bounds_rect(physical_offset, size);
+    PhysicalRect bounds_rect(physical_offset, size);
     if (UNLIKELY(result.GetHitTestRequest().GetType() &
                  HitTestRequest::kHitTestVisualOverflow)) {
-      bounds_rect = box_fragment_.SelfInkOverflow().ToLayoutRect();
-      bounds_rect.MoveBy(physical_offset);
+      bounds_rect = box_fragment_.SelfInkOverflow();
+      bounds_rect.Move(physical_offset);
     }
     if (location_in_container.Intersects(bounds_rect)) {
       Node* node = box_fragment_.NodeForHitTest();
       if (!result.InnerNode() && node) {
-        LayoutPoint point =
-            location_in_container.Point() - ToLayoutSize(physical_offset);
+        PhysicalOffset point = location_in_container.Point() - physical_offset;
         result.SetNodeAndPosition(node, point);
       }
       if (result.AddNodeToListBasedTestResult(node, location_in_container,
@@ -1070,22 +1065,22 @@
     HitTestResult& result,
     const NGPaintFragment& text_paint_fragment,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& physical_offset,
+    const PhysicalOffset& physical_offset,
     HitTestAction action) {
   if (action != kHitTestForeground)
     return false;
 
   const auto& text_fragment =
       To<NGPhysicalTextFragment>(text_paint_fragment.PhysicalFragment());
-  LayoutSize size(text_fragment.Size().width, text_fragment.Size().height);
-  LayoutRect border_rect(physical_offset, size);
+  PhysicalSize size(text_fragment.Size().width, text_fragment.Size().height);
+  PhysicalRect border_rect(physical_offset, size);
 
   // TODO(layout-dev): Clip to line-top/bottom.
-  LayoutRect rect = LayoutRect(PixelSnappedIntRect(border_rect));
+  PhysicalRect rect(PixelSnappedIntRect(border_rect));
   if (UNLIKELY(result.GetHitTestRequest().GetType() &
                HitTestRequest::kHitTestVisualOverflow)) {
-    rect = text_fragment.SelfInkOverflow().ToLayoutRect();
-    rect.MoveBy(border_rect.Location());
+    rect = text_fragment.SelfInkOverflow();
+    rect.Move(border_rect.offset);
   }
 
   if (FragmentVisibleToHitTestRequest(text_paint_fragment,
@@ -1093,9 +1088,8 @@
       location_in_container.Intersects(rect)) {
     Node* node = text_paint_fragment.NodeForHitTest();
     if (!result.InnerNode() && node) {
-      LayoutPoint point =
-          location_in_container.Point() - ToLayoutSize(physical_offset) +
-          text_paint_fragment.InlineOffsetToContainerBox().ToLayoutPoint();
+      PhysicalOffset point = location_in_container.Point() - physical_offset +
+                             text_paint_fragment.InlineOffsetToContainerBox();
       result.SetNodeAndPosition(node, point);
     }
 
@@ -1113,7 +1107,7 @@
     HitTestResult& result,
     const NGPaintFragment& fragment,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& physical_offset,
+    const PhysicalOffset& physical_offset,
     HitTestAction action) {
   if (HitTestChildren(result, fragment.Children(), location_in_container,
                       physical_offset, action))
@@ -1125,17 +1119,18 @@
   if (!VisibleToHitTestRequest(result.GetHitTestRequest()))
     return false;
 
-  const LayoutPoint overflow_location =
-      fragment.SelfInkOverflow().offset.ToLayoutPoint() + physical_offset;
+  const PhysicalOffset overflow_location =
+      fragment.SelfInkOverflow().offset + physical_offset;
   if (HitTestClippedOutByBorder(location_in_container, overflow_location))
     return false;
 
-  const LayoutSize size = fragment.Size().ToLayoutSize();
-  const LayoutRect bounds_rect(physical_offset, size);
+  const PhysicalSize size = fragment.Size();
+  const PhysicalRect bounds_rect(physical_offset, size);
   const ComputedStyle& containing_box_style = box_fragment_.Style();
   if (containing_box_style.HasBorderRadius() &&
       !location_in_container.Intersects(
-          containing_box_style.GetRoundedBorderFor(bounds_rect))) {
+          containing_box_style.GetRoundedBorderFor(
+              bounds_rect.ToLayoutRect()))) {
     return false;
   }
 
@@ -1145,9 +1140,9 @@
 
   Node* node = fragment.NodeForHitTest();
   if (!result.InnerNode() && node) {
-    const LayoutPoint point =
-        location_in_container.Point() - ToLayoutSize(physical_offset) +
-        fragment.InlineOffsetToContainerBox().ToLayoutPoint();
+    const PhysicalOffset point = location_in_container.Point() -
+                                 physical_offset +
+                                 fragment.InlineOffsetToContainerBox();
     result.SetNodeAndPosition(node, point);
   }
   return result.AddNodeToListBasedTestResult(node, location_in_container,
@@ -1158,7 +1153,7 @@
     HitTestResult& result,
     const NGPaintFragment& paint_fragment,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& physical_offset,
+    const PhysicalOffset& physical_offset,
     HitTestAction action) {
   const NGPhysicalFragment& fragment = paint_fragment.PhysicalFragment();
 
@@ -1185,11 +1180,10 @@
 
   LayoutBox* const layout_box = ToLayoutBox(fragment.GetMutableLayoutObject());
 
-  // To be passed as |accumulated_offset| to legacy hit test functions of
-  // LayoutBox or subclass overrides, where it isn't in any well-defined
-  // coordinate space, but only equals the difference below.
-  const LayoutPoint fallback_accumulated_offset =
-      physical_offset - ToLayoutSize(layout_box->Location());
+  // The |accumulated_offset| parameter of legacy hit testing functions doesn't
+  // include the object itself's offset.
+  const PhysicalOffset fallback_accumulated_offset =
+      physical_offset - layout_box->PhysicalLocation();
 
   // https://www.w3.org/TR/CSS22/zindex.html#painting-order
   // Hit test all phases of inline blocks, inline tables, replaced elements and
@@ -1207,7 +1201,7 @@
     HitTestResult& result,
     NGPaintFragment::ChildList children,
     const HitTestLocation& location_in_container,
-    const LayoutPoint& accumulated_offset,
+    const PhysicalOffset& accumulated_offset,
     HitTestAction action) {
   Vector<NGPaintFragment*, 16> child_vector;
   children.ToList(&child_vector);
@@ -1218,8 +1212,7 @@
       continue;
 
     const NGPhysicalFragment& fragment = child->PhysicalFragment();
-    const LayoutPoint child_physical_offset =
-        accumulated_offset + offset.ToLayoutPoint();
+    const PhysicalOffset child_physical_offset = accumulated_offset + offset;
 
     bool stop_hit_testing = false;
     if (fragment.Type() == NGPhysicalFragment::kFragmentBox) {
@@ -1253,14 +1246,13 @@
 
 bool NGBoxFragmentPainter::HitTestClippedOutByBorder(
     const HitTestLocation& location_in_container,
-    const LayoutPoint& border_box_location) const {
+    const PhysicalOffset& border_box_location) const {
   const ComputedStyle& style = box_fragment_.Style();
-  LayoutRect rect =
-      LayoutRect(LayoutPoint(), PhysicalFragment().Size().ToLayoutSize());
-  rect.MoveBy(border_box_location);
+  PhysicalRect rect(PhysicalOffset(), PhysicalFragment().Size());
+  rect.Move(border_box_location);
   const NGBorderEdges& border_edges = BorderEdges();
   return !location_in_container.Intersects(style.GetRoundedBorderFor(
-      rect, border_edges.line_left, border_edges.line_right));
+      rect.ToLayoutRect(), border_edges.line_left, border_edges.line_right));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
index 946cc95..3606414 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
@@ -39,11 +39,12 @@
                    bool suppress_box_decoration_background = false);
 
   // Hit tests this box fragment.
-  // @param physical_offset Physical offset of this box fragment in paint layer.
+  // @param physical_offset Physical offset of this box fragment in the
+  // coordinate space of |location_in_container|.
   // TODO(eae): Change to take a HitTestResult pointer instead as it mutates.
   bool NodeAtPoint(HitTestResult&,
                    const HitTestLocation& location_in_container,
-                   const LayoutPoint& physical_offset,
+                   const PhysicalOffset& physical_offset,
                    HitTestAction);
 
  protected:
@@ -103,10 +104,10 @@
   void PaintCarets(const PaintInfo&, const PhysicalOffset& paint_offset);
 
   void RecordHitTestData(const PaintInfo& paint_info,
-                         const LayoutPoint& paint_offset);
+                         const PhysicalOffset& paint_offset);
 
   void RecordHitTestDataForLine(const PaintInfo& paint_info,
-                                const LayoutPoint& paint_offset,
+                                const PhysicalOffset& paint_offset,
                                 const NGPaintFragment& line);
 
   bool IsInSelfHitTestingPhase(HitTestAction) const;
@@ -120,7 +121,7 @@
   bool HitTestChildren(HitTestResult&,
                        NGPaintFragment::ChildList,
                        const HitTestLocation& location_in_container,
-                       const LayoutPoint& physical_offset,
+                       const PhysicalOffset& physical_offset,
                        HitTestAction);
 
   // Hit tests a box fragment, which is a child of either |box_fragment_|, or
@@ -130,7 +131,7 @@
   bool HitTestChildBoxFragment(HitTestResult&,
                                const NGPaintFragment&,
                                const HitTestLocation& location_in_container,
-                               const LayoutPoint& physical_offset,
+                               const PhysicalOffset& physical_offset,
                                HitTestAction);
 
   // Hit tests the given text fragment.
@@ -138,7 +139,7 @@
   bool HitTestTextFragment(HitTestResult&,
                            const NGPaintFragment&,
                            const HitTestLocation& location_in_container,
-                           const LayoutPoint& physical_offset,
+                           const PhysicalOffset& physical_offset,
                            HitTestAction);
 
   // Hit tests the given line box fragment.
@@ -147,17 +148,14 @@
   bool HitTestLineBoxFragment(HitTestResult&,
                               const NGPaintFragment&,
                               const HitTestLocation& location_in_container,
-                              const LayoutPoint& physical_offset,
+                              const PhysicalOffset& physical_offset,
                               HitTestAction);
 
   // Returns whether the hit test location is completely outside the border box,
   // which possibly has rounded corners.
-  bool HitTestClippedOutByBorder(const HitTestLocation&,
-                                 const LayoutPoint& border_box_location) const;
-
-  LayoutPoint FlipForWritingModeForChild(
-      const NGPhysicalFragment& child_fragment,
-      const LayoutPoint& offset);
+  bool HitTestClippedOutByBorder(
+      const HitTestLocation&,
+      const PhysicalOffset& border_box_location) const;
 
   const NGPhysicalBoxFragment& PhysicalFragment() const;
   const NGBorderEdges& BorderEdges() const;
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
index fe4581b7..406d1a8e 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
@@ -133,9 +133,12 @@
   const bool should_fallback = child.PhysicalFragment().IsBlockFlow() ||
                                child.PhysicalFragment().IsLegacyLayoutRoot();
   const PositionWithAffinity result =
-      should_fallback ? child.GetLayoutObject()->PositionForPoint(
-                            child_point.ToLayoutPoint())
-                      : child.PositionForPoint(child_point);
+      should_fallback
+          ? child.GetLayoutObject()->PositionForPoint(
+                // Flip because LayoutObject::PositionForPoint() requires
+                // flipped physical coordinates.
+                child.GetLayoutObject()->FlipForWritingMode(child_point))
+          : child.PositionForPoint(child_point);
   if (result.IsNotNull())
     return result;
   return base::nullopt;
diff --git a/third_party/blink/renderer/core/paint/paint_info.h b/third_party/blink/renderer/core/paint/paint_info.h
index f5a5330..d68d5fc 100644
--- a/third_party/blink/renderer/core/paint/paint_info.h
+++ b/third_party/blink/renderer/core/paint/paint_info.h
@@ -137,6 +137,12 @@
 
   const CullRect& GetCullRect() const { return cull_rect_; }
 
+  bool IntersectsCullRect(
+      const PhysicalRect& rect,
+      const PhysicalOffset& offset = PhysicalOffset()) const {
+    return cull_rect_.Intersects(rect.ToLayoutRect(), offset.ToLayoutPoint());
+  }
+
   void ApplyInfiniteCullRect() { cull_rect_ = CullRect::Infinite(); }
 
   void TransformCullRect(const TransformPaintPropertyNode& transform) {
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index 29d5d8f..51a098b 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -399,6 +399,12 @@
     }
     return;
   }
+
+  // Scrolling affects the unclipped_absolute_bounding_box and
+  // clipped_absolute_bounding_box fields of AncestorDependentCompositingInputs
+  // for all descendants.
+  SetNeedsCompositingInputsUpdate();
+
   ClearClipRects();
   UpdateLayerPositionRecursive(AllLayers, /* dirty_compositing */ false);
 }
@@ -1795,7 +1801,7 @@
 }
 
 PaintLayer::HitTestRecursionData::HitTestRecursionData(
-    const LayoutRect& rect_arg,
+    const PhysicalRect& rect_arg,
     const HitTestLocation& location_arg,
     const HitTestLocation& original_location_arg)
     : rect(rect_arg),
@@ -1805,7 +1811,7 @@
 
 bool PaintLayer::HitTest(const HitTestLocation& hit_test_location,
                          HitTestResult& result,
-                         const LayoutRect& hit_test_area) {
+                         const PhysicalRect& hit_test_area) {
   DCHECK(IsSelfPaintingLayer() || HasSelfPaintingLayerDescendant());
 
   // LayoutView should make sure to update layout before entering hit testing
@@ -1839,9 +1845,7 @@
       fallback = true;
     }
     if (fallback) {
-      GetLayoutObject().UpdateHitTestResult(
-          result, ToLayoutView(GetLayoutObject())
-                      .DeprecatedFlipForWritingMode(hit_test_location.Point()));
+      GetLayoutObject().UpdateHitTestResult(result, hit_test_location.Point());
       inside_layer = this;
 
       // Don't cache this result since it really wasn't a true hit.
@@ -1898,7 +1902,7 @@
     PaintLayer* container_layer,
     const HitTestRecursionData& recursion_data,
     const HitTestingTransformState* container_transform_state,
-    const LayoutPoint& translation_offset) const {
+    const PhysicalOffset& translation_offset) const {
   // If we're already computing transform state, then it's relative to the
   // container (which we know is non-null).
   // If this is the first time we need to make transform state, then base it
@@ -1916,7 +1920,7 @@
   else
     ConvertToLayerCoords(root_layer, offset);
 
-  offset += PhysicalOffsetToBeNoop(translation_offset);
+  offset += translation_offset;
 
   LayoutObject* container_layout_object =
       container_layer ? &container_layer->GetLayoutObject() : nullptr;
@@ -2129,7 +2133,7 @@
   // Collect the fragments. This will compute the clip rectangles for each
   // layer fragment.
   base::Optional<PaintLayerFragments> layer_fragments;
-  LayoutPoint offset;
+  PhysicalOffset offset;
   if (recursion_data.intersects_location) {
     layer_fragments.emplace();
     if (applied_transform) {
@@ -2154,7 +2158,7 @@
     // Next we want to see if the mouse pos is inside the child LayoutObjects of
     // the layer. Check every fragment in reverse order.
     if (IsSelfPaintingLayer()) {
-      offset = -LayoutBoxLocation();
+      offset = -LayoutBoxPhysicalLocation();
       // Hit test with a temporary HitTestResult, because we only want to commit
       // to 'result' if we know we're frontmost.
       HitTestResult temp_result(result.GetHitTestRequest(),
@@ -2223,7 +2227,7 @@
 
 bool PaintLayer::HitTestContentsForFragments(
     const PaintLayerFragments& layer_fragments,
-    const LayoutPoint& offset,
+    const PhysicalOffset& offset,
     HitTestResult& result,
     const HitTestLocation& hit_test_location,
     HitTestFilter hit_test_filter,
@@ -2239,8 +2243,8 @@
          !fragment.foreground_rect.Intersects(hit_test_location)))
       continue;
     inside_clip_rect = true;
-    LayoutPoint fragment_offset = offset;
-    fragment_offset.MoveBy(fragment.layer_bounds.offset.ToLayoutPoint());
+    PhysicalOffset fragment_offset = offset;
+    fragment_offset += fragment.layer_bounds.offset;
     if (HitTestContents(result, fragment_offset, hit_test_location,
                         hit_test_filter))
       return true;
@@ -2269,12 +2273,12 @@
     // Apply the page/column clip for this fragment, as well as any clips
     // established by layers in between us and the enclosing pagination layer.
     PhysicalRect clip_rect = fragment.background_rect.Rect();
-    if (!recursion_data.location.Intersects(clip_rect.ToLayoutRect()))
+    if (!recursion_data.location.Intersects(clip_rect))
       continue;
 
     PaintLayer* hit_layer = HitTestLayerByApplyingTransform(
         root_layer, container_layer, result, recursion_data, transform_state,
-        z_offset, fragment.fragment_data->PaginationOffset().ToLayoutPoint());
+        z_offset, fragment.fragment_data->PaginationOffset());
     if (hit_layer)
       return hit_layer;
   }
@@ -2289,7 +2293,7 @@
     const HitTestRecursionData& recursion_data,
     HitTestingTransformState* transform_state,
     double* z_offset,
-    const LayoutPoint& translation_offset) {
+    const PhysicalOffset& translation_offset) {
   // Create a transform state to accumulate this transform.
   HitTestingTransformState new_transform_state =
       CreateLocalTransformState(root_layer, container_layer, recursion_data,
@@ -2308,7 +2312,7 @@
   // been flattened (losing z) by our container.
   FloatPoint local_point = new_transform_state.MappedPoint();
   FloatQuad local_point_quad = new_transform_state.MappedQuad();
-  LayoutRect bounds_of_mapped_area = new_transform_state.BoundsOfMappedArea();
+  PhysicalRect bounds_of_mapped_area = new_transform_state.BoundsOfMappedArea();
   base::Optional<HitTestLocation> new_location;
   if (recursion_data.location.IsRectBasedTest())
     new_location.emplace(local_point, local_point_quad);
@@ -2323,7 +2327,7 @@
 }
 
 bool PaintLayer::HitTestContents(HitTestResult& result,
-                                 const LayoutPoint& fragment_offset,
+                                 const PhysicalOffset& fragment_offset,
                                  const HitTestLocation& hit_test_location,
                                  HitTestFilter hit_test_filter) const {
   DCHECK(IsSelfPaintingLayer() || HasSelfPaintingLayerDescendant());
@@ -2513,7 +2517,7 @@
   else
     ConvertToLayerCoords(root_layer, origin);
 
-  FloatPoint point(hit_test_location.Point() - origin.offset.ToLayoutPoint());
+  FloatPoint point(hit_test_location.Point() - origin.offset);
   FloatRect reference_box(
       ClipPathClipper::LocalReferenceBox(GetLayoutObject()));
 
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h
index d070784..c864d18 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.h
+++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -426,7 +426,7 @@
   // LayoutObject to consider for hit testing.
   bool HitTest(const HitTestLocation& location,
                HitTestResult&,
-               const LayoutRect& hit_test_area);
+               const PhysicalRect& hit_test_area);
 
   bool IntersectsDamageRect(const PhysicalRect& layer_bounds,
                             const PhysicalRect& damage_rect,
@@ -955,9 +955,10 @@
       const PhysicalOffset* offset_from_root = nullptr,
       const PhysicalOffset& sub_pixel_accumulation = PhysicalOffset()) const;
 
-  LayoutPoint LayoutBoxLocation() const {
-    return GetLayoutObject().IsBox() ? ToLayoutBox(GetLayoutObject()).Location()
-                                     : LayoutPoint();
+  PhysicalOffset LayoutBoxPhysicalLocation() const {
+    return GetLayoutObject().IsBox()
+               ? ToLayoutBox(GetLayoutObject()).PhysicalLocation()
+               : PhysicalOffset();
   }
 
   enum TransparencyClipBoxBehavior {
@@ -1123,12 +1124,12 @@
   void UpdateHasSelfPaintingLayerDescendant() const;
 
   struct HitTestRecursionData {
-    const LayoutRect& rect;
+    const PhysicalRect& rect;
     // Whether location.Intersects(rect) returns true.
     const HitTestLocation& location;
     const HitTestLocation& original_location;
     const bool intersects_location;
-    HitTestRecursionData(const LayoutRect& rect_arg,
+    HitTestRecursionData(const PhysicalRect& rect_arg,
                          const HitTestLocation& location_arg,
                          const HitTestLocation& original_location_arg);
   };
@@ -1147,7 +1148,7 @@
       const HitTestRecursionData& recursion_data,
       HitTestingTransformState* = nullptr,
       double* z_offset = nullptr,
-      const LayoutPoint& translation_offset = LayoutPoint());
+      const PhysicalOffset& translation_offset = PhysicalOffset());
   PaintLayer* HitTestChildren(
       ChildrenIteration,
       PaintLayer* root_layer,
@@ -1164,14 +1165,14 @@
       PaintLayer* container_layer,
       const HitTestRecursionData& recursion_data,
       const HitTestingTransformState* container_transform_state,
-      const LayoutPoint& translation_offset = LayoutPoint()) const;
+      const PhysicalOffset& translation_offset = PhysicalOffset()) const;
 
   bool HitTestContents(HitTestResult&,
-                       const LayoutPoint& fragment_offset,
+                       const PhysicalOffset& fragment_offset,
                        const HitTestLocation&,
                        HitTestFilter) const;
   bool HitTestContentsForFragments(const PaintLayerFragments&,
-                                   const LayoutPoint& offset,
+                                   const PhysicalOffset& offset,
                                    HitTestResult&,
                                    const HitTestLocation&,
                                    HitTestFilter,
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index e3d58f8..0050a7d 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -112,14 +112,6 @@
 
 }  // namespace
 
-static LayoutRect LocalToAbsolute(const LayoutBox& box, LayoutRect rect) {
-  return box.LocalToAbsoluteRect(PhysicalRectToBeNoop(rect)).ToLayoutRect();
-}
-
-static LayoutRect AbsoluteToLocal(const LayoutBox& box, LayoutRect rect) {
-  return box.AbsoluteToLocalRect(PhysicalRectToBeNoop(rect)).ToLayoutRect();
-}
-
 PaintLayerScrollableAreaRareData::PaintLayerScrollableAreaRareData() = default;
 
 const int kResizerControlExpandRatioForTouch = 2;
@@ -610,7 +602,7 @@
   ShowOverlayScrollbars();
 }
 
-LayoutRect PaintLayerScrollableArea::LayoutContentRect(
+PhysicalRect PaintLayerScrollableArea::LayoutContentRect(
     IncludeScrollbarsInRect scrollbar_inclusion) const {
   // LayoutContentRect is conceptually the same as the box's client rect.
   LayoutSize layer_size(Layer()->Size());
@@ -628,29 +620,29 @@
             : 0);
   }
 
-  return LayoutRect(
-      LayoutPoint(ScrollPosition()),
-      LayoutSize(
-          layer_size.Width() - border_width - vertical_scrollbar_width,
-          layer_size.Height() - border_height - horizontal_scrollbar_height)
-          .ExpandedTo(LayoutSize()));
+  PhysicalSize size(
+      layer_size.Width() - border_width - vertical_scrollbar_width,
+      layer_size.Height() - border_height - horizontal_scrollbar_height);
+  size.ClampNegativeToZero();
+  return PhysicalRect(PhysicalOffset::FromFloatPointRound(ScrollPosition()),
+                      size);
 }
 
 IntRect PaintLayerScrollableArea::VisibleContentRect(
     IncludeScrollbarsInRect scrollbar_inclusion) const {
-  LayoutRect layout_content_rect(LayoutContentRect(scrollbar_inclusion));
+  PhysicalRect layout_content_rect(LayoutContentRect(scrollbar_inclusion));
   // TODO(szager): It's not clear that Floor() is the right thing to do here;
   // what is the correct behavior for fractional scroll offsets?
-  return IntRect(FlooredIntPoint(layout_content_rect.Location()),
-                 PixelSnappedIntSize(layout_content_rect.Size(),
+  return IntRect(FlooredIntPoint(layout_content_rect.offset),
+                 PixelSnappedIntSize(layout_content_rect.size.ToLayoutSize(),
                                      GetLayoutBox()->Location()));
 }
 
-LayoutRect PaintLayerScrollableArea::VisibleScrollSnapportRect(
+PhysicalRect PaintLayerScrollableArea::VisibleScrollSnapportRect(
     IncludeScrollbarsInRect scrollbar_inclusion) const {
   const ComputedStyle* style = GetLayoutBox()->Style();
-  LayoutRect layout_content_rect(LayoutContentRect(scrollbar_inclusion));
-  layout_content_rect.MoveBy(LayoutPoint(-ScrollOrigin()));
+  PhysicalRect layout_content_rect(LayoutContentRect(scrollbar_inclusion));
+  layout_content_rect.Move(PhysicalOffset(-ScrollOrigin()));
   LayoutRectOutsets padding(MinimumValueForLength(style->ScrollPaddingTop(),
                                                   layout_content_rect.Height()),
                             MinimumValueForLength(style->ScrollPaddingRight(),
@@ -673,7 +665,7 @@
 
 IntSize PaintLayerScrollableArea::PixelSnappedContentsSize(
     const PhysicalOffset& paint_offset) const {
-  return PixelSnappedIntRect(PhysicalRect(paint_offset, overflow_rect_.Size()))
+  return PixelSnappedIntRect(PhysicalRect(paint_offset, overflow_rect_.size))
       .Size();
 }
 
@@ -841,12 +833,12 @@
 void PaintLayerScrollableArea::UpdateScrollOrigin() {
   // This should do nothing prior to first layout; the if-clause will catch
   // that.
-  if (OverflowRect().IsEmpty())
+  if (overflow_rect_.IsEmpty())
     return;
-  LayoutRect scrollable_overflow(overflow_rect_);
-  scrollable_overflow.Move(-GetLayoutBox()->BorderLeft(),
-                           -GetLayoutBox()->BorderTop());
-  IntPoint new_origin(FlooredIntPoint(-scrollable_overflow.Location()) +
+  PhysicalRect scrollable_overflow = overflow_rect_;
+  scrollable_overflow.Move(-PhysicalOffset(GetLayoutBox()->BorderLeft(),
+                                           GetLayoutBox()->BorderTop()));
+  IntPoint new_origin(FlooredIntPoint(-scrollable_overflow.offset) +
                       GetLayoutBox()->OriginAdjustmentForScrollbars());
   if (new_origin != scroll_origin_)
     scroll_origin_changed_ = true;
@@ -854,17 +846,15 @@
 }
 
 void PaintLayerScrollableArea::UpdateScrollDimensions() {
-  LayoutRect new_overflow_rect = GetLayoutBox()->LayoutOverflowRect();
-  GetLayoutBox()->DeprecatedFlipForWritingMode(new_overflow_rect);
+  PhysicalRect new_overflow_rect = GetLayoutBox()->PhysicalLayoutOverflowRect();
 
   // The layout viewport can be larger than the document's layout overflow when
   // top controls are hidden.  Expand the overflow here to ensure that our
   // contents size >= visible size.
-  new_overflow_rect.Unite(
-      LayoutRect(new_overflow_rect.Location(),
-                 LayoutContentRect(kExcludeScrollbars).Size()));
+  new_overflow_rect.Unite(PhysicalRect(
+      new_overflow_rect.offset, LayoutContentRect(kExcludeScrollbars).size));
 
-  if (overflow_rect_.Size() != new_overflow_rect.Size())
+  if (overflow_rect_.size != new_overflow_rect.size)
     ContentsResized();
   overflow_rect_ = new_overflow_rect;
   UpdateScrollOrigin();
@@ -1161,7 +1151,7 @@
 void PaintLayerScrollableArea::UpdateAfterStyleChange(
     const ComputedStyle* old_style) {
   // Don't do this on first style recalc, before layout has ever happened.
-  if (!OverflowRect().Size().IsZero()) {
+  if (!overflow_rect_.size.IsZero()) {
     UpdateScrollableAreaSet();
   }
 
@@ -1519,10 +1509,10 @@
         !GetLayoutBox()->HasAutoHorizontalScrollbar())
       return false;
 
-    LayoutSize client_size_with_scrollbars =
-        LayoutContentRect(kIncludeScrollbars).Size();
-    if (ScrollWidth() <= client_size_with_scrollbars.Width() &&
-        ScrollHeight() <= client_size_with_scrollbars.Height()) {
+    PhysicalSize client_size_with_scrollbars =
+        LayoutContentRect(kIncludeScrollbars).size;
+    if (ScrollWidth() <= client_size_with_scrollbars.width &&
+        ScrollHeight() <= client_size_with_scrollbars.height) {
       return true;
     }
   }
@@ -2036,17 +2026,18 @@
   // keep the point under the cursor in view.
 }
 
-LayoutRect PaintLayerScrollableArea::ScrollIntoView(
-    const LayoutRect& absolute_rect,
+PhysicalRect PaintLayerScrollableArea::ScrollIntoView(
+    const PhysicalRect& absolute_rect,
     const WebScrollIntoViewParams& params) {
-  LayoutRect local_expose_rect =
-      AbsoluteToLocal(*GetLayoutBox(), absolute_rect);
-  LayoutSize border_origin_to_scroll_origin =
-      LayoutSize(-GetLayoutBox()->BorderLeft(), -GetLayoutBox()->BorderTop()) +
-      LayoutSize(GetScrollOffset());
+  PhysicalRect local_expose_rect =
+      GetLayoutBox()->AbsoluteToLocalRect(absolute_rect);
+  PhysicalOffset border_origin_to_scroll_origin(-GetLayoutBox()->BorderLeft(),
+                                                -GetLayoutBox()->BorderTop());
+  border_origin_to_scroll_origin +=
+      PhysicalOffset::FromFloatSizeRound(GetScrollOffset());
   // Represent the rect in the container's scroll-origin coordinate.
   local_expose_rect.Move(border_origin_to_scroll_origin);
-  LayoutRect scroll_snapport_rect = VisibleScrollSnapportRect();
+  PhysicalRect scroll_snapport_rect = VisibleScrollSnapportRect();
 
   ScrollOffset target_offset = ScrollAlignment::GetScrollOffsetToExpose(
       scroll_snapport_rect, local_expose_rect, params.GetScrollAlignmentX(),
@@ -2090,18 +2081,20 @@
   // The container hasn't performed the scroll yet if it's for scroll sequence.
   // To calculate the result from the scroll, we move the |local_expose_rect| to
   // the will-be-scrolled location.
-  local_expose_rect.Move(-LayoutSize(scroll_offset_difference));
+  local_expose_rect.Move(
+      -PhysicalOffset::FromFloatSizeRound(scroll_offset_difference));
 
   // Represent the rects in the container's border-box coordinate.
   local_expose_rect.Move(-border_origin_to_scroll_origin);
   scroll_snapport_rect.Move(-border_origin_to_scroll_origin);
-  LayoutRect intersect = Intersection(scroll_snapport_rect, local_expose_rect);
+  PhysicalRect intersect =
+      Intersection(scroll_snapport_rect, local_expose_rect);
 
   if (intersect.IsEmpty() && !scroll_snapport_rect.IsEmpty() &&
       !local_expose_rect.IsEmpty()) {
-    return LocalToAbsolute(*GetLayoutBox(), local_expose_rect);
+    return GetLayoutBox()->LocalToAbsoluteRect(local_expose_rect);
   }
-  intersect = LocalToAbsolute(*GetLayoutBox(), intersect);
+  intersect = GetLayoutBox()->LocalToAbsoluteRect(intersect);
   return intersect;
 }
 
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
index ddd09a9..b9b0205c 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
@@ -315,7 +315,7 @@
   IntSize MaximumScrollOffsetInt() const override;
   IntRect VisibleContentRect(
       IncludeScrollbarsInRect = kExcludeScrollbars) const override;
-  LayoutRect VisibleScrollSnapportRect(
+  PhysicalRect VisibleScrollSnapportRect(
       IncludeScrollbarsInRect = kExcludeScrollbars) const override;
   IntSize ContentsSize() const override;
 
@@ -347,9 +347,6 @@
   IntPoint ScrollOrigin() const { return scroll_origin_; }
   bool ScrollOriginChanged() const { return scroll_origin_changed_; }
 
-  // FIXME: We shouldn't allow access to m_overflowRect outside this class.
-  LayoutRect OverflowRect() const { return overflow_rect_; }
-
   void ScrollToAbsolutePosition(
       const FloatPoint& position,
       ScrollBehavior scroll_behavior = kScrollBehaviorInstant,
@@ -425,8 +422,8 @@
 
   // Returns the new offset, after scrolling, of the given rect in absolute
   // coordinates, clipped by the parent's client rect.
-  LayoutRect ScrollIntoView(const LayoutRect&,
-                            const WebScrollIntoViewParams&) override;
+  PhysicalRect ScrollIntoView(const PhysicalRect&,
+                              const WebScrollIntoViewParams&) override;
 
   // Returns true if scrollable area is in the FrameView's collection of
   // scrollable areas. This can only happen if we're scrollable, visible to hit
@@ -592,7 +589,7 @@
 
   void UpdateScrollCornerStyle();
   LayoutSize MinimumSizeForResizing(float zoom_factor);
-  LayoutRect LayoutContentRect(IncludeScrollbarsInRect) const;
+  PhysicalRect LayoutContentRect(IncludeScrollbarsInRect) const;
 
   // See comments on isPointInResizeControl.
   void UpdateResizerAreaSet();
@@ -673,7 +670,7 @@
   // This is OverflowModel's layout overflow translated to physical
   // coordinates. See OverflowModel for the different overflow and
   // LayoutBoxModelObject for the coordinate systems.
-  LayoutRect overflow_rect_;
+  PhysicalRect overflow_rect_;
 
   // ScrollbarManager holds the Scrollbar instances.
   ScrollbarManager scrollbar_manager_;
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
index 898e2475..bdf0e26 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
@@ -983,22 +983,22 @@
   scrollable_area->SetScrollbarsHiddenIfOverlay(true);
 
   HitTestRequest hit_request(HitTestRequest::kMove | HitTestRequest::kReadOnly);
-  HitTestLocation location(LayoutPoint(95, 5));
+  HitTestLocation location(PhysicalOffset(95, 5));
   HitTestResult hit_result(hit_request, location);
   GetDocument().GetLayoutView()->HitTest(location, hit_result);
   EXPECT_EQ(hit_result.GetScrollbar(), nullptr);
-  location = HitTestLocation(LayoutPoint(5, 95));
+  location = HitTestLocation(PhysicalOffset(5, 95));
   hit_result = HitTestResult(hit_request, location);
   GetDocument().GetLayoutView()->HitTest(location, hit_result);
   EXPECT_EQ(hit_result.GetScrollbar(), nullptr);
 
   scrollable_area->SetScrollbarsHiddenIfOverlay(false);
 
-  location = HitTestLocation(LayoutPoint(95, 5));
+  location = HitTestLocation(PhysicalOffset(95, 5));
   hit_result = HitTestResult(hit_request, location);
   GetDocument().GetLayoutView()->HitTest(location, hit_result);
   EXPECT_EQ(hit_result.GetScrollbar(), scrollable_area->VerticalScrollbar());
-  location = HitTestLocation(LayoutPoint(5, 95));
+  location = HitTestLocation(PhysicalOffset(5, 95));
   hit_result = HitTestResult(hit_request, location);
   GetDocument().GetLayoutView()->HitTest(location, hit_result);
   EXPECT_EQ(hit_result.GetScrollbar(), scrollable_area->HorizontalScrollbar());
diff --git a/third_party/blink/renderer/core/paint/paint_layer_test.cc b/third_party/blink/renderer/core/paint/paint_layer_test.cc
index cce4f67..386a489 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_test.cc
@@ -1610,7 +1610,7 @@
 
   // Regular hit test over 'child'
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestLocation location((LayoutPoint(50, 25)));
+  HitTestLocation location((PhysicalOffset(50, 25)));
   HitTestResult result(request, location);
   GetDocument().GetLayoutView()->HitTest(location, result);
   EXPECT_EQ(child, result.InnerNode());
@@ -1624,7 +1624,7 @@
 
   // Regular hit test over 'overlap'
   request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  location = HitTestLocation((LayoutPoint(50, 75)));
+  location = HitTestLocation((PhysicalOffset(50, 75)));
   result = HitTestResult(request, location);
   GetDocument().GetLayoutView()->HitTest(location, result);
   EXPECT_EQ(overlap, result.InnerNode());
@@ -1641,7 +1641,7 @@
   request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive |
                                HitTestRequest::kListBased,
                            hit->GetLayoutObject());
-  location = HitTestLocation((LayoutRect(40, 15, 20, 20)));
+  location = HitTestLocation((PhysicalRect(40, 15, 20, 20)));
   result = HitTestResult(request, location);
   GetDocument().GetLayoutView()->HitTest(location, result);
   EXPECT_EQ(1u, result.ListBasedTestResult().size());
@@ -1670,7 +1670,7 @@
   Element* table = GetDocument().getElementById("table");
   Element* cell11 = GetDocument().getElementById("cell11");
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestLocation location((LayoutPoint(50, 50)));
+  HitTestLocation location((PhysicalOffset(50, 50)));
   HitTestResult result(request, location);
   GetDocument().GetLayoutView()->HitTest(location, result);
   EXPECT_EQ(cell11, result.InnerNode());
@@ -1691,7 +1691,7 @@
   Element* svg = GetDocument().getElementById("svg");
   Element* circle = GetDocument().getElementById("circle");
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestLocation location((LayoutPoint(50, 50)));
+  HitTestLocation location((PhysicalOffset(50, 50)));
   HitTestResult result(request, location);
   GetDocument().GetLayoutView()->HitTest(location, result);
   EXPECT_EQ(circle, result.InnerNode());
@@ -1744,7 +1744,7 @@
   )HTML");
   Element* target = GetDocument().getElementById("target");
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestLocation location(LayoutPoint(10, 10));
+  HitTestLocation location(PhysicalOffset(10, 10));
   HitTestResult result(request, location);
   GetDocument().GetLayoutView()->HitTest(location, result);
   EXPECT_EQ(target, result.InnerNode());
@@ -1768,7 +1768,7 @@
   Element* target = GetDocument().getElementById("target");
   Element* container = GetDocument().getElementById("container");
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestLocation location(LayoutPoint(10, 10));
+  HitTestLocation location(PhysicalOffset(10, 10));
   HitTestResult result(request, location);
   GetDocument().GetLayoutView()->HitTest(location, result);
   EXPECT_EQ(target, result.InnerNode());
@@ -1793,7 +1793,7 @@
   Element* target = GetDocument().getElementById("target");
   Element* container = GetDocument().getElementById("container");
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestLocation location(LayoutPoint(10, 10));
+  HitTestLocation location(PhysicalOffset(10, 10));
   HitTestResult result(request, location);
   GetDocument().GetLayoutView()->HitTest(location, result);
   EXPECT_EQ(target, result.InnerNode());
@@ -1819,7 +1819,7 @@
   )HTML");
   Node* target = GetDocument().getElementById("target")->firstChild();
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestLocation location(LayoutPoint(55, 5));  // At the center of "bar"
+  HitTestLocation location(PhysicalOffset(55, 5));  // At the center of "bar"
   HitTestResult result(request, location);
   GetDocument().GetLayoutView()->HitTest(location, result);
   EXPECT_EQ(target, result.InnerNode());
@@ -1842,7 +1842,7 @@
   Element* target = GetDocument().getElementById("target");
   Element* container = GetDocument().getElementById("container");
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestLocation location(LayoutPoint(10, 10));
+  HitTestLocation location(PhysicalOffset(10, 10));
   HitTestResult result(request, location);
   GetDocument().GetLayoutView()->HitTest(location, result);
   EXPECT_EQ(target, result.InnerNode());
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index e064ff1..49664ab 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -108,9 +108,9 @@
       inflated_expected.Inflate(LayoutUnit(slop_factor));                      \
       SCOPED_TRACE(String::Format(                                             \
           "Slow path rect: %s, Expected: %s, Inflated expected: %s",           \
-          slow_path_rect.ToString().Ascii().data(),                            \
-          expected.ToString().Ascii().data(),                                  \
-          inflated_expected.ToString().Ascii().data()));                       \
+          slow_path_rect.ToString().Ascii().c_str(),                           \
+          expected.ToString().Ascii().c_str(),                                 \
+          inflated_expected.ToString().Ascii().c_str()));                      \
       EXPECT_TRUE(                                                             \
           PhysicalRect(EnclosingIntRect(slow_path_rect)).Contains(expected));  \
       EXPECT_TRUE(inflated_expected.Contains(slow_path_rect));                 \
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_printer_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_printer_test.cc
index 6eda864..4861e3b 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_printer_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_printer_test.cc
@@ -33,7 +33,7 @@
   SetBodyInnerHTML("hello world");
   String transform_tree_as_string =
       transformPropertyTreeAsString(*GetDocument().View());
-  EXPECT_THAT(transform_tree_as_string.Ascii().data(),
+  EXPECT_THAT(transform_tree_as_string.Ascii(),
               testing::MatchesRegex("root .*"
                                     "  .*Translation \\(.*\\) .*"));
 }
@@ -41,7 +41,7 @@
 TEST_P(PaintPropertyTreePrinterTest, SimpleClipTree) {
   SetBodyInnerHTML("hello world");
   String clip_tree_as_string = clipPropertyTreeAsString(*GetDocument().View());
-  EXPECT_THAT(clip_tree_as_string.Ascii().data(),
+  EXPECT_THAT(clip_tree_as_string.Ascii().c_str(),
               testing::MatchesRegex("root .*"
                                     "  .*Clip \\(.*\\) .*"));
 }
@@ -51,7 +51,7 @@
   String effect_tree_as_string =
       effectPropertyTreeAsString(*GetDocument().View());
   EXPECT_THAT(
-      effect_tree_as_string.Ascii().data(),
+      effect_tree_as_string.Ascii().c_str(),
       testing::MatchesRegex("root .*"
                             "  Effect \\(LayoutN?G?BlockFlow DIV\\) .*"));
 }
@@ -60,7 +60,7 @@
   SetBodyInnerHTML("<div style='height: 4000px;'>hello world</div>");
   String scroll_tree_as_string =
       scrollPropertyTreeAsString(*GetDocument().View());
-  EXPECT_THAT(scroll_tree_as_string.Ascii().data(),
+  EXPECT_THAT(scroll_tree_as_string.Ascii().c_str(),
               testing::MatchesRegex("root .*"
                                     "  Scroll \\(.*\\) .*"));
 }
@@ -75,7 +75,7 @@
       transformed_object->FirstFragment().PaintProperties();
   String transform_path_as_string =
       transformed_object_properties->Transform()->ToTreeString();
-  EXPECT_THAT(transform_path_as_string.Ascii().data(),
+  EXPECT_THAT(transform_path_as_string.Ascii().c_str(),
               testing::MatchesRegex("root .*\"scroll\".*"
                                     "  .*\"parent\".*"
                                     "    .*\"translation2d\".*"
@@ -92,7 +92,7 @@
       clipped_object->FirstFragment().PaintProperties();
   String clip_path_as_string =
       clipped_object_properties->CssClip()->ToTreeString();
-  EXPECT_THAT(clip_path_as_string.Ascii().data(),
+  EXPECT_THAT(clip_path_as_string.Ascii().c_str(),
               testing::MatchesRegex("root .*\"rect\".*"
                                     "  .*\"rect\".*"
                                     "    .*\"rect\".*"));
@@ -106,7 +106,7 @@
       effect_object->FirstFragment().PaintProperties();
   String effect_path_as_string =
       effect_object_properties->Effect()->ToTreeString();
-  EXPECT_THAT(effect_path_as_string.Ascii().data(),
+  EXPECT_THAT(effect_path_as_string.Ascii().c_str(),
               testing::MatchesRegex("root .*\"outputClip\".*"
                                     "  .*\"parent\".*\"opacity\".*"));
 }
@@ -124,7 +124,7 @@
   String scroll_path_as_string = scroll_object_properties->ScrollTranslation()
                                      ->ScrollNode()
                                      ->ToTreeString();
-  EXPECT_THAT(scroll_path_as_string.Ascii().data(),
+  EXPECT_THAT(scroll_path_as_string.Ascii().c_str(),
               testing::MatchesRegex("root .* \\{\\}.*"
                                     "  .*\"parent\".*"));
 }
diff --git a/third_party/blink/renderer/core/paint/table_section_painter.cc b/third_party/blink/renderer/core/paint/table_section_painter.cc
index f4fc4c8..6641f3e 100644
--- a/third_party/blink/renderer/core/paint/table_section_painter.cc
+++ b/third_party/blink/renderer/core/paint/table_section_painter.cc
@@ -119,7 +119,7 @@
 
   LayoutRect table_aligned_rect =
       layout_table_section_.LogicalRectForWritingModeAndDirection(
-          local_cull_rect.ToLayoutRect());
+          local_cull_rect);
   return table_aligned_rect;
 }
 
diff --git a/third_party/blink/renderer/core/paint/text_element_timing.cc b/third_party/blink/renderer/core/paint/text_element_timing.cc
index 68db930d..99fed4b 100644
--- a/third_party/blink/renderer/core/paint/text_element_timing.cc
+++ b/third_party/blink/renderer/core/paint/text_element_timing.cc
@@ -5,7 +5,9 @@
 #include "third_party/blink/renderer/core/paint/text_element_timing.h"
 
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/paint/text_paint_timing_detector.h"
+#include "third_party/blink/renderer/core/timing/dom_window_performance.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
@@ -25,18 +27,46 @@
 }
 
 TextElementTiming::TextElementTiming(LocalDOMWindow& window)
-    : Supplement<LocalDOMWindow>(window) {
+    : Supplement<LocalDOMWindow>(window),
+      performance_(DOMWindowPerformance::performance(window)) {
   DCHECK(RuntimeEnabledFeatures::ElementTimingEnabled(
       GetSupplementable()->document()));
 }
 
 void TextElementTiming::OnTextNodesPainted(
     const Deque<base::WeakPtr<TextRecord>>& text_nodes_painted) {
-  // TODO(npm): implement entry creation.
+  DCHECK(performance_);
+  // If the entries cannot be exposed via PerformanceObserver nor added to the
+  // buffer, bail out.
+  if (!performance_->HasObserverFor(PerformanceEntry::kElement) &&
+      performance_->IsElementTimingBufferFull()) {
+    return;
+  }
+  for (const auto record : text_nodes_painted) {
+    Node* node = DOMNodeIds::NodeForId(record->node_id);
+    if (!node || node->IsInShadowTree())
+      continue;
+
+    // Text aggregators should be Elements!
+    DCHECK(node->IsElementNode());
+    Element* element = ToElement(node);
+    const AtomicString& attr =
+        element->FastGetAttribute(html_names::kElementtimingAttr);
+    if (attr.IsEmpty())
+      continue;
+
+    const AtomicString& id = element->GetIdAttribute();
+    DEFINE_STATIC_LOCAL(const AtomicString, kTextPaint, ("text-paint"));
+    // TODO(npm): Add the rect once these are stored in TextRecord.
+    performance_->AddElementTiming(kTextPaint, g_empty_string, FloatRect(),
+                                   record->paint_time, TimeTicks(), attr,
+                                   IntSize(), id, element);
+  }
 }
 
 void TextElementTiming::Trace(blink::Visitor* visitor) {
   Supplement<LocalDOMWindow>::Trace(visitor);
+  visitor->Trace(performance_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/text_element_timing.h b/third_party/blink/renderer/core/paint/text_element_timing.h
index 70b9e017..741504c 100644
--- a/third_party/blink/renderer/core/paint/text_element_timing.h
+++ b/third_party/blink/renderer/core/paint/text_element_timing.h
@@ -7,6 +7,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/timing/window_performance.h"
 #include "third_party/blink/renderer/platform/supplementable.h"
 #include "third_party/blink/renderer/platform/wtf/deque.h"
 
@@ -34,6 +35,8 @@
 
   void Trace(blink::Visitor* visitor) override;
 
+  Member<WindowPerformance> performance_;
+
   DISALLOW_COPY_AND_ASSIGN(TextElementTiming);
 };
 
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
index cc6f96a..b03352e 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
@@ -44,14 +44,7 @@
       timer_(frame_view->GetFrame().GetTaskRunner(TaskType::kInternalDefault),
              this,
              &TextPaintTimingDetector::TimerFired),
-      frame_view_(frame_view) {
-  Document* document = frame_view_->GetFrame().GetDocument();
-  if (document && RuntimeEnabledFeatures::ElementTimingEnabled(document)) {
-    LocalDOMWindow* window = document->domWindow();
-    if (window)
-      records_manager_.SetTextElementTiming(&TextElementTiming::From(*window));
-  }
-}
+      frame_view_(frame_view) {}
 
 void TextPaintTimingDetector::PopulateTraceValue(
     TracedValue& value,
@@ -160,6 +153,16 @@
 
 void TextPaintTimingDetector::ReportSwapTime(WebWidgetClient::SwapResult result,
                                              base::TimeTicks timestamp) {
+  if (!records_manager_.HasTextElementTiming()) {
+    Document* document = frame_view_->GetFrame().GetDocument();
+    if (document && RuntimeEnabledFeatures::ElementTimingEnabled(document)) {
+      LocalDOMWindow* window = document->domWindow();
+      if (window) {
+        records_manager_.SetTextElementTiming(
+            &TextElementTiming::From(*window));
+      }
+    }
+  }
   records_manager_.AssignPaintTimeToQueuedNodes(timestamp);
   awaiting_swap_promise_ = false;
 }
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
index 1052a83..4b9c0417 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
@@ -79,6 +79,7 @@
   void StopRecordingLargestTextPaint();
   bool IsRecordingLargestTextPaint() const { return is_recording_ltp_; }
 
+  bool HasTextElementTiming() const { return !!text_element_timing_; }
   void SetTextElementTiming(TextElementTiming* text_element_timing) {
     text_element_timing_ = text_element_timing;
   }
@@ -105,7 +106,7 @@
   TextRecordSet size_ordered_set_;
   Deque<base::WeakPtr<TextRecord>> texts_queued_for_paint_time_;
   TextRecord* cached_largest_paint_candidate_;
-  WeakMember<TextElementTiming> text_element_timing_;
+  Member<TextElementTiming> text_element_timing_;
 
   DISALLOW_COPY_AND_ASSIGN(TextRecordsManager);
 };
diff --git a/third_party/blink/renderer/core/scroll/scroll_alignment.cc b/third_party/blink/renderer/core/scroll/scroll_alignment.cc
index cc195a28..504efa93 100644
--- a/third_party/blink/renderer/core/scroll/scroll_alignment.cc
+++ b/third_party/blink/renderer/core/scroll/scroll_alignment.cc
@@ -43,7 +43,7 @@
 
 #include "third_party/blink/renderer/core/scroll/scroll_alignment.h"
 
-#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
 
 namespace blink {
 
@@ -67,13 +67,13 @@
 #define MIN_INTERSECT_FOR_REVEAL 32
 
 ScrollOffset ScrollAlignment::GetScrollOffsetToExpose(
-    const LayoutRect& scroll_snapport_rect,
-    const LayoutRect& expose_rect,
+    const PhysicalRect& scroll_snapport_rect,
+    const PhysicalRect& expose_rect,
     const ScrollAlignment& align_x,
     const ScrollAlignment& align_y,
     const ScrollOffset& current_scroll_offset) {
   // Prevent degenerate cases by giving the visible rect a minimum non-0 size.
-  LayoutRect non_zero_visible_rect(scroll_snapport_rect);
+  PhysicalRect non_zero_visible_rect = scroll_snapport_rect;
   LayoutUnit minimum_layout_unit;
   minimum_layout_unit.SetRawValue(1);
   if (non_zero_visible_rect.Width() == LayoutUnit())
@@ -83,8 +83,9 @@
 
   // Determine the appropriate X behavior.
   ScrollAlignmentBehavior scroll_x;
-  LayoutRect expose_rect_x(expose_rect.X(), non_zero_visible_rect.Y(),
-                           expose_rect.Width(), non_zero_visible_rect.Height());
+  PhysicalRect expose_rect_x(expose_rect.X(), non_zero_visible_rect.Y(),
+                             expose_rect.Width(),
+                             non_zero_visible_rect.Height());
   LayoutUnit intersect_width =
       Intersection(non_zero_visible_rect, expose_rect_x).Width();
   if (intersect_width == expose_rect.Width() ||
@@ -111,9 +112,9 @@
     // Closest edge is the right in two cases:
     // (1) exposeRect to the right of and smaller than nonZeroVisibleRect
     // (2) exposeRect to the left of and larger than nonZeroVisibleRect
-    if ((expose_rect.MaxX() > non_zero_visible_rect.MaxX() &&
+    if ((expose_rect.Right() > non_zero_visible_rect.Right() &&
          expose_rect.Width() < non_zero_visible_rect.Width()) ||
-        (expose_rect.MaxX() < non_zero_visible_rect.MaxX() &&
+        (expose_rect.Right() < non_zero_visible_rect.Right() &&
          expose_rect.Width() > non_zero_visible_rect.Width())) {
       scroll_x = kScrollAlignmentRight;
     }
@@ -121,8 +122,9 @@
 
   // Determine the appropriate Y behavior.
   ScrollAlignmentBehavior scroll_y;
-  LayoutRect expose_rect_y(non_zero_visible_rect.X(), expose_rect.Y(),
-                           non_zero_visible_rect.Width(), expose_rect.Height());
+  PhysicalRect expose_rect_y(non_zero_visible_rect.X(), expose_rect.Y(),
+                             non_zero_visible_rect.Width(),
+                             expose_rect.Height());
   LayoutUnit intersect_height =
       Intersection(non_zero_visible_rect, expose_rect_y).Height();
   if (intersect_height == expose_rect.Height()) {
@@ -145,9 +147,9 @@
     // Closest edge is the bottom in two cases:
     // (1) exposeRect below and smaller than nonZeroVisibleRect
     // (2) exposeRect above and larger than nonZeroVisibleRect
-    if ((expose_rect.MaxY() > non_zero_visible_rect.MaxY() &&
+    if ((expose_rect.Bottom() > non_zero_visible_rect.Bottom() &&
          expose_rect.Height() < non_zero_visible_rect.Height()) ||
-        (expose_rect.MaxY() < non_zero_visible_rect.MaxY() &&
+        (expose_rect.Bottom() < non_zero_visible_rect.Bottom() &&
          expose_rect.Height() > non_zero_visible_rect.Height())) {
       scroll_y = kScrollAlignmentBottom;
     }
@@ -155,17 +157,18 @@
 
   // We would like calculate the ScrollPosition to move |expose_rect| inside
   // the scroll_snapport, which is based on the scroll_origin of the scroller.
-  non_zero_visible_rect.Move(LayoutSize(-current_scroll_offset));
+  non_zero_visible_rect.Move(
+      -PhysicalOffset::FromFloatSizeRound(current_scroll_offset));
 
   // Given the X behavior, compute the X coordinate.
   float x;
   if (scroll_x == kScrollAlignmentNoScroll) {
     x = current_scroll_offset.Width();
   } else if (scroll_x == kScrollAlignmentRight) {
-    x = (expose_rect.MaxX() - non_zero_visible_rect.MaxX()).ToFloat();
+    x = (expose_rect.Right() - non_zero_visible_rect.Right()).ToFloat();
   } else if (scroll_x == kScrollAlignmentCenter) {
-    x = ((expose_rect.X() + expose_rect.MaxX() -
-          (non_zero_visible_rect.X() + non_zero_visible_rect.MaxX())) /
+    x = ((expose_rect.X() + expose_rect.Right() -
+          (non_zero_visible_rect.X() + non_zero_visible_rect.Right())) /
          2)
             .ToFloat();
   } else {
@@ -177,10 +180,10 @@
   if (scroll_y == kScrollAlignmentNoScroll) {
     y = current_scroll_offset.Height();
   } else if (scroll_y == kScrollAlignmentBottom) {
-    y = (expose_rect.MaxY() - non_zero_visible_rect.MaxY()).ToFloat();
+    y = (expose_rect.Bottom() - non_zero_visible_rect.Bottom()).ToFloat();
   } else if (scroll_y == kScrollAlignmentCenter) {
-    y = ((expose_rect.Y() + expose_rect.MaxY() -
-          (non_zero_visible_rect.Y() + non_zero_visible_rect.MaxY())) /
+    y = ((expose_rect.Y() + expose_rect.Bottom() -
+          (non_zero_visible_rect.Y() + non_zero_visible_rect.Bottom())) /
          2)
             .ToFloat();
   } else {
diff --git a/third_party/blink/renderer/core/scroll/scroll_alignment.h b/third_party/blink/renderer/core/scroll/scroll_alignment.h
index cd9196a..aa5373f 100644
--- a/third_party/blink/renderer/core/scroll/scroll_alignment.h
+++ b/third_party/blink/renderer/core/scroll/scroll_alignment.h
@@ -60,7 +60,7 @@
   kScrollAlignmentClosestEdge
 };
 
-class LayoutRect;
+struct PhysicalRect;
 
 struct CORE_EXPORT ScrollAlignment {
   STACK_ALLOCATED();
@@ -86,8 +86,8 @@
   // visible rect contracted by its scroll-padding.
   // FIXME: This function should probably go somewhere else but where?
   static ScrollOffset GetScrollOffsetToExpose(
-      const LayoutRect& visible_scroll_snapport_rect,
-      const LayoutRect& expose_rect,
+      const PhysicalRect& visible_scroll_snapport_rect,
+      const PhysicalRect& expose_rect,
       const ScrollAlignment& align_x,
       const ScrollAlignment& align_y,
       const ScrollOffset& current_scroll_offset);
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.cc b/third_party/blink/renderer/core/scroll/scrollable_area.cc
index 33793c4..63898f41 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.cc
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.cc
@@ -321,13 +321,13 @@
   GetScrollAnimator().ScrollToOffsetWithoutAnimation(ScrollOffset(x, y));
 }
 
-LayoutRect ScrollableArea::ScrollIntoView(
-    const LayoutRect& rect_in_absolute,
+PhysicalRect ScrollableArea::ScrollIntoView(
+    const PhysicalRect& rect_in_absolute,
     const WebScrollIntoViewParams& params) {
   // TODO(bokan): This should really be implemented here but ScrollAlignment is
   // in Core which is a dependency violation.
   NOTREACHED();
-  return LayoutRect();
+  return PhysicalRect();
 }
 
 void ScrollableArea::ScrollOffsetChanged(const ScrollOffset& offset,
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.h b/third_party/blink/renderer/core/scroll/scrollable_area.h
index 845aba0..ce628940 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.h
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.h
@@ -27,9 +27,9 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_SCROLL_SCROLLABLE_AREA_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
 #include "third_party/blink/renderer/core/scroll/scrollbar.h"
 #include "third_party/blink/renderer/platform/geometry/float_quad.h"
-#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
 #include "third_party/blink/renderer/platform/graphics/scroll_types.h"
@@ -47,18 +47,17 @@
 }
 
 namespace blink {
+class ChromeClient;
 class CompositorAnimationTimeline;
 class GraphicsLayer;
 class LayoutBox;
 class LayoutObject;
 class PaintLayer;
-class ChromeClient;
 class ProgrammaticScrollAnimator;
 class ScrollAnchor;
 class ScrollAnimatorBase;
 struct SerializedAnchor;
 class SmoothScrollSequencer;
-class CompositorAnimationTimeline;
 struct WebScrollIntoViewParams;
 
 enum IncludeScrollbarsInRect {
@@ -110,8 +109,8 @@
   // Scrolls the area so that the given rect, given in absolute coordinates,
   // such that it's visible in the area. Returns the new location of the input
   // rect in absolute coordinates.
-  virtual LayoutRect ScrollIntoView(const LayoutRect&,
-                                    const WebScrollIntoViewParams&);
+  virtual PhysicalRect ScrollIntoView(const PhysicalRect&,
+                                      const WebScrollIntoViewParams&);
 
   static bool ScrollBehaviorFromString(const String&, ScrollBehavior&);
 
@@ -264,9 +263,9 @@
   // container for the scroll snap areas when calculating snap positions. It's
   // the box's scrollport contracted by its scroll-padding.
   // https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding
-  virtual LayoutRect VisibleScrollSnapportRect(
+  virtual PhysicalRect VisibleScrollSnapportRect(
       IncludeScrollbarsInRect scrollbar_inclusion = kExcludeScrollbars) const {
-    return LayoutRect(VisibleContentRect(scrollbar_inclusion));
+    return PhysicalRect(VisibleContentRect(scrollbar_inclusion));
   }
 
   virtual IntPoint LastKnownMousePosition() const { return IntPoint(); }
diff --git a/third_party/blink/renderer/core/style/style_path.cc b/third_party/blink/renderer/core/style/style_path.cc
index e9f6619..41ccc527 100644
--- a/third_party/blink/renderer/core/style/style_path.cc
+++ b/third_party/blink/renderer/core/style/style_path.cc
@@ -29,7 +29,7 @@
   return base::AdoptRef(new StylePath(std::move(path_byte_stream)));
 }
 
-StylePath* StylePath::EmptyPath() {
+const StylePath* StylePath::EmptyPath() {
   DEFINE_STATIC_REF(StylePath, empty_path,
                     StylePath::Create(std::make_unique<SVGPathByteStream>()));
   return empty_path;
diff --git a/third_party/blink/renderer/core/style/style_path.h b/third_party/blink/renderer/core/style/style_path.h
index 5876df04..b25f308 100644
--- a/third_party/blink/renderer/core/style/style_path.h
+++ b/third_party/blink/renderer/core/style/style_path.h
@@ -21,7 +21,7 @@
   static scoped_refptr<StylePath> Create(std::unique_ptr<SVGPathByteStream>);
   ~StylePath() override;
 
-  static StylePath* EmptyPath();
+  static const StylePath* EmptyPath();
 
   const Path& GetPath() const;
   float length() const;
diff --git a/third_party/blink/renderer/core/testing/core_unit_test_helper.cc b/third_party/blink/renderer/core/testing/core_unit_test_helper.cc
index fdd1ccd..ba94f888 100644
--- a/third_party/blink/renderer/core/testing/core_unit_test_helper.cc
+++ b/third_party/blink/renderer/core/testing/core_unit_test_helper.cc
@@ -45,7 +45,7 @@
     : UseMockScrollbarSettings(), local_frame_client_(local_frame_client) {}
 
 const Node* RenderingTest::HitTest(int x, int y) {
-  HitTestLocation location(LayoutPoint(x, y));
+  HitTestLocation location(PhysicalOffset(x, y));
   HitTestResult result(
       HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive |
                      HitTestRequest::kAllowChildFrameContent),
@@ -54,7 +54,8 @@
   return result.InnerNode();
 }
 
-HitTestResult::NodeSet RenderingTest::RectBasedHitTest(LayoutRect rect) {
+HitTestResult::NodeSet RenderingTest::RectBasedHitTest(
+    const PhysicalRect& rect) {
   HitTestLocation location(rect);
   HitTestResult result(
       HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive |
diff --git a/third_party/blink/renderer/core/testing/core_unit_test_helper.h b/third_party/blink/renderer/core/testing/core_unit_test_helper.h
index 7f525260..51dab05 100644
--- a/third_party/blink/renderer/core/testing/core_unit_test_helper.h
+++ b/third_party/blink/renderer/core/testing/core_unit_test_helper.h
@@ -105,7 +105,7 @@
   explicit RenderingTest(LocalFrameClient* = nullptr);
 
   const Node* HitTest(int x, int y);
-  HitTestResult::NodeSet RectBasedHitTest(LayoutRect rect);
+  HitTestResult::NodeSet RectBasedHitTest(const PhysicalRect& rect);
 
  protected:
   void SetUp() override;
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc
index b251981..6f5c9c28 100644
--- a/third_party/blink/renderer/core/testing/internals.cc
+++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -1472,10 +1472,10 @@
                             Document* document) {
   document->UpdateStyleAndLayout();
   EventHandler& event_handler = document->GetFrame()->GetEventHandler();
-  LayoutPoint hit_test_point(
-      document->GetFrame()->View()->ConvertFromRootFrame(LayoutPoint(x, y)));
-  location = HitTestLocation(
-      (LayoutRect(hit_test_point, LayoutSize((int)width, (int)height))));
+  PhysicalRect rect{LayoutUnit(x), LayoutUnit(y), LayoutUnit(width),
+                    LayoutUnit(height)};
+  rect.offset = document->GetFrame()->View()->ConvertFromRootFrame(rect.offset);
+  location = HitTestLocation(rect);
   result = event_handler.HitTestResultAtLocation(
       location, HitTestRequest::kReadOnly | HitTestRequest::kActive |
                     HitTestRequest::kListBased);
@@ -1961,7 +1961,8 @@
                                                 HitTestRequest::kActive |
                                                 HitTestRequest::kListBased;
   LocalFrame* frame = document->GetFrame();
-  LayoutRect rect(x, y, width, height);
+  PhysicalRect rect{LayoutUnit(x), LayoutUnit(y), LayoutUnit(width),
+                    LayoutUnit(height)};
   if (ignore_clipping) {
     hit_type |= HitTestRequest::kIgnoreClipping;
   } else if (!IntRect(IntPoint(), frame->View()->Size())
@@ -2658,6 +2659,10 @@
       return "RowResize";
     case Cursor::kMiddlePanning:
       return "MiddlePanning";
+    case Cursor::kMiddlePanningVertical:
+      return "MiddlePanningVertical";
+    case Cursor::kMiddlePanningHorizontal:
+      return "MiddlePanningHorizontal";
     case Cursor::kEastPanning:
       return "EastPanning";
     case Cursor::kNorthPanning:
diff --git a/third_party/blink/renderer/core/timing/performance_element_timing.cc b/third_party/blink/renderer/core/timing/performance_element_timing.cc
index 328d992..bb6401b6 100644
--- a/third_party/blink/renderer/core/timing/performance_element_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_element_timing.cc
@@ -11,6 +11,7 @@
 
 // static
 PerformanceElementTiming* PerformanceElementTiming::Create(
+    const AtomicString& name,
     const String& url,
     const FloatRect& intersection_rect,
     DOMHighResTimeStamp start_time,
@@ -26,11 +27,12 @@
   DCHECK_GE(naturalHeight, 0);
   DCHECK(element);
   return MakeGarbageCollected<PerformanceElementTiming>(
-      url, intersection_rect, start_time, response_end, identifier,
+      name, url, intersection_rect, start_time, response_end, identifier,
       naturalWidth, naturalHeight, id, element);
 }
 
 PerformanceElementTiming::PerformanceElementTiming(
+    const AtomicString& name,
     const String& url,
     const FloatRect& intersection_rect,
     DOMHighResTimeStamp start_time,
@@ -40,7 +42,7 @@
     int naturalHeight,
     const AtomicString& id,
     Element* element)
-    : PerformanceEntry("image-paint", start_time, start_time),
+    : PerformanceEntry(name, start_time, start_time),
       element_(element),
       intersection_rect_(DOMRectReadOnly::FromFloatRect(intersection_rect)),
       response_end_(response_end),
diff --git a/third_party/blink/renderer/core/timing/performance_element_timing.h b/third_party/blink/renderer/core/timing/performance_element_timing.h
index 3a42329..3075a6ac 100644
--- a/third_party/blink/renderer/core/timing/performance_element_timing.h
+++ b/third_party/blink/renderer/core/timing/performance_element_timing.h
@@ -21,7 +21,8 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static PerformanceElementTiming* Create(const String& url,
+  static PerformanceElementTiming* Create(const AtomicString& name,
+                                          const String& url,
                                           const FloatRect& intersection_rect,
                                           DOMHighResTimeStamp start_time,
                                           DOMHighResTimeStamp response_end,
@@ -30,7 +31,8 @@
                                           int naturalHeight,
                                           const AtomicString& id,
                                           Element*);
-  PerformanceElementTiming(const String& url,
+  PerformanceElementTiming(const AtomicString& name,
+                           const String& url,
                            const FloatRect& intersection_rect,
                            DOMHighResTimeStamp start_time,
                            DOMHighResTimeStamp response_end,
diff --git a/third_party/blink/renderer/core/timing/profiler.idl b/third_party/blink/renderer/core/timing/profiler.idl
index 9d48971..74852cd 100644
--- a/third_party/blink/renderer/core/timing/profiler.idl
+++ b/third_party/blink/renderer/core/timing/profiler.idl
@@ -4,8 +4,6 @@
 
 // https://wicg.github.io/js-self-profiling/
 
-typedef any ProfilerTrace;
-
 [Exposed=(Window,Worker), RuntimeEnabled=ExperimentalJSProfiler]
 interface Profiler {
   readonly attribute DOMHighResTimeStamp sampleInterval;
diff --git a/third_party/blink/renderer/core/timing/profiler_frame.idl b/third_party/blink/renderer/core/timing/profiler_frame.idl
new file mode 100644
index 0000000..55e3332
--- /dev/null
+++ b/third_party/blink/renderer/core/timing/profiler_frame.idl
@@ -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.
+
+// https://wicg.github.io/js-self-profiling/#the-profilerframe-dictionary
+
+dictionary ProfilerFrame {
+  required DOMString name;
+  unsigned long long resourceId;
+  unsigned long long line;
+  unsigned long long column;
+};
diff --git a/third_party/blink/renderer/core/timing/profiler_group.cc b/third_party/blink/renderer/core/timing/profiler_group.cc
index 769418c6..a0bc711a 100644
--- a/third_party/blink/renderer/core/timing/profiler_group.cc
+++ b/third_party/blink/renderer/core/timing/profiler_group.cc
@@ -6,9 +6,11 @@
 
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "third_party/blink/renderer/bindings/core/v8/profiler_trace_builder.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/timing/profiler.h"
 #include "third_party/blink/renderer/core/timing/profiler_init_options.h"
+#include "third_party/blink/renderer/core/timing/profiler_trace.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
@@ -123,8 +125,10 @@
   v8::Local<v8::String> profiler_id =
       V8String(isolate_, profiler->ProfilerId());
   auto* profile = cpu_profiler_->StopProfiling(profiler_id);
-  // TODO(acomminos): Process v8::CpuProfile into JS Self-Profiling trace format
-  resolver->Resolve();
+  auto* trace = ProfilerTraceBuilder::FromProfile(
+      script_state, profile, profiler->SourceOrigin(), profiler->TimeOrigin());
+  resolver->Resolve(trace);
+
   profile->Delete();
 }
 
diff --git a/third_party/blink/renderer/core/timing/profiler_sample.idl b/third_party/blink/renderer/core/timing/profiler_sample.idl
new file mode 100644
index 0000000..bea7c30
--- /dev/null
+++ b/third_party/blink/renderer/core/timing/profiler_sample.idl
@@ -0,0 +1,10 @@
+// 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.
+
+// https://wicg.github.io/js-self-profiling/#the-profilersample-dictionary
+
+dictionary ProfilerSample {
+  required DOMHighResTimeStamp timestamp;
+  unsigned long long stackId;
+};
diff --git a/third_party/blink/renderer/core/timing/profiler_stack.idl b/third_party/blink/renderer/core/timing/profiler_stack.idl
new file mode 100644
index 0000000..5b53e4d4b
--- /dev/null
+++ b/third_party/blink/renderer/core/timing/profiler_stack.idl
@@ -0,0 +1,10 @@
+// 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.
+
+// https://wicg.github.io/js-self-profiling/#the-profilerstack-dictionary
+
+dictionary ProfilerStack {
+  unsigned long long parentId;
+  required unsigned long long frameId;
+};
diff --git a/third_party/blink/renderer/core/timing/profiler_trace.idl b/third_party/blink/renderer/core/timing/profiler_trace.idl
new file mode 100644
index 0000000..352f4a0
--- /dev/null
+++ b/third_party/blink/renderer/core/timing/profiler_trace.idl
@@ -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.
+
+// https://wicg.github.io/js-self-profiling/#the-profilertrace-dictionary
+
+dictionary ProfilerTrace {
+  required sequence<DOMString> resources;
+  required sequence<ProfilerFrame> frames;
+  required sequence<ProfilerStack> stacks;
+  required sequence<ProfilerSample> samples;
+};
diff --git a/third_party/blink/renderer/core/timing/window_performance.cc b/third_party/blink/renderer/core/timing/window_performance.cc
index 9c8b675..d1ce721 100644
--- a/third_party/blink/renderer/core/timing/window_performance.cc
+++ b/third_party/blink/renderer/core/timing/window_performance.cc
@@ -392,7 +392,8 @@
   event_timings_.clear();
 }
 
-void WindowPerformance::AddElementTiming(const String& url,
+void WindowPerformance::AddElementTiming(const AtomicString& name,
+                                         const String& url,
                                          const FloatRect& rect,
                                          TimeTicks start_time,
                                          TimeTicks response_end,
@@ -402,7 +403,7 @@
                                          Element* element) {
   DCHECK(RuntimeEnabledFeatures::ElementTimingEnabled(GetExecutionContext()));
   PerformanceElementTiming* entry = PerformanceElementTiming::Create(
-      url, rect, MonotonicTimeToDOMHighResTimeStamp(start_time),
+      name, url, rect, MonotonicTimeToDOMHighResTimeStamp(start_time),
       MonotonicTimeToDOMHighResTimeStamp(response_end), identifier,
       intrinsic_size.Width(), intrinsic_size.Height(), id, element);
   if (HasObserverFor(PerformanceEntry::kElement)) {
diff --git a/third_party/blink/renderer/core/timing/window_performance.h b/third_party/blink/renderer/core/timing/window_performance.h
index e2a6d3a..0bb1ee8 100644
--- a/third_party/blink/renderer/core/timing/window_performance.h
+++ b/third_party/blink/renderer/core/timing/window_performance.h
@@ -76,7 +76,8 @@
                            TimeTicks processing_end,
                            bool cancelable);
 
-  void AddElementTiming(const String& url,
+  void AddElementTiming(const AtomicString& name,
+                        const String& url,
                         const FloatRect& rect,
                         TimeTicks start_time,
                         TimeTicks response_end,
diff --git a/third_party/blink/renderer/core/workers/worker_reporting_proxy.h b/third_party/blink/renderer/core/workers/worker_reporting_proxy.h
index 6c4a462..239d436 100644
--- a/third_party/blink/renderer/core/workers/worker_reporting_proxy.h
+++ b/third_party/blink/renderer/core/workers/worker_reporting_proxy.h
@@ -128,6 +128,11 @@
   // Invoked when the thread is stopped and WorkerGlobalScope is being
   // destructed. This is the last method that is called on this interface.
   virtual void DidTerminateWorkerThread() {}
+
+  // This is a quick fix for service worker onion-soup. Don't add a similar
+  // function like IsDedicatedWorkerGlobalScopeProxy().
+  // TODO(leonhsl): Remove this after this becomes unnecessary.
+  virtual bool IsServiceWorkerGlobalScopeProxy() const { return false; }
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
index b1d7d51d..ae159f05 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
@@ -1486,7 +1486,7 @@
 
     if (response_.GetType() == network::mojom::FetchResponseType::kCors &&
         !cors::IsCorsSafelistedResponseHeader(it->key) &&
-        access_control_expose_header_set.find(it->key.Ascii().data()) ==
+        access_control_expose_header_set.find(it->key.Ascii()) ==
             access_control_expose_header_set.end()) {
       continue;
     }
@@ -1523,7 +1523,7 @@
 
   if (response_.GetType() == network::mojom::FetchResponseType::kCors &&
       !cors::IsCorsSafelistedResponseHeader(name) &&
-      access_control_expose_header_set.find(name.Ascii().data()) ==
+      access_control_expose_header_set.find(name.Ascii()) ==
           access_control_expose_header_set.end()) {
     LogConsoleError(GetExecutionContext(),
                     "Refused to get unsafe header \"" + name + "\"");
diff --git a/third_party/blink/renderer/devtools/front_end/console/ConsolePinPane.js b/third_party/blink/renderer/devtools/front_end/console/ConsolePinPane.js
index 967df6c3..95a7d15 100644
--- a/third_party/blink/renderer/devtools/front_end/console/ConsolePinPane.js
+++ b/third_party/blink/renderer/devtools/front_end/console/ConsolePinPane.js
@@ -144,6 +144,7 @@
 
     this._editorPromise = self.runtime.extension(UI.TextEditorFactory).instance().then(factory => {
       this._editor = factory.createEditor({
+        devtoolsAccessibleName: ls`Live expression editor`,
         lineNumbers: false,
         lineWrapping: true,
         mimeType: 'javascript',
diff --git a/third_party/blink/renderer/devtools/front_end/console/console_strings.grdp b/third_party/blink/renderer/devtools/front_end/console/console_strings.grdp
index 7d58b2c..e833eb11 100644
--- a/third_party/blink/renderer/devtools/front_end/console/console_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/console/console_strings.grdp
@@ -126,6 +126,9 @@
   <message name="IDS_DEVTOOLS_650140bd73628d283d870d4648ae3324" desc="">
     Find string in logs
   </message>
+  <message name="IDS_DEVTOOLS_6775864ce3df150aabbf2d60eb12f513" desc="">
+    Live expression editor
+  </message>
   <message name="IDS_DEVTOOLS_689202409e48743b914713f96d93947c" desc="">
     Value
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/elements/StylePropertyTreeElement.js b/third_party/blink/renderer/devtools/front_end/elements/StylePropertyTreeElement.js
index 9012b72..11f5e46 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/StylePropertyTreeElement.js
+++ b/third_party/blink/renderer/devtools/front_end/elements/StylePropertyTreeElement.js
@@ -232,6 +232,26 @@
     return container;
   }
 
+  /**
+   * @param {string} propertyValue
+   * @param {string} propertyName
+   * @return {!Node}
+   */
+  _processGrid(propertyValue, propertyName) {
+    const splitResult = TextUtils.TextUtils.splitStringByRegexes(propertyValue, [SDK.CSSMetadata.GridAreaRowRegex]);
+    if (splitResult.length <= 1)
+      return createTextNode(propertyValue);
+
+    const indent = Common.moduleSetting('textEditorIndent').get();
+    const container = createDocumentFragment();
+    for (const result of splitResult) {
+      const value = result.value.trim();
+      const content = UI.html`<br /><span class='styles-clipboard-only'>${indent.repeat(2)}</span>${value}`;
+      container.appendChild(content);
+    }
+    return container;
+  }
+
   _updateState() {
     if (!this.listItemElement)
       return;
@@ -413,6 +433,7 @@
       propertyRenderer.setColorHandler(this._processColor.bind(this));
       propertyRenderer.setBezierHandler(this._processBezier.bind(this));
       propertyRenderer.setShadowHandler(this._processShadow.bind(this));
+      propertyRenderer.setGridHandler(this._processGrid.bind(this));
     }
 
     this.listItemElement.removeChildren();
@@ -427,7 +448,9 @@
     this.listItemElement.createChild('span', 'styles-clipboard-only')
         .createTextChild(indent + (this.property.disabled ? '/* ' : ''));
     this.listItemElement.appendChild(this.nameElement);
-    this.listItemElement.createTextChild(': ');
+    const lineBreakValue = this.valueElement.firstElementChild && this.valueElement.firstElementChild.tagName === 'BR';
+    const separator = lineBreakValue ? ':' : ': ';
+    this.listItemElement.createChild('span', 'styles-name-value-separator').textContent = separator;
     if (this._expandElement)
       this.listItemElement.appendChild(this._expandElement);
     this.listItemElement.appendChild(this.valueElement);
@@ -523,8 +546,19 @@
       return;
 
     const isEditingName = selectElement === this.nameElement;
-    if (!isEditingName)
+    if (!isEditingName) {
+      if (SDK.cssMetadata().isGridAreaDefiningProperty(this.name))
+        this.valueElement.textContent = restoreGridIndents(this.value);
       this.valueElement.textContent = restoreURLs(this.valueElement.textContent, this.value);
+    }
+
+    /**
+     * @param {string} value
+     */
+    function restoreGridIndents(value) {
+      const splitResult = TextUtils.TextUtils.splitStringByRegexes(value, [SDK.CSSMetadata.GridAreaRowRegex]);
+      return splitResult.map(result => result.value.trim()).join('\n');
+    }
 
     /**
      * @param {string} fieldValue
@@ -638,7 +672,7 @@
 
     let result;
 
-    if (isEnterKey(event)) {
+    if (isEnterKey(event) && !event.shiftKey) {
       result = 'forward';
     } else if (event.keyCode === UI.KeyboardShortcut.Keys.Esc.code || event.key === 'Escape') {
       result = 'cancel';
@@ -976,7 +1010,7 @@
       return;
 
     const hasBeenEditedIncrementally = this._hasBeenEditedIncrementally;
-    styleText = styleText.replace(/\s/g, ' ').trim();  // Replace &nbsp; with whitespace.
+    styleText = styleText.replace(/[\u00a0\t]/g, ' ').trim();  // Replace &nbsp; with whitespace.
     if (!styleText.length && majorChange && this._newProperty && !hasBeenEditedIncrementally) {
       // The user deleted everything and never applied a new property value via Up/Down scrolling/live editing, so remove the tree element and update.
       this.parent.removeChild(this);
diff --git a/third_party/blink/renderer/devtools/front_end/elements/StylesSidebarPane.js b/third_party/blink/renderer/devtools/front_end/elements/StylesSidebarPane.js
index dafcf70..e031c94 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/StylesSidebarPane.js
+++ b/third_party/blink/renderer/devtools/front_end/elements/StylesSidebarPane.js
@@ -2143,6 +2143,8 @@
         }
         break;
       case 'Enter':
+        if (event.shiftKey)
+          return;
         // Accept any available autocompletions and advance to the next field.
         this.tabKeyPressed();
         event.preventDefault();
@@ -2341,6 +2343,8 @@
     this._bezierHandler = null;
     /** @type {?function(string, string):!Node} */
     this._shadowHandler = null;
+    /** @type {?function(string, string):!Node} */
+    this._gridHandler = null;
     /** @type {?function(string):!Node} */
     this._varHandler = createTextNode;
   }
@@ -2367,6 +2371,13 @@
   }
 
   /**
+   * @param {function(string, string):!Node} handler
+   */
+  setGridHandler(handler) {
+    this._gridHandler = handler;
+  }
+
+  /**
    * @param {function(string):!Node} handler
    */
   setVarHandler(handler) {
@@ -2401,6 +2412,12 @@
       return valueElement;
     }
 
+    if (this._gridHandler && SDK.cssMetadata().isGridAreaDefiningProperty(this._propertyName)) {
+      valueElement.appendChild(this._gridHandler(this._propertyValue, this._propertyName));
+      valueElement.normalize();
+      return valueElement;
+    }
+
     const regexes = [SDK.CSSMetadata.VariableRegex, SDK.CSSMetadata.URLRegex];
     const processors = [this._varHandler, this._processURL.bind(this)];
     if (this._bezierHandler && SDK.cssMetadata().isBezierAwareProperty(this._propertyName)) {
diff --git a/third_party/blink/renderer/devtools/front_end/elements/stylesSectionTree.css b/third_party/blink/renderer/devtools/front_end/elements/stylesSectionTree.css
index b80c7fe..54ca6c7 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/stylesSectionTree.css
+++ b/third_party/blink/renderer/devtools/front_end/elements/stylesSectionTree.css
@@ -165,6 +165,13 @@
     cursor: default;
 }
 
+.styles-name-value-separator {
+    display: inline-block;
+    width: 14px;
+    text-decoration: inherit;
+    white-space: pre;
+}
+
 .styles-clipboard-only {
     display: inline-block;
     width: 0;
diff --git a/third_party/blink/renderer/devtools/front_end/elements_test_runner/ElementsTestRunner.js b/third_party/blink/renderer/devtools/front_end/elements_test_runner/ElementsTestRunner.js
index 177d9f4f..26ee7ba7 100644
--- a/third_party/blink/renderer/devtools/front_end/elements_test_runner/ElementsTestRunner.js
+++ b/third_party/blink/renderer/devtools/front_end/elements_test_runner/ElementsTestRunner.js
@@ -150,7 +150,7 @@
   return UI.panels.elements._computedStyleWidget;
 };
 
-ElementsTestRunner.dumpComputedStyle = function(doNotAutoExpand) {
+ElementsTestRunner.dumpComputedStyle = function(doNotAutoExpand, printInnerText) {
   const computed = ElementsTestRunner.computedStyleWidget();
   const treeOutline = computed._propertiesOutline;
   const children = treeOutline.rootElement().children();
@@ -162,8 +162,8 @@
       continue;
 
     let dumpText = '';
-    dumpText += treeElement.title.querySelector('.property-name').textContent;
-    dumpText += treeElement.title.querySelector('.property-value').textContent;
+    dumpText += text(treeElement.title.querySelector('.property-name'));
+    dumpText += text(treeElement.title.querySelector('.property-value'));
     TestRunner.addResult(dumpText);
 
     if (doNotAutoExpand && !treeElement.expanded)
@@ -176,9 +176,9 @@
       if (trace.title.classList.contains('property-trace-inactive'))
         dumpText += 'OVERLOADED ';
 
-      dumpText += title.querySelector('.property-trace-value').textContent;
+      dumpText += text(title.querySelector('.property-trace-value'));
       dumpText += ' - ';
-      dumpText += title.querySelector('.property-trace-selector').textContent;
+      dumpText += text(title.querySelector('.property-trace-selector'));
       const link = title.querySelector('.trace-link');
 
       if (link)
@@ -187,6 +187,10 @@
       TestRunner.addResult('    ' + dumpText);
     }
   }
+
+  function text(node) {
+    return printInnerText ? node.innerText : node.textContent;
+  }
 };
 
 ElementsTestRunner.findComputedPropertyWithName = function(name) {
@@ -411,11 +415,11 @@
 };
 
 ElementsTestRunner.dumpSelectedElementStyles = function(
-    excludeComputed, excludeMatched, omitLonghands, includeSelectorGroupMarks) {
+    excludeComputed, excludeMatched, omitLonghands, includeSelectorGroupMarks, printInnerText) {
   const sectionBlocks = UI.panels.elements._stylesWidget._sectionBlocks;
 
   if (!excludeComputed)
-    ElementsTestRunner.dumpComputedStyle();
+    ElementsTestRunner.dumpComputedStyle(false /* doNotAutoExpand */, printInnerText);
 
   for (const block of sectionBlocks) {
     for (const section of block.sections) {
@@ -426,17 +430,21 @@
         let nodeDescription = '';
 
         if (section.element.previousSibling.firstElementChild)
-          nodeDescription = section.element.previousSibling.firstElementChild.shadowRoot.lastChild.textContent;
+          nodeDescription = text(section.element.previousSibling.firstElementChild.shadowRoot.lastChild);
 
-        TestRunner.addResult('======== ' + section.element.previousSibling.textContent + nodeDescription + ' ========');
+        TestRunner.addResult('======== ' + text(section.element.previousSibling) + nodeDescription + ' ========');
       }
 
-      printStyleSection(section, omitLonghands, includeSelectorGroupMarks);
+      printStyleSection(section, omitLonghands, includeSelectorGroupMarks, printInnerText);
     }
   }
+
+  function text(node) {
+    return printInnerText ? node.innerText : node.textContent;
+  }
 };
 
-function printStyleSection(section, omitLonghands, includeSelectorGroupMarks) {
+function printStyleSection(section, omitLonghands, includeSelectorGroupMarks, printInnerText) {
   if (!section)
     return;
 
@@ -446,13 +454,13 @@
 
   for (let i = 0; i < medias.length; ++i) {
     const media = medias[i];
-    TestRunner.addResult(media.textContent);
+    TestRunner.addResult(text(media));
   }
 
   const selector =
       section._titleElement.querySelector('.selector') || section._titleElement.querySelector('.keyframe-key');
-  let selectorText = (includeSelectorGroupMarks ? buildMarkedSelectors(selector) : selector.textContent);
-  selectorText += selector.nextSibling.textContent;
+  let selectorText = (includeSelectorGroupMarks ? buildMarkedSelectors(selector) : text(selector));
+  selectorText += text(selector.nextSibling);
   const anchor = section._titleElement.querySelector('.styles-section-subtitle');
 
   if (anchor) {
@@ -461,10 +469,14 @@
   }
 
   TestRunner.addResult(selectorText);
-  ElementsTestRunner.dumpStyleTreeOutline(section.propertiesTreeOutline, (omitLonghands ? 1 : 2));
+  ElementsTestRunner.dumpStyleTreeOutline(section.propertiesTreeOutline, (omitLonghands ? 1 : 2), printInnerText);
   if (!section._showAllButton.classList.contains('hidden'))
-    TestRunner.addResult(section._showAllButton.textContent);
+    TestRunner.addResult(text(section._showAllButton));
   TestRunner.addResult('');
+
+  function text(node) {
+    return printInnerText ? node.innerText : node.textContent;
+  }
 }
 
 function extractLinkText(element) {
@@ -612,15 +624,16 @@
   return null;
 };
 
-ElementsTestRunner.dumpStyleTreeOutline = function(treeItem, depth) {
+ElementsTestRunner.dumpStyleTreeOutline = function(treeItem, depth, printInnerText) {
   const children = treeItem.rootElement().children();
 
   for (let i = 0; i < children.length; ++i)
-    ElementsTestRunner.dumpStyleTreeItem(children[i], '', depth || 2);
+    ElementsTestRunner.dumpStyleTreeItem(children[i], '', depth || 2, printInnerText);
 };
 
-ElementsTestRunner.dumpStyleTreeItem = function(treeItem, prefix, depth) {
-  const textContent = TestRunner.textContentWithoutStyles(treeItem.listItemElement);
+ElementsTestRunner.dumpStyleTreeItem = function(treeItem, prefix, depth, printInnerText) {
+  const textContent = printInnerText ? treeItem.listItemElement.innerText :
+                                       TestRunner.textContentWithoutStyles(treeItem.listItemElement);
   if (textContent.indexOf(' width:') !== -1 || textContent.indexOf(' height:') !== -1)
     return;
 
diff --git a/third_party/blink/renderer/devtools/front_end/externs.js b/third_party/blink/renderer/devtools/front_end/externs.js
index 925c138e..ace7420 100644
--- a/third_party/blink/renderer/devtools/front_end/externs.js
+++ b/third_party/blink/renderer/devtools/front_end/externs.js
@@ -544,7 +544,7 @@
 };
 /** @type {!{cursorDiv: Element, lineSpace: Element, gutters: Element}} */
 CodeMirror.prototype.display;
-/** @type {!{mode: string, lineWrapping: boolean}} */
+/** @type {!{devtoolsAccessibleName: string, mode: string, lineWrapping: boolean}} */
 CodeMirror.prototype.options;
 /** @type {!Object} */
 CodeMirror.Pass;
diff --git a/third_party/blink/renderer/devtools/front_end/network/NetworkConfigView.js b/third_party/blink/renderer/devtools/front_end/network/NetworkConfigView.js
index e83f69d..eb2295a 100644
--- a/third_party/blink/renderer/devtools/front_end/network/NetworkConfigView.js
+++ b/third_party/blink/renderer/devtools/front_end/network/NetworkConfigView.js
@@ -16,11 +16,13 @@
   }
 
   /**
+   * @param {string} title
    * @return {{select: !Element, input: !Element}}
    */
-  static createUserAgentSelectAndInput() {
+  static createUserAgentSelectAndInput(title) {
     const userAgentSetting = Common.settings.createSetting('customUserAgent', '');
     const userAgentSelectElement = createElement('select');
+    UI.ARIAUtils.setAccessibleName(userAgentSelectElement, title);
 
     const customOverride = {title: Common.UIString('Custom...'), value: 'custom'};
     userAgentSelectElement.appendChild(new Option(customOverride.title, customOverride.value));
@@ -42,6 +44,7 @@
     otherUserAgentElement.title = userAgentSetting.get();
     otherUserAgentElement.placeholder = Common.UIString('Enter a custom user agent');
     otherUserAgentElement.required = true;
+    UI.ARIAUtils.setAccessibleName(otherUserAgentElement, otherUserAgentElement.placeholder);
 
     settingChanged();
     userAgentSelectElement.addEventListener('change', userAgentSelected, false);
@@ -105,14 +108,17 @@
   }
 
   _createNetworkThrottlingSection() {
-    const section = this._createSection(Common.UIString('Network throttling'), 'network-config-throttling');
+    const title = ls`Network throttling`;
+    const section = this._createSection(title, 'network-config-throttling');
     const networkThrottlingSelect =
         /** @type {!HTMLSelectElement} */ (section.createChild('select', 'chrome-select'));
     MobileThrottling.throttlingManager().decorateSelectWithNetworkThrottling(networkThrottlingSelect);
+    UI.ARIAUtils.setAccessibleName(networkThrottlingSelect, title);
   }
 
   _createUserAgentSection() {
-    const section = this._createSection(Common.UIString('User agent'), 'network-config-ua');
+    const title = ls`User agent`;
+    const section = this._createSection(title, 'network-config-ua');
     const checkboxLabel = UI.CheckboxLabel.create(Common.UIString('Select automatically'), true);
     section.appendChild(checkboxLabel);
     const autoCheckbox = checkboxLabel.checkboxElement;
@@ -125,7 +131,7 @@
     });
     const customUserAgentSelectBox = section.createChild('div', 'network-config-ua-custom');
     autoCheckbox.addEventListener('change', userAgentSelectBoxChanged);
-    const customSelectAndInput = Network.NetworkConfigView.createUserAgentSelectAndInput();
+    const customSelectAndInput = Network.NetworkConfigView.createUserAgentSelectAndInput(title);
     customSelectAndInput.select.classList.add('chrome-select');
     customUserAgentSelectBox.appendChild(customSelectAndInput.select);
     customUserAgentSelectBox.appendChild(customSelectAndInput.input);
diff --git a/third_party/blink/renderer/devtools/front_end/network/NetworkDataGridNode.js b/third_party/blink/renderer/devtools/front_end/network/NetworkDataGridNode.js
index 642523f..caef4c3 100644
--- a/third_party/blink/renderer/devtools/front_end/network/NetworkDataGridNode.js
+++ b/third_party/blink/renderer/devtools/front_end/network/NetworkDataGridNode.js
@@ -796,12 +796,14 @@
     let iconElement;
     if (this._request.resourceType() === Common.resourceTypes.Image) {
       const previewImage = createElementWithClass('img', 'image-network-icon-preview');
+      UI.ARIAUtils.setAccessibleName(previewImage, this._request.resourceType().title());
       this._request.populateImageSource(previewImage);
 
       iconElement = createElementWithClass('div', 'icon');
       iconElement.appendChild(previewImage);
     } else {
       iconElement = createElementWithClass('img', 'icon');
+      UI.ARIAUtils.setAccessibleName(iconElement, this._request.resourceType().title());
     }
     iconElement.classList.add(this._request.resourceType().name());
 
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/CSSMetadata.js b/third_party/blink/renderer/devtools/front_end/sdk/CSSMetadata.js
index 42a2e24..b4d70fc 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/CSSMetadata.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/CSSMetadata.js
@@ -158,6 +158,15 @@
    * @param {string} propertyName
    * @return {boolean}
    */
+  isGridAreaDefiningProperty(propertyName) {
+    propertyName = propertyName.toLowerCase();
+    return propertyName === 'grid' || propertyName === 'grid-template' || propertyName === 'grid-template-areas';
+  }
+
+  /**
+   * @param {string} propertyName
+   * @return {boolean}
+   */
   isLengthProperty(propertyName) {
     propertyName = propertyName.toLowerCase();
     if (propertyName === 'line-height')
@@ -285,6 +294,16 @@
 SDK.CSSMetadata.URLRegex = /url\(\s*('.+?'|".+?"|[^)]+)\s*\)/g;
 
 /**
+ * Matches an instance of a grid area 'row' definition.
+ * 'grid-template-areas', e.g.
+ *    "a a ."
+ *
+ * 'grid', 'grid-template', e.g.
+ *    [track-name] "a a ." minmax(50px, auto) [track-name]
+ */
+SDK.CSSMetadata.GridAreaRowRegex = /((?:\[[\w\- ]+\]\s*)*(?:"[^"]+"|'[^']+'))[^'"\[]*\[?[^'"\[]*/;
+
+/**
  * @return {!SDK.CSSMetadata}
  */
 SDK.cssMetadata = function() {
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/CSSProperty.js b/third_party/blink/renderer/devtools/front_end/sdk/CSSProperty.js
index e72033d5..e386ab3 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/CSSProperty.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/CSSProperty.js
@@ -180,9 +180,11 @@
    * @return {string}
    */
   static _formatStyle(styleText, indentation, endIndentation, tokenizerFactory) {
+    const doubleIndent = indentation.substring(endIndentation.length) + indentation;
     if (indentation)
       indentation = '\n' + indentation;
     let result = '';
+    let propertyName = '';
     let propertyText;
     let insideProperty = false;
     let needsSemi = false;
@@ -225,9 +227,17 @@
         result = result.trimRight() + indentation + propertyText.trim() + ';';
         needsSemi = false;
         insideProperty = false;
+        propertyName = '';
         if (token === '}')
           result += '}';
       } else {
+        if (SDK.cssMetadata().isGridAreaDefiningProperty(propertyName)) {
+          const rowResult = SDK.CSSMetadata.GridAreaRowRegex.exec(token);
+          if (rowResult && rowResult.index === 0 && !propertyText.trimRight().endsWith(']'))
+            propertyText = propertyText.trimRight() + '\n' + doubleIndent;
+        }
+        if (!propertyName && token === ':')
+          propertyName = propertyText;
         propertyText += token;
       }
     }
diff --git a/third_party/blink/renderer/devtools/front_end/settings/SettingsScreen.js b/third_party/blink/renderer/devtools/front_end/settings/SettingsScreen.js
index 1fb8918..3a8475f 100644
--- a/third_party/blink/renderer/devtools/front_end/settings/SettingsScreen.js
+++ b/third_party/blink/renderer/devtools/front_end/settings/SettingsScreen.js
@@ -71,7 +71,7 @@
         /** @type {!Settings.SettingsScreen} */ (self.runtime.sharedInstance(Settings.SettingsScreen));
     if (settingsScreen.isShowing())
       return;
-    const dialog = new UI.Dialog();
+    const dialog = new UI.Dialog(/* modal=*/ true);
     dialog.addCloseButton();
     settingsScreen.show(dialog.contentElement);
     dialog.show();
diff --git a/third_party/blink/renderer/devtools/front_end/text_editor/CodeMirrorTextEditor.js b/third_party/blink/renderer/devtools/front_end/text_editor/CodeMirrorTextEditor.js
index fa70e90a..3badece 100644
--- a/third_party/blink/renderer/devtools/front_end/text_editor/CodeMirrorTextEditor.js
+++ b/third_party/blink/renderer/devtools/front_end/text_editor/CodeMirrorTextEditor.js
@@ -43,6 +43,7 @@
     this.registerRequiredCSS('text_editor/cmdevtools.css');
 
     this._codeMirror = new CodeMirror(this.element, {
+      devtoolsAccessibleName: options.devtoolsAccessibleName,
       lineNumbers: options.lineNumbers,
       matchBrackets: true,
       smartIndent: true,
@@ -1739,7 +1740,7 @@
    */
   init(display) {
     super.init(display);
-    UI.ARIAUtils.setAccessibleName(this.textarea, ls`Code editor`);
+    UI.ARIAUtils.setAccessibleName(this.textarea, this.cm.options.devtoolsAccessibleName || ls`Code editor`);
     this.textarea.addEventListener('compositionstart', this._onCompositionStart.bind(this));
   }
 
diff --git a/third_party/blink/renderer/devtools/front_end/ui/ARIAUtils.js b/third_party/blink/renderer/devtools/front_end/ui/ARIAUtils.js
index 84e9cee..2071e5ee 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/ARIAUtils.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/ARIAUtils.js
@@ -21,6 +21,16 @@
 
 /**
  * @param {!Element} element
+ * @param {boolean=} modal
+ */
+UI.ARIAUtils.markAsDialog = function(element, modal) {
+  element.setAttribute('role', 'dialog');
+  if (modal)
+    element.setAttribute('aria-modal', 'true');
+};
+
+/**
+ * @param {!Element} element
  */
 UI.ARIAUtils.markAsGroup = function(element) {
   element.setAttribute('role', 'group');
diff --git a/third_party/blink/renderer/devtools/front_end/ui/Dialog.js b/third_party/blink/renderer/devtools/front_end/ui/Dialog.js
index a60ba4f..ba7e455 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/Dialog.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/Dialog.js
@@ -29,7 +29,10 @@
  */
 
 UI.Dialog = class extends UI.GlassPane {
-  constructor() {
+  /**
+   * @param {boolean=} modal
+   */
+  constructor(modal) {
     super();
     this.registerRequiredCSS('ui/dialog.css');
     this.contentElement.tabIndex = 0;
@@ -41,6 +44,7 @@
       this.hide();
       event.consume(true);
     });
+    UI.ARIAUtils.markAsDialog(this.contentElement, modal);
     /** @type {!Map<!HTMLElement, number>} */
     this._tabIndexMap = new Map();
     /** @type {?UI.WidgetFocusRestorer} */
diff --git a/third_party/blink/renderer/devtools/front_end/ui/TextEditor.js b/third_party/blink/renderer/devtools/front_end/ui/TextEditor.js
index 1d418847..34cb20c 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/TextEditor.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/TextEditor.js
@@ -108,6 +108,7 @@
 /**
  * @typedef {{
  *  bracketMatchingSetting: (!Common.Setting|undefined),
+ *  devtoolsAccessibleName: (string|undefined),
  *  lineNumbers: boolean,
  *  lineWrapping: boolean,
  *  mimeType: (string|undefined),
diff --git a/third_party/blink/renderer/devtools/front_end/web_audio/AudioContextContentBuilder.js b/third_party/blink/renderer/devtools/front_end/web_audio/AudioContextContentBuilder.js
index 714d40d1c..c503d933 100644
--- a/third_party/blink/renderer/devtools/front_end/web_audio/AudioContextContentBuilder.js
+++ b/third_party/blink/renderer/devtools/front_end/web_audio/AudioContextContentBuilder.js
@@ -69,12 +69,16 @@
    */
   constructor(contextId, contextRealtimeData) {
     const time = contextRealtimeData.currentTime.toFixed(3);
+    const mean = (contextRealtimeData.callbackIntervalMean * 1000).toFixed(3);
+    const stddev = (Math.sqrt(contextRealtimeData.callbackIntervalVariance) * 1000).toFixed(3);
     const capacity = (contextRealtimeData.renderCapacity * 100).toFixed(3);
     this._fragment = createDocumentFragment();
     this._fragment.appendChild(UI.html`
       <div class="context-summary-container">
         <span>${ls`Current Time`}: ${time} s</span>
         <span>\u2758</span>
+        <span>${ls`Callback Interval`}: μ = ${mean} ms, σ = ${stddev} ms</span>
+        <span>\u2758</span>
         <span>${ls`Render Capacity`}: ${capacity} %</span>
       </div>
     `);
@@ -86,4 +90,4 @@
   getFragment() {
     return this._fragment;
   }
-};
\ No newline at end of file
+};
diff --git a/third_party/blink/renderer/devtools/front_end/web_audio/WebAudioView.js b/third_party/blink/renderer/devtools/front_end/web_audio/WebAudioView.js
index 67e8510..2fa72c9 100644
--- a/third_party/blink/renderer/devtools/front_end/web_audio/WebAudioView.js
+++ b/third_party/blink/renderer/devtools/front_end/web_audio/WebAudioView.js
@@ -175,7 +175,7 @@
       // Display summary only for real-time context.
       if (context.contextType === 'realtime') {
         const realtimeData = await model.requestRealtimeData(context.contextId);
-        if (realtimeData && realtimeData.currentTime && realtimeData.renderCapacity)
+        if (realtimeData)
           this._updateSummaryBar(context.contextId, realtimeData);
       } else {
         this._clearSummaryBar();
diff --git a/third_party/blink/renderer/devtools/front_end/web_audio/web_audio_strings.grdp b/third_party/blink/renderer/devtools/front_end/web_audio/web_audio_strings.grdp
index 665fadf..5134c285 100644
--- a/third_party/blink/renderer/devtools/front_end/web_audio/web_audio_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/web_audio/web_audio_strings.grdp
@@ -18,6 +18,9 @@
   <message name="IDS_DEVTOOLS_5f8695ebb8bc9a78c593ee7bfae153a5" desc="">
     AudioContext
   </message>
+  <message name="IDS_DEVTOOLS_ab7799fb0ebb5a4167781f756efb820a" desc="">
+    Callback Interval
+  </message>
   <message name="IDS_DEVTOOLS_b78dc7712ef83a078c1c68822d2a1a3f" desc="">
     BaseAudioContexts
   </message>
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index f86a054..48b44188 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -118,6 +118,7 @@
     "//third_party/blink/renderer/modules/installation",
     "//third_party/blink/renderer/modules/installedapp",
     "//third_party/blink/renderer/modules/keyboard",
+    "//third_party/blink/renderer/modules/launch",
     "//third_party/blink/renderer/modules/locks",
     "//third_party/blink/renderer/modules/manifest",
     "//third_party/blink/renderer/modules/media_capabilities",
@@ -325,6 +326,7 @@
     "mediacapturefromelement/canvas_capture_handler_unittest.cc",
     "mediacapturefromelement/html_audio_element_capturer_source_unittest.cc",
     "mediacapturefromelement/html_video_element_capturer_source_unittest.cc",
+    "mediarecorder/audio_track_recorder_unittest.cc",
     "mediastream/media_constraints_test.cc",
     "mediastream/media_devices_test.cc",
     "mediastream/media_stream_video_capturer_source_test.cc",
@@ -431,6 +433,7 @@
     "//third_party/blink/renderer/modules/storage:unit_tests",
     "//third_party/blink/renderer/platform",
     "//third_party/blink/renderer/platform/wtf",
+    "//third_party/opus",
     "//third_party/webrtc/rtc_base:rtc_base",
     "//v8",
   ]
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index 2791541..f314727 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -1685,7 +1685,7 @@
   HitTestLocation location(point);
   HitTestResult hit_test_result = HitTestResult(request, location);
   layer->HitTest(location, hit_test_result,
-                 LayoutRect(LayoutRect::InfiniteIntRect()));
+                 PhysicalRect(PhysicalRect::InfiniteIntRect()));
 
   Node* node = hit_test_result.InnerNode();
   if (!node)
@@ -2341,7 +2341,7 @@
            layout_text->FirstAbstractInlineTextBox();
        box.get(); box = box->NextInlineTextBox()) {
     AXObject* ax_object = AXObjectCache().GetOrCreate(box.get());
-    if (!ax_object->AccessibilityIsIgnored())
+    if (ax_object->AccessibilityIsIncludedInTree())
       children_.push_back(ax_object);
   }
 }
@@ -3065,7 +3065,7 @@
       // Find out where the last layout sibling is located within m_children.
       if (AXObject* child_object =
               AXObjectCache().Get(child.GetLayoutObject())) {
-        if (child_object->AccessibilityIsIgnored()) {
+        if (!child_object->AccessibilityIsIncludedInTree()) {
           const auto& children = child_object->Children();
           child_object = children.size() ? children.back().Get() : nullptr;
         }
@@ -3104,7 +3104,7 @@
       AXImageMapLink* area_object = ToAXImageMapLink(obj);
       area_object->SetParent(this);
       DCHECK_NE(area_object->AXObjectID(), 0U);
-      if (!area_object->AccessibilityIsIgnored())
+      if (area_object->AccessibilityIsIncludedInTree())
         children_.push_back(area_object);
       else
         AXObjectCache().Remove(area_object->AXObjectID());
@@ -3146,7 +3146,7 @@
 
   root->SetParent(this);
 
-  if (root->AccessibilityIsIgnored()) {
+  if (!root->AccessibilityIsIncludedInTree()) {
     for (const auto& child : root->Children())
       children_.push_back(child);
   } else {
@@ -3167,7 +3167,7 @@
       if (HTMLTableCaptionElement* caption =
               ToHTMLTableElement(table_node)->caption()) {
         AXObject* caption_object = ax_cache.GetOrCreate(caption);
-        if (caption_object && !caption_object->AccessibilityIsIgnored())
+        if (caption_object && caption_object->AccessibilityIsIncludedInTree())
           children_.push_front(caption_object);
       }
     }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_menu_list.cc b/third_party/blink/renderer/modules/accessibility/ax_menu_list.cc
index cdd8977..caaeda8 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_menu_list.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_menu_list.cc
@@ -78,7 +78,7 @@
     return;
 
   ToAXMockObject(popup)->SetParent(this);
-  if (popup->AccessibilityIsIgnored()) {
+  if (!popup->AccessibilityIsIncludedInTree()) {
     cache.Remove(popup->AXObjectID());
     return;
   }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 31460709..1745c22 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -1365,6 +1365,17 @@
       .Contains(type);
 }
 
+base::Optional<DocumentMarker::MarkerType> GetAriaSpellingOrGrammarMarker(
+    const AXObject& obj) {
+  const AtomicString& attribute_value =
+      obj.GetAOMPropertyOrARIAAttribute(AOMStringProperty::kInvalid);
+  if (EqualIgnoringASCIICase(attribute_value, "spelling"))
+    return DocumentMarker::kSpelling;
+  else if (EqualIgnoringASCIICase(attribute_value, "grammar"))
+    return DocumentMarker::kGrammar;
+  return base::nullopt;
+}
+
 }  // namespace
 
 void AXNodeObject::Markers(Vector<DocumentMarker::MarkerType>& marker_types,
@@ -1376,11 +1387,35 @@
   if (!text_node)
     return;
 
+  // First use ARIA markers for spelling/grammar if available.
+  // As an optimization, only checks until the nearest block-like ancestor.
+  AXObject* ax_ancestor = ParentObjectUnignored();
+  base::Optional<DocumentMarker::MarkerType> aria_marker;
+  while (ax_ancestor) {
+    aria_marker = GetAriaSpellingOrGrammarMarker(*ax_ancestor);
+    if (aria_marker)
+      break;  // Result obtained.
+    if (ax_ancestor->GetNode()) {
+      if (const ComputedStyle* style =
+              ax_ancestor->GetNode()->GetComputedStyle()) {
+        if (style->IsDisplayBlockContainer())
+          break;  // Do not go higher than block container.
+      }
+    }
+    ax_ancestor = ax_ancestor->ParentObjectUnignored();
+  }
+  if (aria_marker) {
+    marker_types.push_back(aria_marker.value());
+    marker_ranges.push_back(AXRange::RangeOfContents(*this));
+  }
+
   DocumentMarkerController& marker_controller = GetDocument()->Markers();
   DocumentMarkerVector markers = marker_controller.MarkersFor(*text_node);
   for (DocumentMarker* marker : markers) {
-    if (!MarkerTypeIsUsedForAccessibility(marker->GetType()))
+    if (!MarkerTypeIsUsedForAccessibility(marker->GetType()) ||
+        aria_marker == marker->GetType()) {
       continue;
+    }
 
     const Position start_position(*GetNode(), marker->StartOffset());
     const Position end_position(*GetNode(), marker->EndOffset());
@@ -1486,7 +1521,7 @@
     for (AXObject* child : parent->Children()) {
       DCHECK(child);
       if (child->RoleValue() == ax::mojom::Role::kRadioButton &&
-          !child->AccessibilityIsIgnored()) {
+          child->AccessibilityIsIncludedInTree()) {
         radio_buttons.push_back(child);
       }
     }
@@ -1590,10 +1625,10 @@
     return ax::mojom::InvalidState::kFalse;
   if (EqualIgnoringASCIICase(attribute_value, "true"))
     return ax::mojom::InvalidState::kTrue;
-  if (EqualIgnoringASCIICase(attribute_value, "spelling"))
-    return ax::mojom::InvalidState::kSpelling;
-  if (EqualIgnoringASCIICase(attribute_value, "grammar"))
-    return ax::mojom::InvalidState::kGrammar;
+  // "spelling" and "grammar" are also invalid values: they are exposed via
+  // Markers() as if they are native errors, but also use the invalid entry
+  // state on the node itself, therefore they are treated like "true".
+  // in terms of the node's invalid state
   // A yet unknown value.
   if (!attribute_value.IsEmpty())
     return ax::mojom::InvalidState::kOther;
@@ -2370,7 +2405,7 @@
   // getting children, ensure data is not stale.
   child->ClearChildren();
 
-  if (child->AccessibilityIsIgnored()) {
+  if (!child->AccessibilityIsIncludedInTree()) {
     const auto& children = child->Children();
     wtf_size_t length = children.size();
     for (wtf_size_t i = 0; i < length; ++i)
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 64bf03e8..4b09f5a7 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -885,6 +885,14 @@
   return cached_is_ignored_;
 }
 
+// TODO(janewman) AccessibilityIsIncludedInTree should be true for all nodes
+// that should be included in the tree, even if they are ignored
+bool AXObject::AccessibilityIsIncludedInTree() const {
+  // TODO(janewman) add DCHECK to ensure we do not disallow unignored nodes from
+  // being in the tree.
+  return !AccessibilityIsIgnored();
+}
+
 void AXObject::UpdateCachedAttributeValuesIfNeeded() const {
   if (IsDetached())
     return;
@@ -901,7 +909,8 @@
   cached_is_descendant_of_disabled_node_ = !!DisabledAncestor();
   cached_has_inherited_presentational_role_ =
       !!InheritsPresentationalRoleFrom();
-  cached_is_ignored_ = ComputeAccessibilityIsIgnored();
+  IgnoredReasons ignored_reasons;
+  cached_is_ignored_ = ComputeAccessibilityIsIgnored(&ignored_reasons);
   cached_is_editable_root_ = ComputeIsEditableRoot();
   // Compute live region root, which can be from any ARIA live value, including
   // "off", or from an automatic ARIA live value, e.g. from role="status".
@@ -2898,7 +2907,7 @@
   LayoutObject* layout_object = node ? node->GetLayoutObject() : nullptr;
   if (!layout_object || !node->isConnected())
     return false;
-  LayoutRect target_rect(layout_object->AbsoluteBoundingBoxRect());
+  PhysicalRect target_rect(layout_object->AbsoluteBoundingBoxRect());
   layout_object->ScrollRectToVisible(
       target_rect,
       WebScrollIntoViewParams(ScrollAlignment::kAlignCenterIfNeeded,
@@ -2926,7 +2935,7 @@
   ScrollAlignment scroll_alignment = {
       kScrollAlignmentNoScroll, kScrollAlignmentCenter, kScrollAlignmentCenter};
   layout_object->ScrollRectToVisible(
-      target_rect.ToLayoutRect(),
+      target_rect,
       WebScrollIntoViewParams(scroll_alignment, scroll_alignment,
                               kProgrammaticScroll, false, kScrollBehaviorAuto));
   AXObjectCache().PostNotification(
@@ -2941,8 +2950,8 @@
   LayoutObject* layout_object = node ? node->GetLayoutObject() : nullptr;
   if (!layout_object || !node->isConnected())
     return false;
-  LayoutRect target_rect(layout_object->AbsoluteBoundingBoxRect());
-  target_rect.MoveBy(-global_point);
+  PhysicalRect target_rect(layout_object->AbsoluteBoundingBoxRect());
+  target_rect.Move(-PhysicalOffset(global_point));
   layout_object->ScrollRectToVisible(
       target_rect,
       WebScrollIntoViewParams(ScrollAlignment::kAlignLeftAlways,
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index e2b0793..cbe8c12 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -497,8 +497,17 @@
   virtual bool CanSetFocusAttribute() const;
   bool CanSetValueAttribute() const;
 
-  // Whether objects are ignored, i.e. not included in the tree.
+  // Whether objects are ignored, i.e. hidden from the AT.
+  // TODO(janewman) Ignored nodes that are included in the tree should be marked
+  // with ax::mojom::State::kIgnored
   bool AccessibilityIsIgnored() const;
+
+  // Whether objects are included in the tree. Nodes that are included in the
+  // tree are serialized, even if they are ignored. This allows browser-side
+  // accessibility code to have a more accurate representation of the tree. e.g.
+  // inspect hidden nodes referenced by labeled-by, know where line breaking
+  // elements are, etc.
+  bool AccessibilityIsIncludedInTree() const;
   typedef HeapVector<IgnoredReason> IgnoredReasons;
   virtual bool ComputeAccessibilityIsIgnored(IgnoredReasons* = nullptr) const {
     return true;
@@ -1028,6 +1037,7 @@
   mutable int last_modification_count_;
   mutable RGBA32 cached_background_color_;
   mutable bool cached_is_ignored_ : 1;
+
   mutable bool cached_is_inert_or_aria_hidden_ : 1;
   mutable bool cached_is_descendant_of_leaf_node_ : 1;
   mutable bool cached_is_descendant_of_disabled_node_ : 1;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 086d54e..27b114f 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -219,7 +219,7 @@
 
   // the HTML element, for example, is focusable but has an AX object that is
   // ignored
-  if (obj->AccessibilityIsIgnored())
+  if (!obj->AccessibilityIsIncludedInTree())
     obj = obj->ParentObjectUnignored();
 
   return obj;
@@ -1438,7 +1438,8 @@
     return nullptr;
 
   AXObject* accessible_object = GetOrCreate(node->GetLayoutObject());
-  while (accessible_object && accessible_object->AccessibilityIsIgnored()) {
+  while (accessible_object &&
+         !accessible_object->AccessibilityIsIncludedInTree()) {
     node = NodeTraversal::Next(*node);
 
     while (node && !node->GetLayoutObject())
@@ -1619,7 +1620,7 @@
   AXObject* obj = GetOrCreate(anchor_node->GetLayoutObject());
   if (!obj)
     return;
-  if (obj->AccessibilityIsIgnored())
+  if (!obj->AccessibilityIsIncludedInTree())
     obj = obj->ParentObjectUnignored();
   PostNotification(obj, ax::mojom::Event::kScrolledToAnchor);
 }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_slider.cc b/third_party/blink/renderer/modules/accessibility/ax_slider.cc
index 8dc379b2..05a3e36 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_slider.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_slider.cc
@@ -87,7 +87,7 @@
 
   // Before actually adding the value indicator to the hierarchy,
   // allow the platform to make a final decision about it.
-  if (thumb->AccessibilityIsIgnored())
+  if (!thumb->AccessibilityIsIncludedInTree())
     cache.Remove(thumb->AXObjectID());
   else
     children_.push_back(thumb);
diff --git a/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc b/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc
index c6630ead..46eac7b 100644
--- a/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc
+++ b/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc
@@ -134,16 +134,6 @@
           CreateProperty(AXPropertyNameEnum::Invalid,
                          CreateValue("true", AXValueTypeEnum::Token)));
       break;
-    case ax::mojom::InvalidState::kSpelling:
-      properties.addItem(
-          CreateProperty(AXPropertyNameEnum::Invalid,
-                         CreateValue("spelling", AXValueTypeEnum::Token)));
-      break;
-    case ax::mojom::InvalidState::kGrammar:
-      properties.addItem(
-          CreateProperty(AXPropertyNameEnum::Invalid,
-                         CreateValue("grammar", AXValueTypeEnum::Token)));
-      break;
     default:
       // TODO(aboxhall): expose invalid: <nothing> and source: aria-invalid as
       // invalid value
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.cc
index b4c50aa..ad1b86b8 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.cc
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.cc
@@ -59,11 +59,12 @@
                             "' because " + reason + "."));
 }
 
+// Returns whether the |request_url| should be blocked by the CSP. Must be
+// called synchronously from the background fetch call.
 bool ShouldBlockDueToCSP(ExecutionContext* execution_context,
                          const KURL& request_url) {
-  return !ContentSecurityPolicy::ShouldBypassMainWorld(execution_context) &&
-         !execution_context->GetContentSecurityPolicy()->AllowConnectToSource(
-             request_url);
+  return !execution_context->GetContentSecurityPolicyForWorld()
+              ->AllowConnectToSource(request_url);
 }
 
 bool ShouldBlockPort(const KURL& request_url) {
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
index e4eddf3..59505bb 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -1139,13 +1139,6 @@
     *reason = kDisableDeferralReasonDrawImageOfVideo;
     return true;
   }
-  if (image_source->IsCanvasElement()) {
-    HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(image_source);
-    if (canvas->IsAnimated2d()) {
-      *reason = kDisableDeferralReasonDrawImageOfAnimated2dCanvas;
-      return true;
-    }
-  }
   return false;
 }
 
@@ -1222,8 +1215,6 @@
   DisableDeferralReason reason = kDisableDeferralReasonUnknown;
   if (ShouldDisableDeferral(image_source, &reason))
     DisableDeferral(reason);
-  else if (image->IsTextureBacked())
-    DisableDeferral(kDisableDeferralDrawImageWithTextureBackedSourceImage);
 
   ValidateStateStack();
 
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
index 904f5dd2..9f30df41 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
@@ -358,12 +358,11 @@
   FloatRect bounding_rect = transformed_path.BoundingRect();
 
   // We first map canvas coordinates to layout coordinates.
-  LayoutRect path_rect(bounding_rect);
-  LayoutRect canvas_rect = layout_box->PhysicalContentBoxRect().ToLayoutRect();
+  PhysicalRect path_rect = PhysicalRect::EnclosingRect(bounding_rect);
+  PhysicalRect canvas_rect = layout_box->PhysicalContentBoxRect();
   // TODO(fserb): Is this kIgnoreTransforms correct?
-  canvas_rect.MoveBy(
-      layout_box->LocalToAbsolutePoint(PhysicalOffset(), kIgnoreTransforms)
-          .ToLayoutPoint());
+  canvas_rect.Move(
+      layout_box->LocalToAbsolutePoint(PhysicalOffset(), kIgnoreTransforms));
   path_rect.SetX(
       (canvas_rect.X() + path_rect.X() * canvas_rect.Width() / Width()));
   path_rect.SetY(
@@ -670,13 +669,12 @@
 }
 
 HitTestCanvasResult* CanvasRenderingContext2D::GetControlAndIdIfHitRegionExists(
-    const LayoutPoint& location) {
+    const PhysicalOffset& location) {
   if (HitRegionsCount() <= 0)
     return MakeGarbageCollected<HitTestCanvasResult>(String(), nullptr);
 
   LayoutBox* box = canvas()->GetLayoutBox();
-  FloatPoint local_pos(
-      box->AbsoluteToLocalPoint(PhysicalOffsetToBeNoop(location)));
+  FloatPoint local_pos(box->AbsoluteToLocalPoint(location));
   if (box->StyleRef().HasBorder() || box->StyleRef().MayHavePadding())
     local_pos.Move(FloatSize(-box->PhysicalContentBoxOffset()));
   float scaleWidth = box->ContentWidth().ToFloat() == 0.0f
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
index ced4a04..3760d69 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
@@ -151,7 +151,7 @@
   void StyleDidChange(const ComputedStyle* old_style,
                       const ComputedStyle& new_style) override;
   HitTestCanvasResult* GetControlAndIdIfHitRegionExists(
-      const LayoutPoint& location) override;
+      const PhysicalOffset& location) override;
   String GetIdFromControl(const Element*) override;
 
   // SVGResourceClient implementation
diff --git a/third_party/blink/renderer/modules/device_orientation/device_orientation_controller.cc b/third_party/blink/renderer/modules/device_orientation/device_orientation_controller.cc
index 52858b27..e5824342 100644
--- a/third_party/blink/renderer/modules/device_orientation/device_orientation_controller.cc
+++ b/third_party/blink/renderer/modules/device_orientation/device_orientation_controller.cc
@@ -175,7 +175,7 @@
       "See "
       "https://github.com/WICG/feature-policy/blob/master/"
       "features.md#sensor-features",
-      event_name.Ascii().data());
+      event_name.Ascii().c_str());
   ConsoleMessage* console_message = ConsoleMessage::Create(
       mojom::ConsoleMessageSource::kJavaScript,
       mojom::ConsoleMessageLevel::kWarning, std::move(message));
diff --git a/third_party/blink/renderer/modules/device_orientation/device_sensor_entry.cc b/third_party/blink/renderer/modules/device_orientation/device_sensor_entry.cc
index a58c56b4..ac124d7 100644
--- a/third_party/blink/renderer/modules/device_orientation/device_sensor_entry.cc
+++ b/third_party/blink/renderer/modules/device_orientation/device_sensor_entry.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/modules/device_orientation/device_sensor_entry.h"
 
+#include "services/device/public/cpp/generic_sensor/sensor_reading.h"
 #include "services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h"
 #include "third_party/blink/renderer/modules/device_orientation/device_sensor_event_pump.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
@@ -73,10 +74,9 @@
   if (!sensor_)
     return false;
 
-  DCHECK(shared_buffer_);
+  DCHECK(shared_buffer_reader_);
 
-  if (!shared_buffer_handle_->is_valid() ||
-      !shared_buffer_reader_->GetReading(reading)) {
+  if (!shared_buffer_reader_->GetReading(reading)) {
     HandleSensorError();
     return false;
   }
@@ -121,22 +121,14 @@
   sensor_.Bind(std::move(params->sensor));
   client_binding_.Bind(std::move(params->client_request));
 
-  shared_buffer_handle_ = std::move(params->memory);
-  DCHECK(!shared_buffer_);
-  shared_buffer_ = shared_buffer_handle_->MapAtOffset(kReadBufferSize,
-                                                      params->buffer_offset);
-  if (!shared_buffer_) {
+  shared_buffer_reader_ = device::SensorReadingSharedBufferReader::Create(
+      std::move(params->memory), params->buffer_offset);
+  if (!shared_buffer_reader_) {
     HandleSensorError();
     event_pump_->DidStartIfPossible();
     return;
   }
 
-  const device::SensorReadingSharedBuffer* buffer =
-      static_cast<const device::SensorReadingSharedBuffer*>(
-          shared_buffer_.get());
-  shared_buffer_reader_.reset(
-      new device::SensorReadingSharedBufferReader(buffer));
-
   device::mojom::blink::SensorConfigurationPtr config =
       std::move(params->default_configuration);
   config->frequency = std::min(
@@ -167,8 +159,7 @@
 void DeviceSensorEntry::HandleSensorError() {
   sensor_.reset();
   state_ = State::NOT_INITIALIZED;
-  shared_buffer_handle_.reset();
-  shared_buffer_.reset();
+  shared_buffer_reader_.reset();
   client_binding_.Close();
 }
 
diff --git a/third_party/blink/renderer/modules/device_orientation/device_sensor_entry.h b/third_party/blink/renderer/modules/device_orientation/device_sensor_entry.h
index c3435b6..ac56113 100644
--- a/third_party/blink/renderer/modules/device_orientation/device_sensor_entry.h
+++ b/third_party/blink/renderer/modules/device_orientation/device_sensor_entry.h
@@ -79,8 +79,6 @@
 
   device::mojom::blink::SensorType type_;
 
-  mojo::ScopedSharedBufferHandle shared_buffer_handle_;
-  mojo::ScopedSharedBufferMapping shared_buffer_;
   std::unique_ptr<device::SensorReadingSharedBufferReader>
       shared_buffer_reader_;
 };
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc
index fd1e232a..1117ee3 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_object.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -1203,6 +1203,13 @@
   return private_->AccessibilityIsIgnored();
 }
 
+bool WebAXObject::AccessibilityIsIncludedInTree() const {
+  if (IsDetached())
+    return false;
+
+  return private_->AccessibilityIsIncludedInTree();
+}
+
 int WebAXObject::AriaColumnCount() const {
   if (IsDetached())
     return 0;
diff --git a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
index 268d218..f82a41c0 100644
--- a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
+++ b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
@@ -62,7 +62,6 @@
 #include "third_party/blink/renderer/core/workers/worker_content_settings_client.h"
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
 #include "third_party/blink/renderer/modules/indexeddb/indexed_db_client.h"
-#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_thread.h"
@@ -381,9 +380,6 @@
 
   ProvideContentSettingsClientToWorker(worker_clients,
                                        std::move(content_settings_client_));
-  ProvideServiceWorkerGlobalScopeClientToWorker(
-      worker_clients, MakeGarbageCollected<ServiceWorkerGlobalScopeClient>(
-                          *worker_context_client_));
 
   // |web_worker_fetch_context| is null in some unit tests.
   scoped_refptr<WebWorkerFetchContext> web_worker_fetch_context =
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad.cc b/third_party/blink/renderer/modules/gamepad/gamepad.cc
index 51a80d1..8ff60de0 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad.cc
+++ b/third_party/blink/renderer/modules/gamepad/gamepad.cc
@@ -89,6 +89,21 @@
   }
 }
 
+void Gamepad::SetMapping(device::GamepadMapping mapping) {
+  switch (mapping) {
+    case device::GamepadMapping::kNone:
+      mapping_ = "";
+      return;
+    case device::GamepadMapping::kStandard:
+      mapping_ = "standard";
+      return;
+    case device::GamepadMapping::kXrStandard:
+      mapping_ = "xr-standard";
+      return;
+  }
+  NOTREACHED();
+}
+
 const Gamepad::DoubleVector& Gamepad::axes() {
   is_axis_data_dirty_ = false;
   return axes_;
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad.h b/third_party/blink/renderer/modules/gamepad/gamepad.h
index 0336def505..1a9e4c5 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad.h
+++ b/third_party/blink/renderer/modules/gamepad/gamepad.h
@@ -72,7 +72,7 @@
   DOMHighResTimeStamp timestamp() const { return timestamp_; }
 
   const String& mapping() const { return mapping_; }
-  void SetMapping(const String& val) { mapping_ = val; }
+  void SetMapping(device::GamepadMapping mapping);
 
   const DoubleVector& axes();
   void SetAxes(unsigned count, const double* data);
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc
index db53ab0..ee2025c8 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc
@@ -118,9 +118,10 @@
   }
 
   probe::AsyncTask async_task(request_->GetExecutionContext(), this, "error");
-  request_->HandleResponse(MakeGarbageCollected<DOMException>(
-      static_cast<DOMExceptionCode>(code), message));
+  IDBRequest* request = request_.Get();
   Detach();
+  request->HandleResponse(MakeGarbageCollected<DOMException>(
+      static_cast<DOMExceptionCode>(code), message));
 }
 
 void WebIDBCallbacksImpl::SuccessNamesAndVersionsList(
@@ -137,8 +138,9 @@
 #if DCHECK_IS_ON()
   DCHECK(!request_->TransactionHasQueuedResults());
 #endif  // DCHECK_IS_ON()
-  request_->EnqueueResponse(std::move(string_list));
+  IDBRequest* request = request_.Get();
   Detach();
+  request->EnqueueResponse(std::move(string_list));
 }
 
 void WebIDBCallbacksImpl::SuccessCursor(
@@ -162,9 +164,10 @@
 
   probe::AsyncTask async_task(request_->GetExecutionContext(), this, "success");
   value->SetIsolate(request_->GetIsolate());
-  request_->HandleResponse(std::move(cursor), std::move(key),
-                           std::move(primary_key), std::move(value));
+  IDBRequest* request = request_.Get();
   Detach();
+  request->HandleResponse(std::move(cursor), std::move(key),
+                          std::move(primary_key), std::move(value));
 }
 
 void WebIDBCallbacksImpl::SuccessCursorPrefetch(
@@ -193,8 +196,9 @@
 #if DCHECK_IS_ON()
     DCHECK(!request_->TransactionHasQueuedResults());
 #endif  // DCHECK_IS_ON()
-    request_->EnqueueResponse(std::move(db), IDBDatabaseMetadata(metadata));
+    IDBRequest* request = request_.Get();
     Detach();
+    request->EnqueueResponse(std::move(db), IDBDatabaseMetadata(metadata));
   } else if (db) {
     db->Close();
   }
@@ -205,8 +209,9 @@
     return;
 
   probe::AsyncTask async_task(request_->GetExecutionContext(), this, "success");
-  request_->HandleResponse(std::move(key));
+  IDBRequest* request = request_.Get();
   Detach();
+  request->HandleResponse(std::move(key));
 }
 
 void WebIDBCallbacksImpl::SuccessValue(
@@ -217,8 +222,9 @@
   std::unique_ptr<IDBValue> value = ConvertReturnValue(return_value);
   probe::AsyncTask async_task(request_->GetExecutionContext(), this, "success");
   value->SetIsolate(request_->GetIsolate());
-  request_->HandleResponse(std::move(value));
+  IDBRequest* request = request_.Get();
   Detach();
+  request->HandleResponse(std::move(value));
 }
 
 void WebIDBCallbacksImpl::SuccessArray(
@@ -234,8 +240,9 @@
     idb_value->SetIsolate(request_->GetIsolate());
     idb_values.emplace_back(std::move(idb_value));
   }
-  request_->HandleResponse(std::move(idb_values));
+  IDBRequest* request = request_.Get();
   Detach();
+  request->HandleResponse(std::move(idb_values));
 }
 
 void WebIDBCallbacksImpl::SuccessInteger(int64_t value) {
@@ -243,8 +250,9 @@
     return;
 
   probe::AsyncTask async_task(request_->GetExecutionContext(), this, "success");
-  request_->HandleResponse(value);
+  IDBRequest* request = request_.Get();
   Detach();
+  request->HandleResponse(value);
 }
 
 void WebIDBCallbacksImpl::Success() {
@@ -252,8 +260,9 @@
     return;
 
   probe::AsyncTask async_task(request_->GetExecutionContext(), this, "success");
-  request_->HandleResponse();
+  IDBRequest* request = request_.Get();
   Detach();
+  request->HandleResponse();
 }
 
 void WebIDBCallbacksImpl::SuccessCursorContinue(
@@ -273,9 +282,10 @@
   }
   DCHECK(value);
   value->SetIsolate(request_->GetIsolate());
-  request_->HandleResponse(std::move(key), std::move(primary_key),
-                           std::move(value));
+  IDBRequest* request = request_.Get();
   Detach();
+  request->HandleResponse(std::move(key), std::move(primary_key),
+                          std::move(value));
 }
 
 void WebIDBCallbacksImpl::Blocked(int64_t old_version) {
diff --git a/third_party/blink/renderer/modules/launch/BUILD.gn b/third_party/blink/renderer/modules/launch/BUILD.gn
new file mode 100644
index 0000000..b8307c3
--- /dev/null
+++ b/third_party/blink/renderer/modules/launch/BUILD.gn
@@ -0,0 +1,18 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/blink/renderer/modules/modules.gni")
+
+blink_modules_sources("launch") {
+  sources = [
+    "dom_window_launch_params.cc",
+    "dom_window_launch_params.h",
+    "launch_params.cc",
+    "launch_params.h",
+  ]
+
+  deps = [
+    "//third_party/blink/renderer/modules/filesystem",
+  ]
+}
diff --git a/third_party/blink/renderer/modules/launch/OWNERS b/third_party/blink/renderer/modules/launch/OWNERS
new file mode 100644
index 0000000..e4add89
--- /dev/null
+++ b/third_party/blink/renderer/modules/launch/OWNERS
@@ -0,0 +1,4 @@
+harrisjay@chromium.org
+
+# TEAM: web-apps-platform-team@chromium.org
+# COMPONENT: UI->Browser->WebAppInstalls
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/launch/README.md b/third_party/blink/renderer/modules/launch/README.md
new file mode 100644
index 0000000..25371a24
--- /dev/null
+++ b/third_party/blink/renderer/modules/launch/README.md
@@ -0,0 +1,11 @@
+# Launch Events API
+
+This directory contains the renderer side implementation of the client window [launch event API](https://github.com/WICG/sw-launch/blob/master/explainer.md).
+
+> Note: This API is under active development, and is a work in progress.
+
+## APIs in this Directory
+
+This API consists of the following parts:
+- `LaunchParams`: These are the parameters that the renderer was launched with. Currently, only file launches are supported but this will be extended in the future.
+- `getLaunchParams`: An entry point for getting the cause of a launch from a client window.
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/launch/dom_window_launch_params.cc b/third_party/blink/renderer/modules/launch/dom_window_launch_params.cc
new file mode 100644
index 0000000..a574964
--- /dev/null
+++ b/third_party/blink/renderer/modules/launch/dom_window_launch_params.cc
@@ -0,0 +1,88 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/launch/dom_window_launch_params.h"
+
+#include "base/bind.h"
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "base/task/post_task.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/mojom/native_file_system/native_file_system_directory_handle.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/modules/launch/launch_params.h"
+#include "third_party/blink/renderer/modules/native_file_system/native_file_system_handle.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+namespace blink {
+
+const char DOMWindowLaunchParams::kSupplementName[] = "DOMWindowLaunchParams";
+
+DOMWindowLaunchParams::DOMWindowLaunchParams(ExecutionContext* context) {}
+DOMWindowLaunchParams::~DOMWindowLaunchParams() = default;
+
+ScriptPromise DOMWindowLaunchParams::getLaunchParams(ScriptState* script_state,
+                                                     LocalDOMWindow& window) {
+  if (!base::FeatureList::IsEnabled(blink::features::kNativeFileSystemAPI) ||
+      !base::FeatureList::IsEnabled(blink::features::kFileHandlingAPI)) {
+    return ScriptPromise::RejectWithDOMException(
+        script_state,
+        MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError));
+  }
+
+  return FromState(script_state, &window)->GetLaunchParams(script_state);
+}
+
+ScriptPromise DOMWindowLaunchParams::GetLaunchParams(
+    ScriptState* script_state) {
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  ScriptPromise resolver_result = resolver->Promise();
+
+  // Immediately resolve the promise if the launch params are already known.
+  // TODO(crbug.com/829689): Getting the launch params from the browser will be
+  // asynchronous, so it will be possible that the |launch_params_| have not
+  // been initialized. Once a launch service has been created, a list of
+  // unresolved promises should be stored, so they can be resolved once the
+  // service has responded to a request for launch params.
+  resolver->Resolve(launch_params_);
+
+  return resolver_result;
+}
+
+void DOMWindowLaunchParams::Trace(blink::Visitor* visitor) {
+  visitor->Trace(launch_params_);
+  Supplement<LocalDOMWindow>::Trace(visitor);
+}
+
+// static
+DOMWindowLaunchParams* DOMWindowLaunchParams::FromState(
+    ScriptState* script_state,
+    LocalDOMWindow* window) {
+  ExecutionContext* context = ExecutionContext::From(script_state);
+  DOMWindowLaunchParams* supplement =
+      Supplement<LocalDOMWindow>::From<DOMWindowLaunchParams>(window);
+  if (!supplement) {
+    supplement = MakeGarbageCollected<DOMWindowLaunchParams>(context);
+    ProvideTo(*window, supplement);
+
+    // TODO(crbug.com/829689): When a service is created for getting launch
+    // params from the browser process, use that instead of hard coding the
+    // launch params.
+    supplement->launch_params_ = MakeGarbageCollected<LaunchParams>(
+        "default", HeapVector<Member<NativeFileSystemHandle>>());
+  }
+  return supplement;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/launch/dom_window_launch_params.h b/third_party/blink/renderer/modules/launch/dom_window_launch_params.h
new file mode 100644
index 0000000..b65f742
--- /dev/null
+++ b/third_party/blink/renderer/modules/launch/dom_window_launch_params.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 THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_DOM_WINDOW_LAUNCH_PARAMS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_DOM_WINDOW_LAUNCH_PARAMS_H_
+
+#include "third_party/blink/renderer/modules/launch/launch_params.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/supplementable.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class LocalDOMWindow;
+class ScriptPromiseResolver;
+class ExecutionContext;
+class ScriptPromiseResolver;
+class Visitor;
+
+class DOMWindowLaunchParams final
+    : public GarbageCollectedFinalized<DOMWindowLaunchParams>,
+      public Supplement<LocalDOMWindow> {
+  USING_GARBAGE_COLLECTED_MIXIN(DOMWindowLaunchParams);
+
+ public:
+  static const char kSupplementName[];
+
+  explicit DOMWindowLaunchParams(ExecutionContext*);
+  ~DOMWindowLaunchParams();
+
+  // IDL Interface.
+  static ScriptPromise getLaunchParams(ScriptState*, LocalDOMWindow&);
+
+  ScriptPromise GetLaunchParams(ScriptState*);
+
+  void Trace(blink::Visitor*) override;
+
+ private:
+  static DOMWindowLaunchParams* FromState(ScriptState*, LocalDOMWindow* window);
+
+  Member<LaunchParams> launch_params_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_DOM_WINDOW_LAUNCH_PARAMS_H_
diff --git a/third_party/blink/renderer/modules/launch/dom_window_launch_params.idl b/third_party/blink/renderer/modules/launch/dom_window_launch_params.idl
new file mode 100644
index 0000000..441173d
--- /dev/null
+++ b/third_party/blink/renderer/modules/launch/dom_window_launch_params.idl
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(crbug.com/829689): Add link to spec once complete.
+// Explainer: https://github.com/WICG/file-handling/blob/master/explainer.md
+
+[
+    ImplementedAs=DOMWindowLaunchParams,
+    RuntimeEnabled=FileHandling
+] partial interface Window {
+    [CallWith=ScriptState]
+    Promise<LaunchParams> getLaunchParams();
+};
diff --git a/third_party/blink/renderer/modules/launch/launch_params.cc b/third_party/blink/renderer/modules/launch/launch_params.cc
new file mode 100644
index 0000000..e04084f
--- /dev/null
+++ b/third_party/blink/renderer/modules/launch/launch_params.cc
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file
+
+#include "third_party/blink/renderer/modules/launch/launch_params.h"
+
+#include "third_party/blink/renderer/modules/native_file_system/native_file_system_handle.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+
+namespace blink {
+
+LaunchParams::LaunchParams(String cause,
+                           HeapVector<Member<NativeFileSystemHandle>> files)
+    : cause_(cause), files_(files) {}
+
+LaunchParams::~LaunchParams() = default;
+
+void LaunchParams::Trace(blink::Visitor* visitor) {
+  visitor->Trace(files_);
+  ScriptWrappable::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/launch/launch_params.h b/third_party/blink/renderer/modules/launch/launch_params.h
new file mode 100644
index 0000000..e3104da0
--- /dev/null
+++ b/third_party/blink/renderer/modules/launch/launch_params.h
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_LAUNCH_PARAMS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_LAUNCH_PARAMS_H_
+
+#include "third_party/blink/renderer/modules/native_file_system/native_file_system_handle.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class Visitor;
+
+class LaunchParams final : public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  LaunchParams(String cause, HeapVector<Member<NativeFileSystemHandle>> files);
+  ~LaunchParams() override;
+
+  // LaunchParams IDL interface.
+  const String& cause() { return cause_; }
+  const HeapVector<Member<NativeFileSystemHandle>>& files() { return files_; }
+
+  void Trace(blink::Visitor*) override;
+
+ private:
+  String cause_;
+  HeapVector<Member<NativeFileSystemHandle>> files_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_LAUNCH_PARAMS_H_
diff --git a/third_party/blink/renderer/modules/launch/launch_params.idl b/third_party/blink/renderer/modules/launch/launch_params.idl
new file mode 100644
index 0000000..5053541
--- /dev/null
+++ b/third_party/blink/renderer/modules/launch/launch_params.idl
@@ -0,0 +1,13 @@
+// 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.
+
+// TODO(crbug.com/829689): Add link to spec once complete.
+// Explainer: https://github.com/WICG/file-handling/blob/master/explainer.md
+
+[RuntimeEnabled=FileHandling] interface LaunchParams {
+  readonly attribute DOMString cause;
+
+  [SameObject]
+  readonly attribute FrozenArray<FileSystemBaseHandle> files;
+};
diff --git a/third_party/blink/renderer/modules/manifest/image_resource_type_converters.cc b/third_party/blink/renderer/modules/manifest/image_resource_type_converters.cc
index 688a2a697..ab10edb 100644
--- a/third_party/blink/renderer/modules/manifest/image_resource_type_converters.cc
+++ b/third_party/blink/renderer/modules/manifest/image_resource_type_converters.cc
@@ -27,7 +27,7 @@
 // https://w3c.github.io/manifest/#sizes-member.
 WTF::Vector<WebSize> ParseSizes(const WTF::String& sizes) {
   WebVector<WebSize> parsed_sizes = blink::WebIconSizesParser::ParseIconSizes(
-      WebString::FromASCII(sizes.Ascii().data()));
+      WebString::FromASCII(sizes.Ascii()));
   WTF::HashSet<std::pair<int, int>, WTF::PairHash<int, int>,
                WTF::PairHashTraits<WTF::UnsignedWithZeroKeyHashTraits<int>,
                                    WTF::UnsignedWithZeroKeyHashTraits<int>>>
@@ -86,7 +86,7 @@
   if (type.IsNull() || type.IsEmpty())
     return "";
 
-  if (!blink::IsSupportedMimeType(type.Ascii().data())) {
+  if (!blink::IsSupportedMimeType(type.Ascii())) {
     // TODO(rayankans): Issue developer warning.
     return "";
   }
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_input_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_input_element.cc
index 65ee21c..58d45cbf 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_input_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_input_element.cc
@@ -269,7 +269,7 @@
 void MediaControlInputElement::RecordCTREvent(CTREvent event) {
   String histogram_name =
       StringView("Media.Controls.CTR.") + GetNameForHistograms();
-  EnumerationHistogram ctr_histogram(histogram_name.Ascii().data(),
+  EnumerationHistogram ctr_histogram(histogram_name.Ascii().c_str(),
                                      static_cast<int>(CTREvent::kCount));
   ctr_histogram.Count(static_cast<int>(event));
 }
diff --git a/third_party/blink/renderer/modules/mediarecorder/BUILD.gn b/third_party/blink/renderer/modules/mediarecorder/BUILD.gn
index 74cbe2c..55c31af8 100644
--- a/third_party/blink/renderer/modules/mediarecorder/BUILD.gn
+++ b/third_party/blink/renderer/modules/mediarecorder/BUILD.gn
@@ -6,9 +6,21 @@
 
 blink_modules_sources("mediarecorder") {
   sources = [
+    "audio_track_encoder.cc",
+    "audio_track_encoder.h",
+    "audio_track_opus_encoder.cc",
+    "audio_track_opus_encoder.h",
+    "audio_track_pcm_encoder.cc",
+    "audio_track_pcm_encoder.h",
+    "audio_track_recorder.cc",
+    "audio_track_recorder.h",
     "blob_event.cc",
     "blob_event.h",
     "media_recorder.cc",
     "media_recorder.h",
   ]
+
+  deps = [
+    "//third_party/opus",
+  ]
 }
diff --git a/third_party/blink/renderer/modules/mediarecorder/DEPS b/third_party/blink/renderer/modules/mediarecorder/DEPS
index f8f1733..58c32c7 100644
--- a/third_party/blink/renderer/modules/mediarecorder/DEPS
+++ b/third_party/blink/renderer/modules/mediarecorder/DEPS
@@ -1,8 +1,16 @@
 include_rules = [
+    "+media",
     "-third_party/blink/renderer/modules",
     "+third_party/blink/renderer/modules/event_modules.h",
     "+third_party/blink/renderer/modules/event_target_modules.h",
     "+third_party/blink/renderer/modules/modules_export.h",
     "+third_party/blink/renderer/modules/mediarecorder",
     "+third_party/blink/renderer/modules/mediastream",
+    '+third_party/opus',
 ]
+
+specific_include_rules = {
+    ".*_unittest.cc": [
+        "+base/run_loop.h",
+    ],
+}
diff --git a/third_party/blink/renderer/modules/mediarecorder/OWNERS b/third_party/blink/renderer/modules/mediarecorder/OWNERS
index f3deb0e..a14da19 100644
--- a/third_party/blink/renderer/modules/mediarecorder/OWNERS
+++ b/third_party/blink/renderer/modules/mediarecorder/OWNERS
@@ -1,6 +1,8 @@
 peter@chromium.org
+guidou@chromium.org
 
 # Original (legacy) owner.
+emircan@chromium.org
 mcasas@chromium.org
 
 # COMPONENT: Blink>MediaRecording
diff --git a/content/renderer/media_recorder/audio_track_encoder.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.cc
similarity index 84%
rename from content/renderer/media_recorder/audio_track_encoder.cc
rename to third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.cc
index 6e1c647..726e4a6 100644
--- a/content/renderer/media_recorder/audio_track_encoder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/media_recorder/audio_track_encoder.h"
+#include "third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.h"
 
-namespace content {
+namespace blink {
 
 AudioTrackEncoder::AudioTrackEncoder(OnEncodedAudioCB on_encoded_audio_cb)
     : paused_(false), on_encoded_audio_cb_(std::move(on_encoded_audio_cb)) {
@@ -17,4 +17,4 @@
 
 AudioTrackEncoder::~AudioTrackEncoder() {}
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/renderer/media_recorder/audio_track_encoder.h b/third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.h
similarity index 63%
rename from content/renderer/media_recorder/audio_track_encoder.h
rename to third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.h
index 048a736..238ae2b8 100644
--- a/content/renderer/media_recorder/audio_track_encoder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.h
@@ -2,20 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_MEDIA_RECORDER_AUDIO_TRACK_ENCODER_H_
-#define CONTENT_RENDERER_MEDIA_RECORDER_AUDIO_TRACK_ENCODER_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIARECORDER_AUDIO_TRACK_ENCODER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIARECORDER_AUDIO_TRACK_ENCODER_H_
 
 #include <memory>
 
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/threading/thread.h"
 #include "base/threading/thread_checker.h"
-#include "base/time/time.h"
 #include "media/base/audio_bus.h"
 #include "media/base/audio_parameters.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
 
-namespace content {
+namespace blink {
 
 // Base interface for an AudioTrackEncoder. This class and its subclasses are
 // used by AudioTrackRecorder to encode audio before output. These are private
@@ -25,12 +25,12 @@
 // main render thread) but otherwise should operate entirely on
 // |encoder_thread_|, which is owned by AudioTrackRecorder. Be sure to delete
 // |encoder_thread_| before deleting the AudioTrackEncoder using it.
-class AudioTrackEncoder : public base::RefCountedThreadSafe<AudioTrackEncoder> {
+class AudioTrackEncoder : public WTF::ThreadSafeRefCounted<AudioTrackEncoder> {
  public:
   using OnEncodedAudioCB =
-      base::Callback<void(const media::AudioParameters& params,
-                          std::unique_ptr<std::string> encoded_data,
-                          base::TimeTicks capture_time)>;
+      base::RepeatingCallback<void(const media::AudioParameters& params,
+                                   std::unique_ptr<std::string> encoded_data,
+                                   base::TimeTicks capture_time)>;
 
   explicit AudioTrackEncoder(OnEncodedAudioCB on_encoded_audio_cb);
 
@@ -41,7 +41,7 @@
   void set_paused(bool paused) { paused_ = paused; }
 
  protected:
-  friend class base::RefCountedThreadSafe<AudioTrackEncoder>;
+  friend class WTF::ThreadSafeRefCounted<AudioTrackEncoder>;
 
   virtual ~AudioTrackEncoder();
 
@@ -57,6 +57,6 @@
   DISALLOW_COPY_AND_ASSIGN(AudioTrackEncoder);
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_MEDIA_RECORDER_AUDIO_TRACK_ENCODER_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIARECORDER_AUDIO_TRACK_ENCODER_H_
diff --git a/content/renderer/media_recorder/audio_track_opus_encoder.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.cc
similarity index 97%
rename from content/renderer/media_recorder/audio_track_opus_encoder.cc
rename to third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.cc
index 46fe61a9c..75de047 100644
--- a/content/renderer/media_recorder/audio_track_opus_encoder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.cc
@@ -2,10 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/media_recorder/audio_track_opus_encoder.h"
-
-#include <string>
-#include <utility>
+#include "third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.h"
 
 #include "media/base/audio_sample_types.h"
 #include "media/base/audio_timestamp_helper.h"
@@ -63,7 +60,7 @@
 
 }  // anonymous namespace
 
-namespace content {
+namespace blink {
 
 AudioTrackOpusEncoder::AudioTrackOpusEncoder(
     OnEncodedAudioCB on_encoded_audio_cb,
@@ -195,4 +192,4 @@
   }
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/renderer/media_recorder/audio_track_opus_encoder.h b/third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.h
similarity index 82%
rename from content/renderer/media_recorder/audio_track_opus_encoder.h
rename to third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.h
index 103a074..8234afd 100644
--- a/content/renderer/media_recorder/audio_track_opus_encoder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.h
@@ -2,21 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_MEDIA_RECORDER_AUDIO_TRACK_OPUS_ENCODER_H_
-#define CONTENT_RENDERER_MEDIA_RECORDER_AUDIO_TRACK_OPUS_ENCODER_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIARECORDER_AUDIO_TRACK_OPUS_ENCODER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIARECORDER_AUDIO_TRACK_OPUS_ENCODER_H_
 
 #include <memory>
 
 #include "base/macros.h"
-#include "base/time/time.h"
-#include "content/renderer/media_recorder/audio_track_encoder.h"
 #include "media/base/audio_bus.h"
 #include "media/base/audio_converter.h"
 #include "media/base/audio_fifo.h"
 #include "media/base/audio_parameters.h"
+#include "third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
 #include "third_party/opus/src/include/opus.h"
 
-namespace content {
+namespace blink {
 
 // Class encapsulating Opus-related encoding details. It contains an
 // AudioConverter to adapt incoming data to the format Opus likes to have.
@@ -64,6 +64,6 @@
   DISALLOW_COPY_AND_ASSIGN(AudioTrackOpusEncoder);
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_MEDIA_RECORDER_AUDIO_TRACK_OPUS_ENCODER_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIARECORDER_AUDIO_TRACK_OPUS_ENCODER_H_
diff --git a/content/renderer/media_recorder/audio_track_pcm_encoder.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.cc
similarity index 92%
rename from content/renderer/media_recorder/audio_track_pcm_encoder.cc
rename to third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.cc
index cae7dd78..c6af2621 100644
--- a/content/renderer/media_recorder/audio_track_pcm_encoder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.cc
@@ -2,15 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/media_recorder/audio_track_pcm_encoder.h"
-
-#include <string.h>
-#include <string>
+#include "third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.h"
 
 #include "media/base/audio_sample_types.h"
 #include "media/base/audio_timestamp_helper.h"
 
-namespace content {
+namespace blink {
 
 AudioTrackPcmEncoder::AudioTrackPcmEncoder(OnEncodedAudioCB on_encoded_audio_cb)
     : AudioTrackEncoder(std::move(on_encoded_audio_cb)) {}
@@ -53,4 +50,4 @@
                            capture_time_of_first_sample);
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/renderer/media_recorder/audio_track_pcm_encoder.h b/third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.h
similarity index 65%
rename from content/renderer/media_recorder/audio_track_pcm_encoder.h
rename to third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.h
index e0ec868c..b2241aa 100644
--- a/content/renderer/media_recorder/audio_track_pcm_encoder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.h
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_MEDIA_RECORDER_AUDIO_TRACK_PCM_ENCODER_H_
-#define CONTENT_RENDERER_MEDIA_RECORDER_AUDIO_TRACK_PCM_ENCODER_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIARECORDER_AUDIO_TRACK_PCM_ENCODER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIARECORDER_AUDIO_TRACK_PCM_ENCODER_H_
 
 #include <memory>
 
 #include "base/macros.h"
-#include "base/time/time.h"
-#include "content/renderer/media_recorder/audio_track_encoder.h"
 #include "media/base/audio_bus.h"
 #include "media/base/audio_parameters.h"
+#include "third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
 
-namespace content {
+namespace blink {
 
 // A signed, 16-bit linear audio "encoder" that will just pass the audio right
 // back out again.
@@ -31,6 +31,6 @@
   DISALLOW_COPY_AND_ASSIGN(AudioTrackPcmEncoder);
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_MEDIA_RECORDER_AUDIO_TRACK_PCM_ENCODER_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIARECORDER_AUDIO_TRACK_PCM_ENCODER_H_
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.cc
new file mode 100644
index 0000000..455f6c3
--- /dev/null
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.cc
@@ -0,0 +1,125 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/web/modules/mediarecorder/audio_track_recorder.h"
+
+#include "base/macros.h"
+#include "media/base/audio_bus.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/bind_to_current_loop.h"
+#include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_track.h"
+#include "third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.h"
+#include "third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.h"
+#include "third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+// Note that this code follows the Chrome media convention of defining a "frame"
+// as "one multi-channel sample" as opposed to another common definition meaning
+// "a chunk of samples". Here this second definition of "frame" is called a
+// "buffer"; so what might be called "frame duration" is instead "buffer
+// duration", and so on.
+
+namespace WTF {
+
+template <>
+struct CrossThreadCopier<media::AudioParameters> {
+  STATIC_ONLY(CrossThreadCopier);
+  using Type = media::AudioParameters;
+  static Type Copy(Type pointer) { return pointer; }
+};
+
+}  // namespace WTF
+
+namespace blink {
+
+AudioTrackRecorder::CodecId AudioTrackRecorder::GetPreferredCodecId() {
+  return CodecId::OPUS;
+}
+
+AudioTrackRecorder::AudioTrackRecorder(CodecId codec,
+                                       const WebMediaStreamTrack& track,
+                                       OnEncodedAudioCB on_encoded_audio_cb,
+                                       int32_t bits_per_second)
+    : track_(track),
+      encoder_(CreateAudioEncoder(codec,
+                                  std::move(on_encoded_audio_cb),
+                                  bits_per_second)),
+      encoder_thread_(Thread::CreateThread(
+          ThreadCreationParams(WebThreadType::kAudioEncoderThread))),
+      encoder_task_runner_(encoder_thread_->GetTaskRunner()) {
+  DCHECK(IsMainThread());
+  DCHECK(!track_.IsNull());
+  DCHECK(MediaStreamAudioTrack::From(track_));
+
+  // Connect the source provider to the track as a sink.
+  WebMediaStreamAudioSink::AddToAudioTrack(this, track_);
+}
+
+AudioTrackRecorder::~AudioTrackRecorder() {
+  DCHECK(IsMainThread());
+  WebMediaStreamAudioSink::RemoveFromAudioTrack(this, track_);
+}
+
+// Creates an audio encoder from the codec. Returns nullptr if the codec is
+// invalid.
+scoped_refptr<AudioTrackEncoder> AudioTrackRecorder::CreateAudioEncoder(
+    CodecId codec,
+    OnEncodedAudioCB on_encoded_audio_cb,
+    int32_t bits_per_second) {
+  if (codec == CodecId::PCM) {
+    return base::MakeRefCounted<AudioTrackPcmEncoder>(
+        media::BindToCurrentLoop(std::move(on_encoded_audio_cb)));
+  }
+
+  // All other paths will use the AudioTrackOpusEncoder.
+  return base::MakeRefCounted<AudioTrackOpusEncoder>(
+      media::BindToCurrentLoop(std::move(on_encoded_audio_cb)),
+      bits_per_second);
+}
+
+void AudioTrackRecorder::OnSetFormat(const media::AudioParameters& params) {
+  // If the source is restarted, might have changed to another capture thread.
+  DETACH_FROM_THREAD(capture_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(capture_thread_checker_);
+  PostCrossThreadTask(
+      *encoder_task_runner_.get(), FROM_HERE,
+      CrossThreadBindOnce(&AudioTrackEncoder::OnSetFormat, encoder_, params));
+}
+
+void AudioTrackRecorder::OnData(const media::AudioBus& audio_bus,
+                                base::TimeTicks capture_time) {
+  DCHECK_CALLED_ON_VALID_THREAD(capture_thread_checker_);
+  DCHECK(!capture_time.is_null());
+
+  std::unique_ptr<media::AudioBus> audio_data =
+      media::AudioBus::Create(audio_bus.channels(), audio_bus.frames());
+  audio_bus.CopyTo(audio_data.get());
+
+  PostCrossThreadTask(
+      *encoder_task_runner_.get(), FROM_HERE,
+      CrossThreadBindOnce(&AudioTrackEncoder::EncodeAudio, encoder_,
+                          std::move(audio_data), capture_time));
+}
+
+void AudioTrackRecorder::Pause() {
+  DCHECK(IsMainThread());
+  DCHECK(encoder_);
+  PostCrossThreadTask(
+      *encoder_task_runner_.get(), FROM_HERE,
+      CrossThreadBindOnce(&AudioTrackEncoder::set_paused, encoder_, true));
+}
+
+void AudioTrackRecorder::Resume() {
+  DCHECK(IsMainThread());
+  DCHECK(encoder_);
+  PostCrossThreadTask(
+      *encoder_task_runner_.get(), FROM_HERE,
+      CrossThreadBindOnce(&AudioTrackEncoder::set_paused, encoder_, false));
+}
+
+}  // namespace blink
diff --git a/content/renderer/media_recorder/audio_track_recorder_unittest.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder_unittest.cc
similarity index 87%
rename from content/renderer/media_recorder/audio_track_recorder_unittest.cc
rename to third_party/blink/renderer/modules/mediarecorder/audio_track_recorder_unittest.cc
index bfd83486..073d35d 100644
--- a/content/renderer/media_recorder/audio_track_recorder_unittest.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder_unittest.cc
@@ -2,19 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/media_recorder/audio_track_recorder.h"
+#include "third_party/blink/public/web/modules/mediarecorder/audio_track_recorder.h"
 
 #include <stdint.h>
 
 #include <string>
 
-#include "base/bind.h"
-#include "base/bind_helpers.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
-#include "base/stl_util.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/time/time.h"
 #include "media/audio/simple_sources.h"
 #include "media/base/audio_sample_types.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -23,8 +18,11 @@
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/web/web_heap.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
 #include "third_party/opus/src/include/opus.h"
 
+using base::TimeTicks;
 using ::testing::_;
 using ::testing::DoAll;
 using ::testing::InSequence;
@@ -33,7 +31,6 @@
 using ::testing::SaveArg;
 using ::testing::TestWithParam;
 using ::testing::ValuesIn;
-using base::TimeTicks;
 
 namespace {
 
@@ -52,9 +49,11 @@
 
 }  // namespace
 
-namespace content {
+namespace blink {
 
-ACTION_P(RunClosure, closure) {
+// Using RunClosure3 instead of RunClosure to avoid symbol collisions in jumbo
+// builds.
+ACTION_P(RunClosure3, closure) {
   closure.Run();
 }
 
@@ -119,8 +118,8 @@
     PrepareBlinkTrack();
     audio_track_recorder_.reset(new AudioTrackRecorder(
         codec_, blink_track_,
-        base::Bind(&AudioTrackRecorderTest::OnEncodedAudio,
-                   base::Unretained(this)),
+        WTF::BindRepeating(&AudioTrackRecorderTest::OnEncodedAudio,
+                           WTF::Unretained(this)),
         0 /* bits_per_second */));
   }
 
@@ -128,7 +127,7 @@
     opus_decoder_destroy(opus_decoder_);
     opus_decoder_ = nullptr;
     blink_track_.Reset();
-    blink::WebHeap::CollectAllGarbageForTesting();
+    WebHeap::CollectAllGarbageForTesting();
     audio_track_recorder_.reset();
     // Let the message loop run to finish destroying the recorder properly.
     base::RunLoop().RunUntilIdle();
@@ -153,8 +152,7 @@
         first_params_.channels(), first_params_.sample_rate() *
                                       kMediaStreamAudioTrackBufferDurationMs /
                                       base::Time::kMillisecondsPerSecond));
-    first_source_.OnMoreData(base::TimeDelta(), base::TimeTicks::Now(), 0,
-                             bus.get());
+    first_source_.OnMoreData(base::TimeDelta(), TimeTicks::Now(), 0, bus.get());
 
     // Save the samples that we read into the first_source_cache_.
     std::unique_ptr<media::AudioBus> cache_bus(
@@ -174,7 +172,7 @@
         second_params_.channels(), second_params_.sample_rate() *
                                        kMediaStreamAudioTrackBufferDurationMs /
                                        base::Time::kMillisecondsPerSecond));
-    second_source_.OnMoreData(base::TimeDelta(), base::TimeTicks::Now(), 0,
+    second_source_.OnMoreData(base::TimeDelta(), TimeTicks::Now(), 0,
                               bus.get());
     return bus;
   }
@@ -214,11 +212,9 @@
     DoOnEncodedAudio(params, *encoded_data, timestamp);
   }
 
-  const base::test::ScopedTaskEnvironment scoped_task_environment_;
-
   // ATR and WebMediaStreamTrack for fooling it.
   std::unique_ptr<AudioTrackRecorder> audio_track_recorder_;
-  blink::WebMediaStreamTrack blink_track_;
+  WebMediaStreamTrack blink_track_;
 
   // The codec we'll use for compression the audio.
   const AudioTrackRecorder::CodecId codec_;
@@ -237,7 +233,7 @@
 
   // Save the data we generate from the first source so that we might compare it
   // later if we happen to be using the PCM encoder.
-  std::vector<float> first_source_cache_;
+  Vector<float> first_source_cache_;
   size_t first_source_cache_pos_;
 
  private:
@@ -245,17 +241,15 @@
   // track, which can be used to capture audio data and pass it to the producer.
   // Adapted from media::WebRTCLocalAudioSourceProviderTest.
   void PrepareBlinkTrack() {
-    blink::WebMediaStreamSource audio_source;
-    audio_source.Initialize(blink::WebString::FromUTF8("dummy_source_id"),
-                            blink::WebMediaStreamSource::kTypeAudio,
-                            blink::WebString::FromUTF8("dummy_source_name"),
+    WebMediaStreamSource audio_source;
+    audio_source.Initialize(WebString::FromUTF8("dummy_source_id"),
+                            WebMediaStreamSource::kTypeAudio,
+                            WebString::FromUTF8("dummy_source_name"),
                             false /* remote */);
-    audio_source.SetPlatformSource(
-        std::make_unique<blink::MediaStreamAudioSource>(
-            blink::scheduler::GetSingleThreadTaskRunnerForTesting(), true));
-    blink_track_.Initialize(blink::WebString::FromUTF8("audio_track"),
-                            audio_source);
-    CHECK(blink::MediaStreamAudioSource::From(audio_source)
+    audio_source.SetPlatformSource(std::make_unique<MediaStreamAudioSource>(
+        scheduler::GetSingleThreadTaskRunnerForTesting(), true));
+    blink_track_.Initialize(WebString::FromUTF8("audio_track"), audio_source);
+    CHECK(MediaStreamAudioSource::From(audio_source)
               ->ConnectToTrack(blink_track_));
   }
 
@@ -283,8 +277,9 @@
   EXPECT_CALL(*this, DoOnEncodedAudio(_, _, _))
       .Times(1)
       // Only reset the decoder once we've heard back:
-      .WillOnce(RunClosure(base::Bind(&AudioTrackRecorderTest::ResetDecoder,
-                                      base::Unretained(this), second_params_)));
+      .WillOnce(RunClosure3(
+          WTF::BindRepeating(&AudioTrackRecorderTest::ResetDecoder,
+                             WTF::Unretained(this), second_params_)));
   audio_track_recorder_->OnData(*GetFirstSourceAudioBus(), TimeTicks::Now());
   for (int i = 0; i < kRatioInputToOutputFrames - 1; ++i)
     audio_track_recorder_->OnData(*GetFirstSourceAudioBus(), TimeTicks::Now());
@@ -300,7 +295,7 @@
   // Send audio with different params.
   EXPECT_CALL(*this, DoOnEncodedAudio(_, _, _))
       .Times(1)
-      .WillOnce(RunClosure(std::move(quit_closure)));
+      .WillOnce(RunClosure3(std::move(quit_closure)));
   audio_track_recorder_->OnData(*GetSecondSourceAudioBus(), TimeTicks::Now());
   for (int i = 0; i < kRatioInputToOutputFrames - 1; ++i)
     audio_track_recorder_->OnData(*GetSecondSourceAudioBus(), TimeTicks::Now());
@@ -321,7 +316,7 @@
 
   EXPECT_CALL(*this, DoOnEncodedAudio(_, _, _)).Times(5);
   EXPECT_CALL(*this, DoOnEncodedAudio(_, _, _))
-      .WillOnce(RunClosure(std::move(quit_closure)));
+      .WillOnce(RunClosure3(std::move(quit_closure)));
 
   audio_track_recorder_->OnData(*GetFirstSourceAudioBus(), TimeTicks::Now());
   for (int i = 0; i < kRatioInputToOutputFrames - 1; ++i)
@@ -351,7 +346,7 @@
   audio_track_recorder_->Resume();
   EXPECT_CALL(*this, DoOnEncodedAudio(_, _, _))
       .Times(1)
-      .WillOnce(RunClosure(std::move(quit_closure)));
+      .WillOnce(RunClosure3(std::move(quit_closure)));
   audio_track_recorder_->OnData(*GetFirstSourceAudioBus(), TimeTicks::Now());
   for (int i = 0; i < kRatioInputToOutputFrames - 1; ++i)
     audio_track_recorder_->OnData(*GetFirstSourceAudioBus(), TimeTicks::Now());
@@ -364,4 +359,4 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(, AudioTrackRecorderTest, ValuesIn(kATRTestParams));
-}  // namespace content
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.cc
index 831fe0c..6c6e75b5c 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.cc
@@ -111,8 +111,8 @@
   if (!internal_state_->frame())
     return;
   internal_state_->GetMediaStreamDispatcherHost()->SetCapturingLinkSecured(
-      device().session_id, static_cast<mojom::MediaStreamType>(device().type),
-      is_secure);
+      device().session_id,
+      static_cast<mojom::blink::MediaStreamType>(device().type), is_secure);
 }
 
 void MediaStreamVideoCapturerSource::StartSourceImpl(
@@ -142,7 +142,7 @@
 
   // Force state update for nondevice sources, since they do not
   // automatically update state after StopCapture().
-  if (device().type == MEDIA_NO_SERVICE)
+  if (device().type == mojom::blink::MediaStreamType::NO_SERVICE)
     OnRunStateChanged(capture_params_, false);
 }
 
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc
index 8955620..cccb172 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc
@@ -390,8 +390,9 @@
   // |ChangeSourceImpl()| will recreate the |delegate_|, so check the
   // |MockStartCapture()| invoking in the |RecreateVideoCapturerSource()|.
   EXPECT_CALL(mock_delegate(), MockStopCapture());
-  MediaStreamDevice fake_video_device(MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
-                                      "Fake_Video_Device", "Fake Video Device");
+  MediaStreamDevice fake_video_device(
+      mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE, "Fake_Video_Device",
+      "Fake Video Device");
   source_->ChangeSourceImpl(fake_video_device);
   EXPECT_EQ(WebMediaStreamSource::kReadyStateLive,
             webkit_source_.GetReadyState());
diff --git a/third_party/blink/renderer/modules/mediastream/video_track_adapter.cc b/third_party/blink/renderer/modules/mediastream/video_track_adapter.cc
index 34b40e2..3edc36d 100644
--- a/third_party/blink/renderer/modules/mediastream/video_track_adapter.cc
+++ b/third_party/blink/renderer/modules/mediastream/video_track_adapter.cc
@@ -683,7 +683,7 @@
            << (kFirstFrameTimeoutInFrameIntervals / source_frame_rate_) << "s";
   PostDelayedCrossThreadTask(
       *io_task_runner_, FROM_HERE,
-      CrossThreadBind(
+      CrossThreadBindOnce(
           &VideoTrackAdapter::CheckFramesReceivedOnIO, WrapRefCounted(this),
           WTF::Passed(std::move(on_muted_callback)), frame_counter_),
       base::TimeDelta::FromSecondsD(kFirstFrameTimeoutInFrameIntervals /
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index 69fb1e3..ed60acb 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -151,6 +151,7 @@
           "filesystem/file_writer_sync.idl",
           "filesystem/metadata.idl",
           "filesystem/metadata_callback.idl",
+          "launch/launch_params.idl",
           "gamepad/gamepad.idl",
           "gamepad/gamepad_axis_event.idl",
           "gamepad/gamepad_button.idl",
@@ -655,12 +656,13 @@
           "native_file_system/choose_file_system_entries_options_accepts.idl",
           "native_file_system/file_system_get_directory_options.idl",
           "native_file_system/file_system_get_file_options.idl",
+          "native_file_system/file_system_handle_permission_descriptor.idl",
           "native_file_system/get_system_directory_options.idl",
           "native_file_system/native_file_system_directory_iterator_entry.idl",
           "nfc/ndef_message.idl",
           "nfc/nfc_push_options.idl",
           "nfc/ndef_record.idl",
-          "nfc/nfc_watch_options.idl",
+          "nfc/nfc_reader_options.idl",
           "notifications/get_notification_options.idl",
           "notifications/notification_action.idl",
           "notifications/notification_event_init.idl",
@@ -897,6 +899,7 @@
           "filesystem/html_input_element_file_system.idl",
           "filesystem/shared_worker_global_scope_file_system.idl",
           "filesystem/window_file_system.idl",
+          "launch/dom_window_launch_params.idl",
           "gamepad/navigator_gamepad.idl",
           "geolocation/navigator_geolocation.idl",
           "hid/navigator_hid.idl",
diff --git a/third_party/blink/renderer/modules/native_file_system/file_system_handle.idl b/third_party/blink/renderer/modules/native_file_system/file_system_handle.idl
index d166763d..3572ab7 100644
--- a/third_party/blink/renderer/modules/native_file_system/file_system_handle.idl
+++ b/third_party/blink/renderer/modules/native_file_system/file_system_handle.idl
@@ -15,9 +15,10 @@
 
     readonly attribute USVString name;
 
-    [CallWith=ScriptState] Promise<FileSystemHandle> moveTo(
-        FileSystemDirectoryHandle parent, optional USVString name);
-    [CallWith=ScriptState] Promise<FileSystemHandle> copyTo(
-        FileSystemDirectoryHandle parent, optional USVString name);
     [CallWith=ScriptState] Promise<void> remove();
+
+    [CallWith = ScriptState] Promise<PermissionState> queryPermission(
+        optional FileSystemHandlePermissionDescriptor descriptor);
+    [CallWith = ScriptState] Promise<PermissionState> requestPermission(
+        optional FileSystemHandlePermissionDescriptor descriptor);
 };
diff --git a/third_party/blink/renderer/modules/native_file_system/file_system_handle_permission_descriptor.idl b/third_party/blink/renderer/modules/native_file_system/file_system_handle_permission_descriptor.idl
new file mode 100644
index 0000000..d37a997
--- /dev/null
+++ b/third_party/blink/renderer/modules/native_file_system/file_system_handle_permission_descriptor.idl
@@ -0,0 +1,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.
+
+// http://wicg.github.io/native-file-system/#dictdef-filesystemhandlepermissiondescriptor
+dictionary FileSystemHandlePermissionDescriptor {
+  boolean writable = false;
+};
diff --git a/third_party/blink/renderer/modules/native_file_system/native_file_system_directory_handle.cc b/third_party/blink/renderer/modules/native_file_system/native_file_system_directory_handle.cc
index 8dc269c..d10d1d3 100644
--- a/third_party/blink/renderer/modules/native_file_system/native_file_system_directory_handle.cc
+++ b/third_party/blink/renderer/modules/native_file_system/native_file_system_directory_handle.cc
@@ -182,4 +182,16 @@
   mojo_ptr_->Remove(/*recursive=*/false, std::move(callback));
 }
 
+void NativeFileSystemDirectoryHandle::QueryPermissionImpl(
+    bool writable,
+    base::OnceCallback<void(mojom::blink::PermissionStatus)> callback) {
+  mojo_ptr_->GetPermissionStatus(writable, std::move(callback));
+}
+
+void NativeFileSystemDirectoryHandle::RequestPermissionImpl(
+    bool writable,
+    base::OnceCallback<void(mojom::blink::PermissionStatus)> callback) {
+  mojo_ptr_->RequestPermission(writable, std::move(callback));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/native_file_system/native_file_system_directory_handle.h b/third_party/blink/renderer/modules/native_file_system/native_file_system_directory_handle.h
index 882b6e13..9c2b199c 100644
--- a/third_party/blink/renderer/modules/native_file_system/native_file_system_directory_handle.h
+++ b/third_party/blink/renderer/modules/native_file_system/native_file_system_directory_handle.h
@@ -45,6 +45,12 @@
   void RemoveImpl(
       base::OnceCallback<void(mojom::blink::NativeFileSystemErrorPtr)>)
       override;
+  void QueryPermissionImpl(
+      bool writable,
+      base::OnceCallback<void(mojom::blink::PermissionStatus)>) override;
+  void RequestPermissionImpl(
+      bool writable,
+      base::OnceCallback<void(mojom::blink::PermissionStatus)>) override;
 
   mojom::blink::NativeFileSystemDirectoryHandlePtr mojo_ptr_;
 };
diff --git a/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.cc b/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.cc
index b3a264b..417c841 100644
--- a/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.cc
+++ b/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.cc
@@ -69,9 +69,22 @@
   mojo_ptr_->Transfer(mojo::MakeRequest(&result));
   return result;
 }
+
 void NativeFileSystemFileHandle::RemoveImpl(
     base::OnceCallback<void(mojom::blink::NativeFileSystemErrorPtr)> callback) {
   mojo_ptr_->Remove(std::move(callback));
 }
 
+void NativeFileSystemFileHandle::QueryPermissionImpl(
+    bool writable,
+    base::OnceCallback<void(mojom::blink::PermissionStatus)> callback) {
+  mojo_ptr_->GetPermissionStatus(writable, std::move(callback));
+}
+
+void NativeFileSystemFileHandle::RequestPermissionImpl(
+    bool writable,
+    base::OnceCallback<void(mojom::blink::PermissionStatus)> callback) {
+  mojo_ptr_->RequestPermission(writable, std::move(callback));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.h b/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.h
index 8994ef9..296e385 100644
--- a/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.h
+++ b/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.h
@@ -33,6 +33,12 @@
   void RemoveImpl(
       base::OnceCallback<void(mojom::blink::NativeFileSystemErrorPtr)>)
       override;
+  void QueryPermissionImpl(
+      bool writable,
+      base::OnceCallback<void(mojom::blink::PermissionStatus)>) override;
+  void RequestPermissionImpl(
+      bool writable,
+      base::OnceCallback<void(mojom::blink::PermissionStatus)>) override;
 
   mojom::blink::NativeFileSystemFileHandlePtr mojo_ptr_;
 };
diff --git a/third_party/blink/renderer/modules/native_file_system/native_file_system_handle.cc b/third_party/blink/renderer/modules/native_file_system/native_file_system_handle.cc
index a0b99b7..5b528237b 100644
--- a/third_party/blink/renderer/modules/native_file_system/native_file_system_handle.cc
+++ b/third_party/blink/renderer/modules/native_file_system/native_file_system_handle.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/fileapi/file_error.h"
+#include "third_party/blink/renderer/modules/native_file_system/file_system_handle_permission_descriptor.h"
 #include "third_party/blink/renderer/modules/native_file_system/native_file_system_directory_handle.h"
 #include "third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -32,56 +33,6 @@
                    std::move(e->entry_handle->get_directory())));
 }
 
-ScriptPromise NativeFileSystemHandle::moveTo(
-    ScriptState* script_state,
-    NativeFileSystemDirectoryHandle* parent,
-    const String& new_name) {
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise result = resolver->Promise();
-
-  parent->MojoHandle()->MoveFrom(
-      Transfer(), new_name.IsEmpty() ? name() : new_name,
-      WTF::Bind(
-          [](ScriptPromiseResolver* resolver, NativeFileSystemErrorPtr result,
-             NativeFileSystemEntryPtr entry) {
-            if (result->error_code == base::File::FILE_OK) {
-              resolver->Resolve(NativeFileSystemHandle::CreateFromMojoEntry(
-                  std::move(entry)));
-            } else {
-              resolver->Reject(
-                  file_error::CreateDOMException(result->error_code));
-            }
-          },
-          WrapPersistent(resolver)));
-
-  return result;
-}
-
-ScriptPromise NativeFileSystemHandle::copyTo(
-    ScriptState* script_state,
-    NativeFileSystemDirectoryHandle* parent,
-    const String& new_name) {
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise result = resolver->Promise();
-
-  parent->MojoHandle()->CopyFrom(
-      Transfer(), new_name.IsEmpty() ? name() : new_name,
-      WTF::Bind(
-          [](ScriptPromiseResolver* resolver, NativeFileSystemErrorPtr result,
-             NativeFileSystemEntryPtr entry) {
-            if (result->error_code == base::File::FILE_OK) {
-              resolver->Resolve(NativeFileSystemHandle::CreateFromMojoEntry(
-                  std::move(entry)));
-            } else {
-              resolver->Reject(
-                  file_error::CreateDOMException(result->error_code));
-            }
-          },
-          WrapPersistent(resolver)));
-
-  return result;
-}
-
 ScriptPromise NativeFileSystemHandle::remove(ScriptState* script_state) {
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise result = resolver->Promise();
@@ -99,4 +50,55 @@
   return result;
 }
 
+namespace {
+String MojoPermissionStatusToString(mojom::blink::PermissionStatus status) {
+  switch (status) {
+    case mojom::blink::PermissionStatus::GRANTED:
+      return "granted";
+    case mojom::blink::PermissionStatus::DENIED:
+      return "denied";
+    case mojom::blink::PermissionStatus::ASK:
+      return "prompt";
+  }
+  NOTREACHED();
+  return "denied";
+}
+}  // namespace
+
+ScriptPromise NativeFileSystemHandle::queryPermission(
+    ScriptState* script_state,
+    const FileSystemHandlePermissionDescriptor* descriptor) {
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  ScriptPromise result = resolver->Promise();
+
+  QueryPermissionImpl(
+      descriptor->writable(),
+      WTF::Bind(
+          [](ScriptPromiseResolver* resolver,
+             mojom::blink::PermissionStatus result) {
+            resolver->Resolve(MojoPermissionStatusToString(result));
+          },
+          WrapPersistent(resolver)));
+
+  return result;
+}
+
+ScriptPromise NativeFileSystemHandle::requestPermission(
+    ScriptState* script_state,
+    const FileSystemHandlePermissionDescriptor* descriptor) {
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  ScriptPromise result = resolver->Promise();
+
+  RequestPermissionImpl(
+      descriptor->writable(),
+      WTF::Bind(
+          [](ScriptPromiseResolver* resolver,
+             mojom::blink::PermissionStatus result) {
+            resolver->Resolve(MojoPermissionStatusToString(result));
+          },
+          WrapPersistent(resolver)));
+
+  return result;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/native_file_system/native_file_system_handle.h b/third_party/blink/renderer/modules/native_file_system/native_file_system_handle.h
index f3f846fd..94413e7 100644
--- a/third_party/blink/renderer/modules/native_file_system/native_file_system_handle.h
+++ b/third_party/blink/renderer/modules/native_file_system/native_file_system_handle.h
@@ -8,6 +8,7 @@
 #include "third_party/blink/public/mojom/native_file_system/native_file_system_directory_handle.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/native_file_system/native_file_system_error.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/native_file_system/native_file_system_transfer_token.mojom-blink-forward.h"
+#include "third_party/blink/public/mojom/permissions/permission_status.mojom-blink-forward.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -16,6 +17,7 @@
 
 namespace blink {
 class NativeFileSystemDirectoryHandle;
+class FileSystemHandlePermissionDescriptor;
 
 class NativeFileSystemHandle : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
@@ -37,11 +39,22 @@
                        const String& new_name = String());
   ScriptPromise remove(ScriptState*);
 
+  ScriptPromise queryPermission(ScriptState*,
+                                const FileSystemHandlePermissionDescriptor*);
+  ScriptPromise requestPermission(ScriptState*,
+                                  const FileSystemHandlePermissionDescriptor*);
+
   virtual mojom::blink::NativeFileSystemTransferTokenPtr Transfer() = 0;
 
  private:
   virtual void RemoveImpl(
       base::OnceCallback<void(mojom::blink::NativeFileSystemErrorPtr)>) = 0;
+  virtual void QueryPermissionImpl(
+      bool writable,
+      base::OnceCallback<void(mojom::blink::PermissionStatus)>) = 0;
+  virtual void RequestPermissionImpl(
+      bool writable,
+      base::OnceCallback<void(mojom::blink::PermissionStatus)>) = 0;
 
   String name_;
 };
diff --git a/third_party/blink/renderer/modules/nfc/nfc.cc b/third_party/blink/renderer/modules/nfc/nfc.cc
index 638106b..447befc 100644
--- a/third_party/blink/renderer/modules/nfc/nfc.cc
+++ b/third_party/blink/renderer/modules/nfc/nfc.cc
@@ -19,7 +19,7 @@
 #include "third_party/blink/renderer/modules/nfc/ndef_message.h"
 #include "third_party/blink/renderer/modules/nfc/nfc_error.h"
 #include "third_party/blink/renderer/modules/nfc/nfc_push_options.h"
-#include "third_party/blink/renderer/modules/nfc/nfc_watch_options.h"
+#include "third_party/blink/renderer/modules/nfc/nfc_reader_options.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/mojo/mojo_helper.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
@@ -38,6 +38,7 @@
 // Mojo type converters
 namespace mojo {
 
+using device::mojom::blink::NDEFCompatibility;
 using device::mojom::blink::NDEFMessage;
 using device::mojom::blink::NDEFMessagePtr;
 using device::mojom::blink::NDEFRecord;
@@ -47,11 +48,10 @@
 using device::mojom::blink::NFCPushOptions;
 using device::mojom::blink::NFCPushOptionsPtr;
 using device::mojom::blink::NFCPushTarget;
-using device::mojom::blink::NFCWatchMode;
-using device::mojom::blink::NFCWatchOptions;
-using device::mojom::blink::NFCWatchOptionsPtr;
+using device::mojom::blink::NFCReaderOptions;
+using device::mojom::blink::NFCReaderOptionsPtr;
 
-NFCPushTarget toNFCPushTarget(const String& target) {
+NFCPushTarget ToNFCPushTarget(const String& target) {
   if (target == "tag")
     return NFCPushTarget::TAG;
 
@@ -61,7 +61,7 @@
   return NFCPushTarget::ANY;
 }
 
-NDEFRecordType toNDEFRecordType(const String& recordType) {
+NDEFRecordType ToNDEFRecordType(const String& recordType) {
   if (recordType == "empty")
     return NDEFRecordType::EMPTY;
 
@@ -81,15 +81,18 @@
   return NDEFRecordType::EMPTY;
 }
 
-NFCWatchMode toNFCWatchMode(const String& watchMode) {
-  if (watchMode == "web-nfc-only")
-    return NFCWatchMode::WEBNFC_ONLY;
+NDEFCompatibility ToNDEFCompatibility(const String& compatibility) {
+  if (compatibility == "nfc-forum")
+    return NDEFCompatibility::NFC_FORUM;
 
-  if (watchMode == "any")
-    return NFCWatchMode::ANY;
+  if (compatibility == "vendor")
+    return NDEFCompatibility::VENDOR;
+
+  if (compatibility == "any")
+    return NDEFCompatibility::ANY;
 
   NOTREACHED();
-  return NFCWatchMode::WEBNFC_ONLY;
+  return NDEFCompatibility::NFC_FORUM;
 }
 
 // https://w3c.github.io/web-nfc/#creating-web-nfc-message Step 2.1
@@ -221,7 +224,7 @@
     NDEFRecordPtr recordPtr = NDEFRecord::New();
 
     if (record->hasRecordType())
-      recordPtr->record_type = toNDEFRecordType(record->recordType());
+      recordPtr->record_type = ToNDEFRecordType(record->recordType());
     else
       recordPtr->record_type = deduceRecordTypeFromDataType(record);
 
@@ -312,7 +315,7 @@
     NFCPushOptionsPtr pushOptionsPtr = NFCPushOptions::New();
 
     if (pushOptions->hasTarget())
-      pushOptionsPtr->target = toNFCPushTarget(pushOptions->target());
+      pushOptionsPtr->target = ToNFCPushTarget(pushOptions->target());
     else
       pushOptionsPtr->target = NFCPushTarget::ANY;
 
@@ -331,25 +334,22 @@
 };
 
 template <>
-struct TypeConverter<NFCWatchOptionsPtr, const blink::NFCWatchOptions*> {
-  static NFCWatchOptionsPtr Convert(
-      const blink::NFCWatchOptions* watchOptions) {
-    // https://w3c.github.io/web-nfc/#the-nfcwatchoptions-dictionary
-    // Default values for NFCWatchOptions dictionary are:
-    // url = "", recordType = null, mediaType = "", mode = "web-nfc-only"
-    NFCWatchOptionsPtr watchOptionsPtr = NFCWatchOptions::New();
+struct TypeConverter<NFCReaderOptionsPtr, const blink::NFCReaderOptions*> {
+  static NFCReaderOptionsPtr Convert(
+      const blink::NFCReaderOptions* watchOptions) {
+    // https://w3c.github.io/web-nfc/#dom-nfcreaderoptions
+    // Default values for NFCReaderOptions dictionary are:
+    // url = "", recordType = null, mediaType = "", compatibility = "nfc-forum"
+    NFCReaderOptionsPtr watchOptionsPtr = NFCReaderOptions::New();
     watchOptionsPtr->url = watchOptions->url();
     watchOptionsPtr->media_type = watchOptions->mediaType();
-
-    if (watchOptions->hasMode())
-      watchOptionsPtr->mode = toNFCWatchMode(watchOptions->mode());
-    else
-      watchOptionsPtr->mode = NFCWatchMode::WEBNFC_ONLY;
+    watchOptionsPtr->compatibility =
+        ToNDEFCompatibility(watchOptions->compatibility());
 
     if (watchOptions->hasRecordType()) {
       watchOptionsPtr->record_filter = NDEFRecordTypeFilter::New();
       watchOptionsPtr->record_filter->record_type =
-          toNDEFRecordType(watchOptions->recordType());
+          ToNDEFRecordType(watchOptions->recordType());
     }
 
     return watchOptionsPtr;
@@ -446,7 +446,7 @@
                                         const NDEFRecord* record) {
   device::mojom::blink::NDEFRecordType type;
   if (record->hasRecordType()) {
-    type = mojo::toNDEFRecordType(record->recordType());
+    type = mojo::ToNDEFRecordType(record->recordType());
   } else {
     type = mojo::deduceRecordTypeFromDataType(record);
 
@@ -751,7 +751,7 @@
   requests_.insert(resolver);
   auto callback = WTF::Bind(&NFC::OnRequestCompleted, WrapPersistent(this),
                             WrapPersistent(resolver));
-  nfc_->CancelPush(mojo::toNFCPushTarget(target), std::move(callback));
+  nfc_->CancelPush(mojo::ToNFCPushTarget(target), std::move(callback));
 
   return resolver->Promise();
 }
@@ -760,7 +760,7 @@
 // https://w3c.github.io/web-nfc/#dom-nfc-watch
 ScriptPromise NFC::watch(ScriptState* script_state,
                          V8MessageCallback* callback,
-                         const NFCWatchOptions* options) {
+                         const NFCReaderOptions* options) {
   ScriptPromise promise = RejectIfNotSupported(script_state);
   if (!promise.IsEmpty())
     return promise;
@@ -781,7 +781,7 @@
       WTF::Bind(&NFC::OnWatchRegistered, WrapPersistent(this),
                 WrapPersistent(ToV8PersistentCallbackFunction(callback)),
                 WrapPersistent(resolver));
-  nfc_->Watch(device::mojom::blink::NFCWatchOptions::From(options),
+  nfc_->Watch(device::mojom::blink::NFCReaderOptions::From(options),
               std::move(watch_callback));
   return resolver->Promise();
 }
diff --git a/third_party/blink/renderer/modules/nfc/nfc.h b/third_party/blink/renderer/modules/nfc/nfc.h
index e491839e9..4ab14d1 100644
--- a/third_party/blink/renderer/modules/nfc/nfc.h
+++ b/third_party/blink/renderer/modules/nfc/nfc.h
@@ -20,7 +20,7 @@
 class NFCPushOptions;
 using NDEFMessageSource = StringOrArrayBufferOrNDEFMessage;
 using NDEFRecordData = StringOrUnrestrictedDoubleOrArrayBufferOrDictionary;
-class NFCWatchOptions;
+class NFCReaderOptions;
 class ScriptPromiseResolver;
 
 class NFC final : public ScriptWrappable,
@@ -50,8 +50,10 @@
   // Cancels ongoing push operation.
   ScriptPromise cancelPush(ScriptState*, const String&);
 
-  // Starts watching for NFC messages that match NFCWatchOptions criteria.
-  ScriptPromise watch(ScriptState*, V8MessageCallback*, const NFCWatchOptions*);
+  // Starts watching for NFC messages that match NFCReaderOptions criteria.
+  ScriptPromise watch(ScriptState*,
+                      V8MessageCallback*,
+                      const NFCReaderOptions*);
 
   // Cancels watch operation with id.
   ScriptPromise cancelWatch(ScriptState*, int32_t id);
diff --git a/third_party/blink/renderer/modules/nfc/nfc.idl b/third_party/blink/renderer/modules/nfc/nfc.idl
index 497a1c0e..4274711c 100644
--- a/third_party/blink/renderer/modules/nfc/nfc.idl
+++ b/third_party/blink/renderer/modules/nfc/nfc.idl
@@ -14,7 +14,7 @@
 ] interface NFC {
     [CallWith=ScriptState, MeasureAs=WebNFCPush] Promise<void> push (NDEFMessageSource message, optional NFCPushOptions options);
     [CallWith=ScriptState, MeasureAs=WebNFCCancelPush] Promise<void> cancelPush (optional NFCPushTarget target = "any");
-    [CallWith=ScriptState, MeasureAs=WebNFCWatch] Promise<long> watch (MessageCallback callback, optional NFCWatchOptions options);
+    [CallWith=ScriptState, MeasureAs=WebNFCWatch] Promise<long> watch (MessageCallback callback, optional NFCReaderOptions options);
     [CallWith=ScriptState, MeasureAs=WebNFCCancelWatch] Promise<void> cancelWatch (optional long id);
 };
 
diff --git a/third_party/blink/renderer/modules/nfc/nfc_reader_options.idl b/third_party/blink/renderer/modules/nfc/nfc_reader_options.idl
new file mode 100644
index 0000000..c7c8c375
--- /dev/null
+++ b/third_party/blink/renderer/modules/nfc/nfc_reader_options.idl
@@ -0,0 +1,15 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+// http://w3c.github.io/web-nfc/#dom-ndefcompatibility
+enum NDEFCompatibility { "nfc-forum", "vendor", "any" };
+
+// https://w3c.github.io/web-nfc/#dom-nfcreaderoptions
+dictionary NFCReaderOptions {
+    USVString url = "";
+    NDEFRecordType? recordType;
+    USVString mediaType = "";
+    NDEFCompatibility compatibility = "nfc-forum";
+};
diff --git a/third_party/blink/renderer/modules/nfc/nfc_watch_options.idl b/third_party/blink/renderer/modules/nfc/nfc_watch_options.idl
deleted file mode 100644
index 7b3dcb6..0000000
--- a/third_party/blink/renderer/modules/nfc/nfc_watch_options.idl
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// https://w3c.github.io/web-nfc/#the-nfcwatchoptions-dictionary
-
-enum NFCWatchMode { "web-nfc-only", "any" };
-
-dictionary NFCWatchOptions {
-    USVString url = "";
-    NDEFRecordType? recordType;
-    USVString mediaType = "";
-    NFCWatchMode mode = "web-nfc-only";
-};
diff --git a/third_party/blink/renderer/modules/payments/can_make_payment_event.cc b/third_party/blink/renderer/modules/payments/can_make_payment_event.cc
index f428ede..f30ba00 100644
--- a/third_party/blink/renderer/modules/payments/can_make_payment_event.cc
+++ b/third_party/blink/renderer/modules/payments/can_make_payment_event.cc
@@ -9,7 +9,6 @@
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
 #include "third_party/blink/renderer/core/workers/worker_location.h"
 #include "third_party/blink/renderer/modules/service_worker/respond_with_observer.h"
-#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 
diff --git a/third_party/blink/renderer/modules/payments/merchant_validation_event.cc b/third_party/blink/renderer/modules/payments/merchant_validation_event.cc
index 56c98cb9..80139c8 100644
--- a/third_party/blink/renderer/modules/payments/merchant_validation_event.cc
+++ b/third_party/blink/renderer/modules/payments/merchant_validation_event.cc
@@ -11,7 +11,6 @@
 #include "third_party/blink/renderer/core/workers/worker_location.h"
 #include "third_party/blink/renderer/modules/payments/payments_validators.h"
 #include "third_party/blink/renderer/modules/service_worker/respond_with_observer.h"
-#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc
index 1d1439d..f6392fd 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request.cc
@@ -1524,7 +1524,7 @@
     const String& message = String::Format(
         "No updateWith() call in '%s' event handler. User may see outdated "
         "line items and total.",
-        event->type().Ascii().data());
+        event->type().Ascii().c_str());
     GetExecutionContext()->AddConsoleMessage(
         ConsoleMessage::Create(mojom::ConsoleMessageSource::kJavaScript,
                                mojom::ConsoleMessageLevel::kWarning, message));
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_dtmf_sender.cc b/third_party/blink/renderer/modules/peerconnection/rtc_dtmf_sender.cc
index c70275eb..3f54777 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_dtmf_sender.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_dtmf_sender.cc
@@ -103,7 +103,8 @@
     return;
   }
   // Spec: Throw on illegal characters
-  if (strspn(tones.Ascii().data(), "0123456789abcdABCD#*,") != tones.length()) {
+  if (strspn(tones.Ascii().c_str(), "0123456789abcdABCD#*,") !=
+      tones.length()) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidCharacterError,
         "Illegal characters in InsertDTMF tone argument");
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.cc
index 57f079a..9e2e179 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.cc
@@ -216,8 +216,7 @@
           DOMExceptionCode::kInvalidModificationError, "Invalid codec");
       return;
     }
-    webrtc_codec.name =
-        codec->mimeType().Substring(slash_position + 1).Ascii().data();
+    webrtc_codec.name = codec->mimeType().Substring(slash_position + 1).Ascii();
     webrtc_codec.clock_rate = codec->clockRate();
     if (codec->hasChannels()) {
       webrtc_codec.num_channels = codec->channels();
@@ -235,7 +234,7 @@
         auto parameter_name = parameter.Left(equal_position);
         auto parameter_value = parameter.Substring(equal_position + 1);
         webrtc_codec.parameters.insert(std::make_pair<std::string, std::string>(
-            parameter_name.Ascii().data(), parameter_value.Ascii().data()));
+            parameter_name.Ascii(), parameter_value.Ascii()));
       }
     }
   }
diff --git a/third_party/blink/renderer/modules/presentation/presentation_availability_callbacks.cc b/third_party/blink/renderer/modules/presentation/presentation_availability_callbacks.cc
index fb026f7..d365e7c6 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_availability_callbacks.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_availability_callbacks.cc
@@ -49,4 +49,8 @@
   resolver_->Reject(CreateAvailabilityNotSupportedError());
 }
 
+void PresentationAvailabilityCallbacks::Trace(blink::Visitor* visitor) {
+  visitor->Trace(resolver_);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/presentation/presentation_availability_callbacks.h b/third_party/blink/renderer/modules/presentation/presentation_availability_callbacks.h
index fb6d3acc..7277217 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_availability_callbacks.h
+++ b/third_party/blink/renderer/modules/presentation/presentation_availability_callbacks.h
@@ -19,9 +19,8 @@
 // depending on the availability result.
 // TODO(crbug.com/749327): Consider removing this class and have
 // PresentationAvailabilityState use PresentationAvailabilityProperty directly.
-class MODULES_EXPORT PresentationAvailabilityCallbacks {
-  USING_FAST_MALLOC(PresentationAvailabilityCallbacks);
-
+class MODULES_EXPORT PresentationAvailabilityCallbacks
+    : public GarbageCollectedFinalized<PresentationAvailabilityCallbacks> {
  public:
   PresentationAvailabilityCallbacks(PresentationAvailabilityProperty*,
                                     const WTF::Vector<KURL>&);
@@ -30,8 +29,10 @@
   virtual void Resolve(bool value);
   virtual void RejectAvailabilityNotSupported();
 
+  void Trace(blink::Visitor*);
+
  private:
-  Persistent<PresentationAvailabilityProperty> resolver_;
+  Member<PresentationAvailabilityProperty> resolver_;
   const WTF::Vector<KURL> urls_;
 
   DISALLOW_COPY_AND_ASSIGN(PresentationAvailabilityCallbacks);
diff --git a/third_party/blink/renderer/modules/presentation/presentation_availability_observer.h b/third_party/blink/renderer/modules/presentation/presentation_availability_observer.h
index 9d6cda3..efd4203 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_availability_observer.h
+++ b/third_party/blink/renderer/modules/presentation/presentation_availability_observer.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PRESENTATION_PRESENTATION_AVAILABILITY_OBSERVER_H_
 
 #include "third_party/blink/public/mojom/presentation/presentation.mojom-blink.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
@@ -15,7 +16,7 @@
 // PresentationAvailabilityObserver is an interface that is implemented by
 // objects that wish to be notified when there is a presentation display
 // availability change for given URLs.
-class PresentationAvailabilityObserver {
+class PresentationAvailabilityObserver : public GarbageCollectedMixin {
  public:
   virtual ~PresentationAvailabilityObserver() = default;
 
diff --git a/third_party/blink/renderer/modules/presentation/presentation_availability_state.cc b/third_party/blink/renderer/modules/presentation/presentation_availability_state.cc
index f40385e..e78fd2e0 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_availability_state.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_availability_state.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/modules/presentation/presentation_availability_observer.h"
 #include "third_party/blink/renderer/modules/presentation/presentation_controller.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 
@@ -20,7 +21,7 @@
 
 void PresentationAvailabilityState::RequestAvailability(
     const Vector<KURL>& urls,
-    std::unique_ptr<PresentationAvailabilityCallbacks> callback) {
+    PresentationAvailabilityCallbacks* callback) {
   auto screen_availability = GetScreenAvailability(urls);
   // Reject Promise if screen availability is unsupported for all URLs.
   if (screen_availability == mojom::blink::ScreenAvailability::DISABLED) {
@@ -28,25 +29,25 @@
         FROM_HERE,
         WTF::Bind(
             &PresentationAvailabilityCallbacks::RejectAvailabilityNotSupported,
-            std::move(callback)));
+            WrapPersistent(callback)));
     // Do not listen to urls if we reject the promise.
     return;
   }
 
   auto* listener = GetAvailabilityListener(urls);
   if (!listener) {
-    listener = new AvailabilityListener(urls);
+    listener = MakeGarbageCollected<AvailabilityListener>(urls);
     availability_listeners_.emplace_back(listener);
   }
 
   if (screen_availability != mojom::blink::ScreenAvailability::UNKNOWN) {
     Thread::Current()->GetTaskRunner()->PostTask(
         FROM_HERE, WTF::Bind(&PresentationAvailabilityCallbacks::Resolve,
-                             std::move(callback),
+                             WrapPersistent(callback),
                              screen_availability ==
                                  mojom::blink::ScreenAvailability::AVAILABLE));
   } else {
-    listener->availability_callbacks.push_back(std::move(callback));
+    listener->availability_callbacks.push_back(callback);
   }
 
   for (const auto& availability_url : urls)
@@ -58,11 +59,13 @@
   const auto& urls = observer->Urls();
   auto* listener = GetAvailabilityListener(urls);
   if (!listener) {
-    listener = new AvailabilityListener(urls);
+    listener = MakeGarbageCollected<AvailabilityListener>(urls);
     availability_listeners_.emplace_back(listener);
   }
 
-  listener->availability_observers.insert(observer);
+  if (listener->availability_observers.Contains(observer))
+    return;
+  listener->availability_observers.push_back(observer);
   for (const auto& availability_url : urls)
     StartListeningToURL(availability_url);
 }
@@ -76,7 +79,10 @@
     return;
   }
 
-  listener->availability_observers.erase(observer);
+  wtf_size_t slot = listener->availability_observers.Find(observer);
+  if (slot != kNotFound) {
+    listener->availability_observers.EraseAt(slot);
+  }
   for (const auto& availability_url : urls)
     MaybeStopListeningToURL(availability_url);
 
@@ -98,42 +104,40 @@
 
   listening_status->last_known_availability = availability;
 
-  std::vector<AvailabilityListener*> modified_listeners;
-  {
-    // Set |iterating_listeners_| so we know not to allow modifications
-    // to |availability_listeners_|.
-    base::AutoReset<bool> iterating(&iterating_listeners_, true);
-    for (auto& listener_ref : availability_listeners_) {
-      auto* listener = listener_ref.get();
-      if (!listener->urls.Contains<KURL>(url))
-        continue;
+  HeapVector<Member<AvailabilityListener>> listeners = availability_listeners_;
+  for (auto& listener : listeners) {
+    if (!listener->urls.Contains<KURL>(url))
+      continue;
 
-      auto screen_availability = GetScreenAvailability(listener->urls);
-      DCHECK(screen_availability != mojom::blink::ScreenAvailability::UNKNOWN);
-      for (auto* observer : listener->availability_observers)
-        observer->AvailabilityChanged(screen_availability);
-
-      if (screen_availability == mojom::blink::ScreenAvailability::DISABLED) {
-        for (auto& callback_ptr : listener->availability_callbacks) {
-          callback_ptr->RejectAvailabilityNotSupported();
-        }
-      } else {
-        for (auto& callback_ptr : listener->availability_callbacks) {
-          callback_ptr->Resolve(screen_availability ==
-                                mojom::blink::ScreenAvailability::AVAILABLE);
-        }
-      }
-      listener->availability_callbacks.clear();
-
-      for (const auto& availability_url : listener->urls)
-        MaybeStopListeningToURL(availability_url);
-
-      modified_listeners.push_back(listener);
+    auto screen_availability = GetScreenAvailability(listener->urls);
+    DCHECK(screen_availability != mojom::blink::ScreenAvailability::UNKNOWN);
+    HeapVector<Member<PresentationAvailabilityObserver>> observers =
+        listener->availability_observers;
+    for (auto& observer : observers) {
+      observer->AvailabilityChanged(screen_availability);
     }
-  }
 
-  for (auto* listener : modified_listeners)
+    if (screen_availability == mojom::blink::ScreenAvailability::DISABLED) {
+      for (auto& callback_ptr : listener->availability_callbacks) {
+        callback_ptr->RejectAvailabilityNotSupported();
+      }
+    } else {
+      for (auto& callback_ptr : listener->availability_callbacks) {
+        callback_ptr->Resolve(screen_availability ==
+                              mojom::blink::ScreenAvailability::AVAILABLE);
+      }
+    }
+    listener->availability_callbacks.clear();
+
+    for (const auto& availability_url : listener->urls)
+      MaybeStopListeningToURL(availability_url);
+
     TryRemoveAvailabilityListener(listener);
+  }
+}
+
+void PresentationAvailabilityState::Trace(blink::Visitor* visitor) {
+  visitor->Trace(availability_listeners_);
 }
 
 void PresentationAvailabilityState::StartListeningToURL(const KURL& url) {
@@ -157,8 +161,8 @@
       continue;
 
     // URL is still observed by some availability object.
-    if (!listener->availability_callbacks.empty() ||
-        !listener->availability_observers.empty()) {
+    if (!listener->availability_callbacks.IsEmpty() ||
+        !listener->availability_observers.IsEmpty()) {
       return;
     }
   }
@@ -216,34 +220,24 @@
 
 PresentationAvailabilityState::AvailabilityListener*
 PresentationAvailabilityState::GetAvailabilityListener(
-    const Vector<KURL>& urls) const {
-  auto listener_it = std::find_if(
+    const Vector<KURL>& urls) {
+  auto* listener_it = std::find_if(
       availability_listeners_.begin(), availability_listeners_.end(),
-      [&urls](const std::unique_ptr<AvailabilityListener>& x) {
-        return x->urls == urls;
-      });
-  return listener_it == availability_listeners_.end() ? nullptr
-                                                      : listener_it->get();
+      [&urls](const auto& listener) { return listener->urls == urls; });
+  return listener_it == availability_listeners_.end() ? nullptr : *listener_it;
 }
 
 void PresentationAvailabilityState::TryRemoveAvailabilityListener(
     AvailabilityListener* listener) {
-  if (iterating_listeners_)
-    return;
-
   // URL is still observed by some availability object.
-  if (!listener->availability_callbacks.empty() ||
-      !listener->availability_observers.empty()) {
+  if (!listener->availability_callbacks.IsEmpty() ||
+      !listener->availability_observers.IsEmpty()) {
     return;
   }
 
-  auto listener_it = availability_listeners_.begin();
-  while (listener_it != availability_listeners_.end()) {
-    if (listener_it->get() == listener) {
-      availability_listeners_.erase(listener_it);
-      return;
-    }
-    ++listener_it;
+  wtf_size_t slot = availability_listeners_.Find(listener);
+  if (slot != kNotFound) {
+    availability_listeners_.EraseAt(slot);
   }
 }
 
@@ -266,6 +260,12 @@
 PresentationAvailabilityState::AvailabilityListener::~AvailabilityListener() =
     default;
 
+void PresentationAvailabilityState::AvailabilityListener::Trace(
+    blink::Visitor* visitor) {
+  visitor->Trace(availability_callbacks);
+  visitor->Trace(availability_observers);
+}
+
 PresentationAvailabilityState::ListeningStatus::ListeningStatus(
     const KURL& availability_url)
     : url(availability_url),
diff --git a/third_party/blink/renderer/modules/presentation/presentation_availability_state.h b/third_party/blink/renderer/modules/presentation/presentation_availability_state.h
index cbc58ab5..b0b1aa9 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_availability_state.h
+++ b/third_party/blink/renderer/modules/presentation/presentation_availability_state.h
@@ -30,9 +30,8 @@
 // TODO(crbug.com/780109): Improve encapsulation of PresentationAvailability and
 // this class by moving the multiple URL tracking logic to the former, and
 // consolidating this class's APIs to take repeating callbacks.
-class MODULES_EXPORT PresentationAvailabilityState {
-  USING_FAST_MALLOC(PresentationAvailabilityState);
-
+class MODULES_EXPORT PresentationAvailabilityState
+    : public GarbageCollectedFinalized<PresentationAvailabilityState> {
  public:
   explicit PresentationAvailabilityState(mojom::blink::PresentationService*);
   ~PresentationAvailabilityState();
@@ -41,7 +40,7 @@
   // with the determined availability value. The callbacks will only be invoked
   // once and will be deleted afterwards.
   void RequestAvailability(const Vector<KURL>&,
-                           std::unique_ptr<PresentationAvailabilityCallbacks>);
+                           PresentationAvailabilityCallbacks* callbacks);
 
   // Starts/stops listening for availability with the given observer.
   void AddObserver(PresentationAvailabilityObserver*);
@@ -51,6 +50,8 @@
   // callbacks and observers.
   void UpdateAvailability(const KURL&, mojom::blink::ScreenAvailability);
 
+  void Trace(blink::Visitor*);
+
  private:
   enum class ListeningState {
     INACTIVE,
@@ -61,15 +62,20 @@
   // Tracks listeners of presentation displays availability for
   // |availability_urls|. Shared with PresentationRequest objects with the same
   // set of URLs.
-  struct AvailabilityListener {
+  class AvailabilityListener
+      : public GarbageCollectedFinalized<AvailabilityListener> {
+   public:
     explicit AvailabilityListener(const Vector<KURL>& availability_urls);
     ~AvailabilityListener();
 
     const Vector<KURL> urls;
-    std::vector<std::unique_ptr<PresentationAvailabilityCallbacks>>
+    HeapVector<Member<PresentationAvailabilityCallbacks>>
         availability_callbacks;
-    std::set<PresentationAvailabilityObserver*> availability_observers;
+    HeapVector<Member<PresentationAvailabilityObserver>> availability_observers;
 
+    void Trace(blink::Visitor*);
+
+   private:
     DISALLOW_COPY_AND_ASSIGN(AvailabilityListener);
   };
 
@@ -104,7 +110,7 @@
       const Vector<KURL>&) const;
 
   // Returns nullptr if there is no AvailabilityListener for the given URLs.
-  AvailabilityListener* GetAvailabilityListener(const Vector<KURL>&) const;
+  AvailabilityListener* GetAvailabilityListener(const Vector<KURL>&);
 
   // Removes the given listener from |availability_set_| if it has no callbacks
   // and no observers.
@@ -117,15 +123,11 @@
   std::vector<std::unique_ptr<ListeningStatus>> availability_listening_status_;
 
   // Set of AvailabilityListener for known PresentationRequests.
-  std::vector<std::unique_ptr<AvailabilityListener>> availability_listeners_;
+  HeapVector<Member<AvailabilityListener>> availability_listeners_;
 
   // A pointer to PresentationService owned by PresentationController.
   mojom::blink::PresentationService* const presentation_service_;
 
-  // Whether we are iterating listeners, if this state is set we avoid
-  // removing items from |availability_listeners_|.
-  bool iterating_listeners_ = false;
-
   DISALLOW_COPY_AND_ASSIGN(PresentationAvailabilityState);
 };
 
diff --git a/third_party/blink/renderer/modules/presentation/presentation_availability_state_test.cc b/third_party/blink/renderer/modules/presentation/presentation_availability_state_test.cc
index 0a94fbdd..74ac709 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_availability_state_test.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_availability_state_test.cc
@@ -20,7 +20,10 @@
 using mojom::blink::ScreenAvailability;
 
 class MockPresentationAvailabilityObserver
-    : public PresentationAvailabilityObserver {
+    : public GarbageCollectedFinalized<MockPresentationAvailabilityObserver>,
+      public PresentationAvailabilityObserver {
+  USING_GARBAGE_COLLECTED_MIXIN(MockPresentationAvailabilityObserver);
+
  public:
   explicit MockPresentationAvailabilityObserver(const Vector<KURL>& urls)
       : urls_(urls) {}
@@ -52,27 +55,35 @@
         url3_(KURL("https://www.example.com/3.html")),
         url4_(KURL("https://www.example.com/4.html")),
         urls_({url1_, url2_, url3_, url4_}),
-        mock_observer_all_urls_(urls_),
-        mock_observer1_({url1_, url2_, url3_}),
-        mock_observer2_({url2_, url3_, url4_}),
-        mock_observer3_({url2_, url3_}),
-        mock_observers_({&mock_observer1_, &mock_observer2_, &mock_observer3_}),
+        mock_observer_all_urls_(
+            MakeGarbageCollected<MockPresentationAvailabilityObserver>(urls_)),
+        mock_observer1_(
+            MakeGarbageCollected<MockPresentationAvailabilityObserver>(
+                Vector<KURL>({url1_, url2_, url3_}))),
+        mock_observer2_(
+            MakeGarbageCollected<MockPresentationAvailabilityObserver>(
+                Vector<KURL>({url2_, url3_, url4_}))),
+        mock_observer3_(
+            MakeGarbageCollected<MockPresentationAvailabilityObserver>(
+                Vector<KURL>({url2_, url3_}))),
+        mock_observers_({mock_observer1_, mock_observer2_, mock_observer3_}),
         mock_presentation_service_(),
-        state_(&mock_presentation_service_) {}
+        state_(MakeGarbageCollected<PresentationAvailabilityState>(
+            &mock_presentation_service_)) {}
 
   ~PresentationAvailabilityStateTest() override = default;
 
   void ChangeURLState(const KURL& url, ScreenAvailability state) {
     if (state != ScreenAvailability::UNKNOWN)
-      state_.UpdateAvailability(url, state);
+      state_->UpdateAvailability(url, state);
   }
 
   void RequestAvailabilityAndAddObservers() {
     for (auto* mock_observer : mock_observers_) {
-      state_.RequestAvailability(
+      state_->RequestAvailability(
           mock_observer->Urls(),
-          std::make_unique<MockPresentationAvailabilityCallbacks>());
-      state_.AddObserver(mock_observer);
+          MakeGarbageCollected<MockPresentationAvailabilityCallbacks>());
+      state_->AddObserver(mock_observer);
     }
   }
 
@@ -93,9 +104,7 @@
           .Times(1);
     }
 
-    state_.RequestAvailability(
-        urls,
-        std::unique_ptr<MockPresentationAvailabilityCallbacks>(mock_callback));
+    state_->RequestAvailability(urls, mock_callback);
     for (wtf_size_t i = 0; i < urls.size(); i++)
       ChangeURLState(urls[i], states[i]);
   }
@@ -106,14 +115,14 @@
   const KURL url3_;
   const KURL url4_;
   const Vector<KURL> urls_;
-  MockPresentationAvailabilityObserver mock_observer_all_urls_;
-  MockPresentationAvailabilityObserver mock_observer1_;
-  MockPresentationAvailabilityObserver mock_observer2_;
-  MockPresentationAvailabilityObserver mock_observer3_;
+  Persistent<MockPresentationAvailabilityObserver> mock_observer_all_urls_;
+  Persistent<MockPresentationAvailabilityObserver> mock_observer1_;
+  Persistent<MockPresentationAvailabilityObserver> mock_observer2_;
+  Persistent<MockPresentationAvailabilityObserver> mock_observer3_;
   std::vector<MockPresentationAvailabilityObserver*> mock_observers_;
 
   MockPresentationService mock_presentation_service_;
-  PresentationAvailabilityState state_;
+  Persistent<PresentationAvailabilityState> state_;
 };
 
 TEST_F(PresentationAvailabilityStateTest, RequestAvailability) {
@@ -123,33 +132,33 @@
                 StopListeningForScreenAvailability(url));
   }
 
-  state_.RequestAvailability(
-      urls_, std::make_unique<MockPresentationAvailabilityCallbacks>());
-  state_.UpdateAvailability(url1_, ScreenAvailability::AVAILABLE);
+  state_->RequestAvailability(
+      urls_, MakeGarbageCollected<MockPresentationAvailabilityCallbacks>());
+  state_->UpdateAvailability(url1_, ScreenAvailability::AVAILABLE);
 
   for (const auto& url : urls_)
     EXPECT_CALL(mock_presentation_service_, ListenForScreenAvailability(url));
 
-  state_.AddObserver(&mock_observer_all_urls_);
+  state_->AddObserver(mock_observer_all_urls_);
 
-  EXPECT_CALL(mock_observer_all_urls_,
+  EXPECT_CALL(*mock_observer_all_urls_,
               AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
-  state_.UpdateAvailability(url1_, ScreenAvailability::UNAVAILABLE);
-  EXPECT_CALL(mock_observer_all_urls_,
+  state_->UpdateAvailability(url1_, ScreenAvailability::UNAVAILABLE);
+  EXPECT_CALL(*mock_observer_all_urls_,
               AvailabilityChanged(ScreenAvailability::AVAILABLE));
-  state_.UpdateAvailability(url1_, ScreenAvailability::AVAILABLE);
+  state_->UpdateAvailability(url1_, ScreenAvailability::AVAILABLE);
   for (const auto& url : urls_) {
     EXPECT_CALL(mock_presentation_service_,
                 StopListeningForScreenAvailability(url));
   }
-  state_.RemoveObserver(&mock_observer_all_urls_);
+  state_->RemoveObserver(mock_observer_all_urls_);
 
   // After RemoveObserver(), |mock_observer_all_urls_| should no longer be
   // notified.
-  EXPECT_CALL(mock_observer_all_urls_,
+  EXPECT_CALL(*mock_observer_all_urls_,
               AvailabilityChanged(ScreenAvailability::UNAVAILABLE))
       .Times(0);
-  state_.UpdateAvailability(url1_, ScreenAvailability::UNAVAILABLE);
+  state_->UpdateAvailability(url1_, ScreenAvailability::UNAVAILABLE);
 }
 
 TEST_F(PresentationAvailabilityStateTest,
@@ -157,35 +166,34 @@
   for (const auto& url : urls_)
     EXPECT_CALL(mock_presentation_service_, ListenForScreenAvailability(url));
 
-  state_.AddObserver(&mock_observer_all_urls_);
+  state_->AddObserver(mock_observer_all_urls_);
 
-  EXPECT_CALL(mock_observer_all_urls_,
+  EXPECT_CALL(*mock_observer_all_urls_,
               AvailabilityChanged(ScreenAvailability::SOURCE_NOT_SUPPORTED));
-  state_.UpdateAvailability(url1_, ScreenAvailability::SOURCE_NOT_SUPPORTED);
+  state_->UpdateAvailability(url1_, ScreenAvailability::SOURCE_NOT_SUPPORTED);
 
   for (const auto& url : urls_) {
     EXPECT_CALL(mock_presentation_service_,
                 StopListeningForScreenAvailability(url));
   }
-  state_.RemoveObserver(&mock_observer_all_urls_);
+  state_->RemoveObserver(mock_observer_all_urls_);
 }
 
 TEST_F(PresentationAvailabilityStateTest,
        RequestAvailabilityOneUrlNoAvailabilityChange) {
-  auto* mock_callback =
-      new testing::StrictMock<MockPresentationAvailabilityCallbacks>();
+  auto* mock_callback = MakeGarbageCollected<
+      testing::StrictMock<MockPresentationAvailabilityCallbacks>>();
 
   EXPECT_CALL(mock_presentation_service_, ListenForScreenAvailability(url1_))
       .Times(1);
 
-  state_.RequestAvailability(
-      Vector<KURL>({url1_}),
-      std::unique_ptr<PresentationAvailabilityCallbacks>(mock_callback));
+  state_->RequestAvailability(Vector<KURL>({url1_}), mock_callback);
 }
 
 TEST_F(PresentationAvailabilityStateTest,
        RequestAvailabilityOneUrlBecomesAvailable) {
-  auto* mock_callback = new MockPresentationAvailabilityCallbacks();
+  auto* mock_callback =
+      MakeGarbageCollected<MockPresentationAvailabilityCallbacks>();
   EXPECT_CALL(*mock_callback, Resolve(true));
 
   TestRequestAvailability({url1_}, {ScreenAvailability::AVAILABLE},
@@ -194,7 +202,8 @@
 
 TEST_F(PresentationAvailabilityStateTest,
        RequestAvailabilityOneUrlBecomesNotCompatible) {
-  auto* mock_callback = new MockPresentationAvailabilityCallbacks();
+  auto* mock_callback =
+      MakeGarbageCollected<MockPresentationAvailabilityCallbacks>();
   EXPECT_CALL(*mock_callback, Resolve(false));
 
   TestRequestAvailability({url1_}, {ScreenAvailability::SOURCE_NOT_SUPPORTED},
@@ -203,7 +212,8 @@
 
 TEST_F(PresentationAvailabilityStateTest,
        RequestAvailabilityOneUrlBecomesUnavailable) {
-  auto* mock_callback = new MockPresentationAvailabilityCallbacks();
+  auto* mock_callback =
+      MakeGarbageCollected<MockPresentationAvailabilityCallbacks>();
   EXPECT_CALL(*mock_callback, Resolve(false));
 
   TestRequestAvailability({url1_}, {ScreenAvailability::UNAVAILABLE},
@@ -212,7 +222,8 @@
 
 TEST_F(PresentationAvailabilityStateTest,
        RequestAvailabilityOneUrlBecomesUnsupported) {
-  auto* mock_callback = new MockPresentationAvailabilityCallbacks();
+  auto* mock_callback =
+      MakeGarbageCollected<MockPresentationAvailabilityCallbacks>();
   EXPECT_CALL(*mock_callback, RejectAvailabilityNotSupported());
 
   TestRequestAvailability({url1_}, {ScreenAvailability::DISABLED},
@@ -221,7 +232,8 @@
 
 TEST_F(PresentationAvailabilityStateTest,
        RequestAvailabilityMultipleUrlsAllBecomesAvailable) {
-  auto* mock_callback = new MockPresentationAvailabilityCallbacks();
+  auto* mock_callback =
+      MakeGarbageCollected<MockPresentationAvailabilityCallbacks>();
   EXPECT_CALL(*mock_callback, Resolve(true)).Times(1);
 
   TestRequestAvailability(
@@ -232,7 +244,8 @@
 
 TEST_F(PresentationAvailabilityStateTest,
        RequestAvailabilityMultipleUrlsAllBecomesUnavailable) {
-  auto* mock_callback = new MockPresentationAvailabilityCallbacks();
+  auto* mock_callback =
+      MakeGarbageCollected<MockPresentationAvailabilityCallbacks>();
   EXPECT_CALL(*mock_callback, Resolve(false)).Times(1);
 
   TestRequestAvailability(
@@ -243,7 +256,8 @@
 
 TEST_F(PresentationAvailabilityStateTest,
        RequestAvailabilityMultipleUrlsAllBecomesNotCompatible) {
-  auto* mock_callback = new MockPresentationAvailabilityCallbacks();
+  auto* mock_callback =
+      MakeGarbageCollected<MockPresentationAvailabilityCallbacks>();
   EXPECT_CALL(*mock_callback, Resolve(false)).Times(1);
 
   TestRequestAvailability({url1_, url2_},
@@ -254,7 +268,8 @@
 
 TEST_F(PresentationAvailabilityStateTest,
        RequestAvailabilityMultipleUrlsAllBecomesUnsupported) {
-  auto* mock_callback = new MockPresentationAvailabilityCallbacks();
+  auto* mock_callback =
+      MakeGarbageCollected<MockPresentationAvailabilityCallbacks>();
   EXPECT_CALL(*mock_callback, RejectAvailabilityNotSupported()).Times(1);
 
   TestRequestAvailability(
@@ -266,7 +281,8 @@
 TEST_F(PresentationAvailabilityStateTest,
        RequestAvailabilityReturnsDirectlyForAlreadyListeningUrls) {
   // First getAvailability() call.
-  auto* mock_callback_1 = new MockPresentationAvailabilityCallbacks();
+  auto* mock_callback_1 =
+      MakeGarbageCollected<MockPresentationAvailabilityCallbacks>();
   EXPECT_CALL(*mock_callback_1, Resolve(false)).Times(1);
 
   std::vector<ScreenAvailability> state_seq = {ScreenAvailability::UNAVAILABLE,
@@ -275,16 +291,15 @@
   TestRequestAvailability({url1_, url2_, url3_}, state_seq, mock_callback_1);
 
   // Second getAvailability() call.
-  for (const auto& url : mock_observer3_.Urls()) {
+  for (const auto& url : mock_observer3_->Urls()) {
     EXPECT_CALL(mock_presentation_service_, ListenForScreenAvailability(url))
         .Times(1);
   }
-  auto* mock_callback_2 = new MockPresentationAvailabilityCallbacks();
+  auto* mock_callback_2 =
+      MakeGarbageCollected<MockPresentationAvailabilityCallbacks>();
   EXPECT_CALL(*mock_callback_2, Resolve(true)).Times(1);
 
-  state_.RequestAvailability(
-      mock_observer3_.Urls(),
-      std::unique_ptr<MockPresentationAvailabilityCallbacks>(mock_callback_2));
+  state_->RequestAvailability(mock_observer3_->Urls(), mock_callback_2);
 }
 
 TEST_F(PresentationAvailabilityStateTest, StartListeningListenToEachURLOnce) {
@@ -305,11 +320,11 @@
         .Times(1);
   }
 
-  EXPECT_CALL(mock_observer1_,
+  EXPECT_CALL(*mock_observer1_,
               AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
-  EXPECT_CALL(mock_observer2_,
+  EXPECT_CALL(*mock_observer2_,
               AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
-  EXPECT_CALL(mock_observer3_,
+  EXPECT_CALL(*mock_observer3_,
               AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
 
   RequestAvailabilityAndAddObservers();
@@ -318,7 +333,7 @@
   ChangeURLState(url2_, ScreenAvailability::UNAVAILABLE);
 
   for (auto* mock_observer : mock_observers_)
-    state_.RemoveObserver(mock_observer);
+    state_->RemoveObserver(mock_observer);
 }
 
 TEST_F(PresentationAvailabilityStateTest,
@@ -342,18 +357,18 @@
   RequestAvailabilityAndAddObservers();
 
   for (auto* mock_observer : mock_observers_)
-    state_.AddObserver(mock_observer);
+    state_->AddObserver(mock_observer);
 
-  EXPECT_CALL(mock_observer1_,
+  EXPECT_CALL(*mock_observer1_,
               AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
-  EXPECT_CALL(mock_observer2_,
+  EXPECT_CALL(*mock_observer2_,
               AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
-  EXPECT_CALL(mock_observer3_,
+  EXPECT_CALL(*mock_observer3_,
               AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
 
   // Clean up callbacks.
   ChangeURLState(url2_, ScreenAvailability::UNAVAILABLE);
-  state_.RemoveObserver(&mock_observer1_);
+  state_->RemoveObserver(mock_observer1_);
 }
 
 TEST_F(PresentationAvailabilityStateTest,
@@ -363,18 +378,18 @@
         .Times(1);
   }
 
-  EXPECT_CALL(mock_observer1_,
+  EXPECT_CALL(*mock_observer1_,
               AvailabilityChanged(ScreenAvailability::AVAILABLE));
 
   RequestAvailabilityAndAddObservers();
 
   ChangeURLState(url1_, ScreenAvailability::AVAILABLE);
 
-  EXPECT_CALL(mock_observer1_,
+  EXPECT_CALL(*mock_observer1_,
               AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
   ChangeURLState(url1_, ScreenAvailability::UNAVAILABLE);
 
-  EXPECT_CALL(mock_observer1_,
+  EXPECT_CALL(*mock_observer1_,
               AvailabilityChanged(ScreenAvailability::SOURCE_NOT_SUPPORTED));
   ChangeURLState(url1_, ScreenAvailability::SOURCE_NOT_SUPPORTED);
 }
diff --git a/third_party/blink/renderer/modules/presentation/presentation_controller.cc b/third_party/blink/renderer/modules/presentation/presentation_controller.cc
index ba568aa0..e281522 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_controller.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_controller.cc
@@ -55,6 +55,7 @@
 void PresentationController::Trace(blink::Visitor* visitor) {
   visitor->Trace(presentation_);
   visitor->Trace(connections_);
+  visitor->Trace(availability_state_);
   Supplement<LocalFrame>::Trace(visitor);
   ContextLifecycleObserver::Trace(visitor);
 }
@@ -70,11 +71,11 @@
 
 PresentationAvailabilityState* PresentationController::GetAvailabilityState() {
   if (!availability_state_) {
-    availability_state_.reset(
-        new PresentationAvailabilityState(GetPresentationService().get()));
+    availability_state_ = MakeGarbageCollected<PresentationAvailabilityState>(
+        GetPresentationService().get());
   }
 
-  return availability_state_.get();
+  return availability_state_;
 }
 
 void PresentationController::AddAvailabilityObserver(
diff --git a/third_party/blink/renderer/modules/presentation/presentation_controller.h b/third_party/blink/renderer/modules/presentation/presentation_controller.h
index 98aac1d6..98b4847 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_controller.h
+++ b/third_party/blink/renderer/modules/presentation/presentation_controller.h
@@ -100,7 +100,7 @@
       const mojom::blink::PresentationInfo&) const;
 
   // Lazily-instantiated when the page queries for availability.
-  std::unique_ptr<PresentationAvailabilityState> availability_state_;
+  Member<PresentationAvailabilityState> availability_state_;
 
   // The Presentation instance associated with that frame.
   WeakMember<Presentation> presentation_;
diff --git a/third_party/blink/renderer/modules/presentation/presentation_request.cc b/third_party/blink/renderer/modules/presentation/presentation_request.cc
index efed5c9..29df8f2c 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_request.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_request.cc
@@ -217,7 +217,7 @@
             PresentationAvailabilityProperty::kReady);
 
     controller->GetAvailabilityState()->RequestAvailability(
-        urls_, std::make_unique<PresentationAvailabilityCallbacks>(
+        urls_, MakeGarbageCollected<PresentationAvailabilityCallbacks>(
                    availability_property_, urls_));
   }
   return availability_property_->Promise(script_state->World());
diff --git a/third_party/blink/renderer/modules/sensor/sensor_proxy.h b/third_party/blink/renderer/modules/sensor/sensor_proxy.h
index 71c95ea..7357953 100644
--- a/third_party/blink/renderer/modules/sensor/sensor_proxy.h
+++ b/third_party/blink/renderer/modules/sensor/sensor_proxy.h
@@ -94,8 +94,6 @@
   device::SensorReading reading_;
   mutable device::SensorReading remapped_reading_;
 
-  using ReadingBuffer = device::SensorReadingSharedBuffer;
-
  private:
   // PageVisibilityObserver overrides.
   void PageVisibilityChanged() override;
@@ -110,7 +108,7 @@
   bool detached_ = false;
 
   static_assert(
-      sizeof(ReadingBuffer) ==
+      sizeof(device::SensorReadingSharedBuffer) ==
           device::mojom::blink::SensorInitParams::kReadBufferSizeForTests,
       "Check reading buffer size for tests");
 
diff --git a/third_party/blink/renderer/modules/sensor/sensor_proxy_impl.cc b/third_party/blink/renderer/modules/sensor/sensor_proxy_impl.cc
index 9aee25cc..9485289 100644
--- a/third_party/blink/renderer/modules/sensor/sensor_proxy_impl.cc
+++ b/third_party/blink/renderer/modules/sensor/sensor_proxy_impl.cc
@@ -95,7 +95,7 @@
 
 void SensorProxyImpl::UpdateSensorReading() {
   DCHECK(ShouldProcessReadings());
-  DCHECK(shared_buffer_handle_->is_valid());
+  DCHECK(shared_buffer_reader_);
 
   // Try to read the latest value from shared memory. Failure should not be
   // fatal because we only retry a finite number of times.
@@ -137,8 +137,7 @@
   // The m_sensor.reset() will release all callbacks and its bound parameters,
   // therefore, handleSensorError accepts messages by value.
   sensor_.reset();
-  shared_buffer_.reset();
-  shared_buffer_handle_.reset();
+  shared_buffer_reader_.reset();
   default_frequency_ = 0.0;
   frequency_limits_ = {0.0, 0.0};
   client_binding_.Close();
@@ -165,9 +164,6 @@
   }
 
   DCHECK_EQ(SensorCreationResult::SUCCESS, result);
-  const size_t kReadBufferSize = sizeof(ReadingBuffer);
-
-  DCHECK_EQ(0u, params->buffer_offset % kReadBufferSize);
 
   mode_ = params->mode;
   if (!params->default_configuration) {
@@ -181,20 +177,13 @@
   sensor_.Bind(std::move(params->sensor));
   client_binding_.Bind(std::move(params->client_request));
 
-  shared_buffer_handle_ = std::move(params->memory);
-  DCHECK(!shared_buffer_);
-  shared_buffer_ = shared_buffer_handle_->MapAtOffset(kReadBufferSize,
-                                                      params->buffer_offset);
-
-  if (!shared_buffer_) {
+  shared_buffer_reader_ = device::SensorReadingSharedBufferReader::Create(
+      std::move(params->memory), params->buffer_offset);
+  if (!shared_buffer_reader_) {
     HandleSensorError();
     return;
   }
 
-  const auto* buffer = static_cast<const device::SensorReadingSharedBuffer*>(
-      shared_buffer_.get());
-  shared_buffer_reader_ =
-      std::make_unique<device::SensorReadingSharedBufferReader>(buffer);
   shared_buffer_reader_->GetReading(&reading_);
 
   frequency_limits_.first = params->minimum_frequency;
diff --git a/third_party/blink/renderer/modules/sensor/sensor_proxy_impl.h b/third_party/blink/renderer/modules/sensor/sensor_proxy_impl.h
index 95ca6a0..3fd5519 100644
--- a/third_party/blink/renderer/modules/sensor/sensor_proxy_impl.h
+++ b/third_party/blink/renderer/modules/sensor/sensor_proxy_impl.h
@@ -80,8 +80,6 @@
   device::mojom::blink::SensorPtr sensor_;
   mojo::Binding<device::mojom::blink::SensorClient> client_binding_;
 
-  mojo::ScopedSharedBufferHandle shared_buffer_handle_;
-  mojo::ScopedSharedBufferMapping shared_buffer_;
   std::unique_ptr<device::SensorReadingSharedBufferReader>
       shared_buffer_reader_;
   double default_frequency_ = 0.0;
diff --git a/third_party/blink/renderer/modules/service_worker/BUILD.gn b/third_party/blink/renderer/modules/service_worker/BUILD.gn
index 39e8f170..37cf4759 100644
--- a/third_party/blink/renderer/modules/service_worker/BUILD.gn
+++ b/third_party/blink/renderer/modules/service_worker/BUILD.gn
@@ -38,8 +38,6 @@
     "service_worker_error.h",
     "service_worker_global_scope.cc",
     "service_worker_global_scope.h",
-    "service_worker_global_scope_client.cc",
-    "service_worker_global_scope_client.h",
     "service_worker_global_scope_proxy.cc",
     "service_worker_global_scope_proxy.h",
     "service_worker_installed_scripts_manager.cc",
diff --git a/third_party/blink/renderer/modules/service_worker/install_event.cc b/third_party/blink/renderer/modules/service_worker/install_event.cc
index 35e3682c..20e7c93e 100644
--- a/third_party/blink/renderer/modules/service_worker/install_event.cc
+++ b/third_party/blink/renderer/modules/service_worker/install_event.cc
@@ -6,7 +6,6 @@
 
 #include "third_party/blink/public/platform/web_security_origin.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
index e67b2695..a0c63be2 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -94,7 +94,7 @@
 #include "third_party/blink/renderer/modules/service_worker/service_worker.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_client.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_clients.h"
-#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h"
+#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_module_tree_client.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_registration.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_script_cached_metadata_handler.h"
@@ -1256,10 +1256,10 @@
   // RequestedTermination() returns true if ServiceWorkerTimeoutTimer agrees
   // we should request the host to terminate this worker now.
   DCHECK(RequestedTermination());
-  // We use CrossThreadBindOnce() here because the callback may be
-  // destroyed on the main thread if the worker thread has already terminated.
-  ServiceWorkerGlobalScopeClient::From(GetExecutionContext())
-      ->RequestTermination(ConvertToBaseOnceCallback(
+  // We use CrossThreadBindOnce() here because the callback may be destroyed on
+  // the main thread if the worker thread has already terminated.
+  To<ServiceWorkerGlobalScopeProxy>(ReportingProxy())
+      .RequestTermination(ConvertToBaseOnceCallback(
           CrossThreadBindOnce(&ServiceWorkerGlobalScope::OnRequestedTermination,
                               WrapCrossThreadWeakPersistent(this))));
 }
@@ -1339,9 +1339,9 @@
   // Set up for navigation preload (FetchEvent#preloadResponse) if needed.
   const bool navigation_preload_sent = !!params->preload_handle;
   if (navigation_preload_sent) {
-    ServiceWorkerGlobalScopeClient::From(GetExecutionContext())
-        ->SetupNavigationPreload(event_id, params->request->url,
-                                 std::move(params->preload_handle));
+    To<ServiceWorkerGlobalScopeProxy>(ReportingProxy())
+        .SetupNavigationPreload(event_id, params->request->url,
+                                std::move(params->preload_handle));
   }
 
   mojom::blink::FetchAPIRequest& fetch_request = *params->request;
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.cc
deleted file mode 100644
index 8f7dffd..0000000
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2014 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h"
-
-#include <memory>
-#include <utility>
-#include "third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h"
-#include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/core/workers/worker_global_scope.h"
-#include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
-#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
-
-namespace blink {
-
-namespace {
-
-}  // namespace
-
-ServiceWorkerGlobalScopeClient::ServiceWorkerGlobalScopeClient(
-    WebServiceWorkerContextClient& client)
-    : client_(client) {}
-
-void ServiceWorkerGlobalScopeClient::SetupNavigationPreload(
-    int fetch_event_id,
-    const KURL& url,
-    mojom::blink::FetchEventPreloadHandlePtr preload_handle) {
-  auto web_preload_handle = std::make_unique<WebFetchEventPreloadHandle>();
-  web_preload_handle->url_loader = preload_handle->url_loader.PassHandle();
-  web_preload_handle->url_loader_client_request =
-      preload_handle->url_loader_client_request.PassMessagePipe();
-  client_.SetupNavigationPreload(fetch_event_id, url,
-                                 std::move(web_preload_handle));
-}
-
-void ServiceWorkerGlobalScopeClient::RequestTermination(
-    base::OnceCallback<void(bool)> callback) {
-  client_.RequestTermination(std::move(callback));
-}
-
-const char ServiceWorkerGlobalScopeClient::kSupplementName[] =
-    "ServiceWorkerGlobalScopeClient";
-
-ServiceWorkerGlobalScopeClient* ServiceWorkerGlobalScopeClient::From(
-    ExecutionContext* context) {
-  // TODO(crbug.com/920854): Replace CHECK() with DCHECK() after crashes are
-  // gone.
-  CHECK(context);
-  WorkerClients* worker_clients = To<WorkerGlobalScope>(context)->Clients();
-  CHECK(worker_clients);
-  ServiceWorkerGlobalScopeClient* client =
-      Supplement<WorkerClients>::From<ServiceWorkerGlobalScopeClient>(
-          worker_clients);
-  CHECK(client);
-  return client;
-}
-
-void ServiceWorkerGlobalScopeClient::Trace(blink::Visitor* visitor) {
-  Supplement<WorkerClients>::Trace(visitor);
-}
-
-void ProvideServiceWorkerGlobalScopeClientToWorker(
-    WorkerClients* clients,
-    ServiceWorkerGlobalScopeClient* client) {
-  clients->ProvideSupplement(client);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h
deleted file mode 100644
index fe02258e..0000000
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2014 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_SERVICE_WORKER_GLOBAL_SCOPE_CLIENT_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_SERVICE_WORKER_GLOBAL_SCOPE_CLIENT_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "third_party/blink/public/mojom/service_worker/dispatch_fetch_event_params.mojom-blink.h"
-#include "third_party/blink/renderer/core/workers/worker_clients.h"
-#include "third_party/blink/renderer/modules/modules_export.h"
-
-namespace blink {
-
-class ExecutionContext;
-class KURL;
-class WebServiceWorkerContextClient;
-
-class MODULES_EXPORT ServiceWorkerGlobalScopeClient final
-    : public GarbageCollectedFinalized<ServiceWorkerGlobalScopeClient>,
-      public Supplement<WorkerClients> {
-  USING_GARBAGE_COLLECTED_MIXIN(ServiceWorkerGlobalScopeClient);
-
- public:
-  static const char kSupplementName[];
-
-  explicit ServiceWorkerGlobalScopeClient(WebServiceWorkerContextClient&);
-
-  // Called from ServiceWorkerGlobalScope.
-  void SetupNavigationPreload(
-      int fetch_event_id,
-      const KURL& url,
-      mojom::blink::FetchEventPreloadHandlePtr preload_handle);
-  void RequestTermination(base::OnceCallback<void(bool)> callback);
-
-  static ServiceWorkerGlobalScopeClient* From(ExecutionContext*);
-
-  void Trace(blink::Visitor*) override;
-
- private:
-  WebServiceWorkerContextClient& client_;
-
-  DISALLOW_COPY_AND_ASSIGN(ServiceWorkerGlobalScopeClient);
-};
-
-MODULES_EXPORT void ProvideServiceWorkerGlobalScopeClientToWorker(
-    WorkerClients*,
-    ServiceWorkerGlobalScopeClient*);
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_SERVICE_WORKER_GLOBAL_SCOPE_CLIENT_H_
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
index a236ef96..952cc8f 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
@@ -288,6 +288,29 @@
   Client().WorkerContextDestroyed();
 }
 
+bool ServiceWorkerGlobalScopeProxy::IsServiceWorkerGlobalScopeProxy() const {
+  return true;
+}
+
+void ServiceWorkerGlobalScopeProxy::SetupNavigationPreload(
+    int fetch_event_id,
+    const KURL& url,
+    mojom::blink::FetchEventPreloadHandlePtr preload_handle) {
+  DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
+  auto web_preload_handle = std::make_unique<WebFetchEventPreloadHandle>();
+  web_preload_handle->url_loader = preload_handle->url_loader.PassHandle();
+  web_preload_handle->url_loader_client_request =
+      preload_handle->url_loader_client_request.PassMessagePipe();
+  Client().SetupNavigationPreload(fetch_event_id, url,
+                                  std::move(web_preload_handle));
+}
+
+void ServiceWorkerGlobalScopeProxy::RequestTermination(
+    base::OnceCallback<void(bool)> callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
+  Client().RequestTermination(std::move(callback));
+}
+
 ServiceWorkerGlobalScopeProxy::ServiceWorkerGlobalScopeProxy(
     WebEmbeddedWorkerImpl& embedded_worker,
     WebServiceWorkerContextClient& client)
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h
index a7b30cc..9d28da86 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h
@@ -35,12 +35,14 @@
 
 #include "base/macros.h"
 #include "base/threading/thread_checker.h"
+#include "third_party/blink/public/mojom/service_worker/dispatch_fetch_event_params.mojom-blink.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/web/modules/service_worker/web_service_worker_context_proxy.h"
 #include "third_party/blink/renderer/core/workers/worker_reporting_proxy.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
 
@@ -120,6 +122,14 @@
   void DidCloseWorkerGlobalScope() override;
   void WillDestroyWorkerGlobalScope() override;
   void DidTerminateWorkerThread() override;
+  bool IsServiceWorkerGlobalScopeProxy() const override;
+
+  // Called from ServiceWorkerGlobalScope.
+  void SetupNavigationPreload(
+      int fetch_event_id,
+      const KURL& url,
+      mojom::blink::FetchEventPreloadHandlePtr preload_handle);
+  void RequestTermination(base::OnceCallback<void(bool)> callback);
 
   void Trace(blink::Visitor*);
 
@@ -152,6 +162,16 @@
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerGlobalScopeProxy);
 };
 
+// TODO(leonhsl): This is only used by ServiceWorkerGlobalScope for calling
+// WebServiceWorkerContextClient::{SetupNavigationPreload,RequestTermination}(),
+// which will be Onion Soupped eventually, at that time we'd remove this.
+template <>
+struct DowncastTraits<ServiceWorkerGlobalScopeProxy> {
+  static bool AllowFrom(const WorkerReportingProxy& proxy) {
+    return proxy.IsServiceWorkerGlobalScopeProxy();
+  }
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_SERVICE_WORKER_GLOBAL_SCOPE_PROXY_H_
diff --git a/third_party/blink/renderer/modules/service_worker/wait_until_observer.h b/third_party/blink/renderer/modules/service_worker/wait_until_observer.h
index 8769b1e..6a71973a 100644
--- a/third_party/blink/renderer/modules/service_worker/wait_until_observer.h
+++ b/third_party/blink/renderer/modules/service_worker/wait_until_observer.h
@@ -8,7 +8,6 @@
 #include "base/callback.h"
 #include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
-#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h"
 #include "third_party/blink/renderer/platform/timer.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 
@@ -57,11 +56,11 @@
   void WillDispatchEvent();
   // Must be called after dispatching the event. If |event_dispatch_failed| is
   // true, then DidDispatchEvent() immediately reports to
-  // ServiceWorkerGlobalScopeClient that the event finished, without waiting for
+  // ServiceWorkerGlobalScope that the event finished, without waiting for
   // all waitUntil promises to settle.
   void DidDispatchEvent(bool event_dispatch_failed);
 
-  // Observes the promise and delays reporting to ServiceWorkerGlobalScopeClient
+  // Observes the promise and delays reporting to ServiceWorkerGlobalScope
   // that the event completed until the promise is resolved or rejected.
   //
   // WaitUntil may be called multiple times. The event is extended until all
@@ -103,7 +102,7 @@
     // Event dispatch has started but not yet finished.
     kDispatching,
     // Event dispatch completed. There may still be outstanding waitUntil
-    // promises that must settle before notifying ServiceWorkerGlobalScopeClient
+    // promises that must settle before notifying ServiceWorkerGlobalScope
     // that the event finished.
     kDispatched,
     // Event dispatch failed. Any outstanding waitUntil promises are ignored.
diff --git a/third_party/blink/renderer/modules/speech/speech_synthesis_event.idl b/third_party/blink/renderer/modules/speech/speech_synthesis_event.idl
index 493fc0a..bfacf03a6 100644
--- a/third_party/blink/renderer/modules/speech/speech_synthesis_event.idl
+++ b/third_party/blink/renderer/modules/speech/speech_synthesis_event.idl
@@ -30,7 +30,7 @@
 ] interface SpeechSynthesisEvent : Event {
     readonly attribute SpeechSynthesisUtterance utterance;
     readonly attribute unsigned long charIndex;
-    [RuntimeEnabled=SpeechSynthesisEventCharLength] readonly attribute unsigned long charLength;
+    readonly attribute unsigned long charLength;
     readonly attribute float elapsedTime;
     readonly attribute DOMString name;
 };
diff --git a/third_party/blink/renderer/modules/speech/speech_synthesis_event_init.idl b/third_party/blink/renderer/modules/speech/speech_synthesis_event_init.idl
index 2066fc7..5cc40f6 100644
--- a/third_party/blink/renderer/modules/speech/speech_synthesis_event_init.idl
+++ b/third_party/blink/renderer/modules/speech/speech_synthesis_event_init.idl
@@ -6,7 +6,7 @@
 dictionary SpeechSynthesisEventInit : EventInit {
     required SpeechSynthesisUtterance utterance;
     unsigned long charIndex = 0;
-    [RuntimeEnabled=SpeechSynthesisEventCharLength] unsigned long charLength = 0;
+    unsigned long charLength = 0;
     float elapsedTime = 0;
     DOMString name = "";
 };
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.cc b/third_party/blink/renderer/modules/webaudio/audio_context.cc
index 9ee11ac8..0a64973 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_context.cc
@@ -674,10 +674,13 @@
   audio_context_manager_ = nullptr;
 }
 
-double AudioContext::RenderCapacity() {
-  DCHECK(IsMainThread());
+AudioCallbackMetric AudioContext::GetCallbackMetric() const {
+  // Return a copy under the graph lock because returning a reference would
+  // allow seeing the audio thread changing the struct values. This method
+  // gets called once per second and the size of the struct is small, so
+  // creating a copy is acceptable here.
   GraphAutoLocker locker(this);
-  return callback_metric_.render_capacity;
+  return callback_metric_;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.h b/third_party/blink/renderer/modules/webaudio/audio_context.h
index 1402683..b018b688 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_context.h
@@ -80,7 +80,7 @@
 
   void HandleAudibility(AudioBus* destination_bus);
 
-  double RenderCapacity() final;
+  AudioCallbackMetric GetCallbackMetric() const;
 
  protected:
   void Uninitialize() final;
diff --git a/third_party/blink/renderer/modules/webaudio/base_audio_context.h b/third_party/blink/renderer/modules/webaudio/base_audio_context.h
index d3ff40e..2b0ae32 100644
--- a/third_party/blink/renderer/modules/webaudio/base_audio_context.h
+++ b/third_party/blink/renderer/modules/webaudio/base_audio_context.h
@@ -329,10 +329,6 @@
   // occurs preventing us from determining the count.
   int32_t CallbackBufferSize();
 
-  // Returns the render capacity, which is the time spend on render divided by
-  // the hardware callback interval. Glitches happen when it goes beyond 1.0.
-  virtual double RenderCapacity() = 0;
-
  protected:
   enum ContextType { kRealtimeContext, kOfflineContext };
 
diff --git a/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.cc b/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.cc
index c21b006..2bc56e5f 100644
--- a/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.cc
+++ b/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
+#include "third_party/blink/renderer/modules/webaudio/audio_context.h"
 
 namespace blink {
 
@@ -80,12 +81,21 @@
   BaseAudioContext* context = tracker->GetContextById(contextId);
   if (!context)
     return Response::Error("Cannot find BaseAudioContext with such id.");
-  *out_data = context
-      ? ContextRealtimeData::create()
+
+  if (!context->HasRealtimeConstraint()) {
+    return Response::Error(
+        "ContextRealtimeData is only avaliable for an AudioContext.");
+  }
+
+  // The realtime metric collection is only for AudioContext.
+  AudioCallbackMetric metric =
+      static_cast<AudioContext*>(context)->GetCallbackMetric();
+  *out_data = ContextRealtimeData::create()
           .setCurrentTime(context->currentTime())
-          .setRenderCapacity(context->RenderCapacity())
-          .build()
-      : ContextRealtimeData::create().build();
+          .setRenderCapacity(metric.render_capacity)
+          .setCallbackIntervalMean(metric.mean_callback_interval)
+          .setCallbackIntervalVariance(metric.variance_callback_interval)
+          .build();
   return Response::OK();
 }
 
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_context.cc b/third_party/blink/renderer/modules/webaudio/offline_audio_context.cc
index 70eda602..672d1b4 100644
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_context.cc
@@ -487,11 +487,4 @@
   return is_rendering_started_;
 }
 
-double OfflineAudioContext::RenderCapacity() {
-  DCHECK(IsMainThread());
-
-  // Offline contexts don't have a meaningful render capacity. So return 0.
-  return 0.0;
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_context.h b/third_party/blink/renderer/modules/webaudio/offline_audio_context.h
index 4c1cbd8..a0ffabb 100644
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_context.h
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_context.h
@@ -99,8 +99,6 @@
   // Document notification
   bool HasPendingActivity() const final;
 
-  double RenderCapacity() final;
-
  private:
   // Fetch directly the destination handler.
   OfflineAudioDestinationHandler& DestinationHandler();
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context.cc b/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context.cc
index 4035087..fc2ae86 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context.cc
@@ -55,7 +55,7 @@
   if (extensions_util->SupportsExtension("GL_EXT_debug_marker")) {
     String context_label(
         String::Format("WebGL2ComputeRenderingContext-%p", context_provider));
-    gl->PushGroupMarkerEXT(0, context_label.Ascii().data());
+    gl->PushGroupMarkerEXT(0, context_label.Ascii().c_str());
   }
   return true;
 }
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context.cc b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context.cc
index 4fd359a..82dcc844 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context.cc
@@ -58,7 +58,7 @@
   if (extensions_util->SupportsExtension("GL_EXT_debug_marker")) {
     String context_label(
         String::Format("WebGL2RenderingContext-%p", context_provider));
-    gl->PushGroupMarkerEXT(0, context_label.Ascii().data());
+    gl->PushGroupMarkerEXT(0, context_label.Ascii().c_str());
   }
   return true;
 }
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
index 3f94f1c..f053bcf 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
@@ -4444,12 +4444,12 @@
       return;
   }
 
-  Vector<CString> keep_alive;  // Must keep these instances alive while looking
-                               // at their data
+  Vector<std::string> keep_alive;  // Must keep these instances alive while
+                                   // looking at their data
   Vector<const char*> varying_strings;
   for (const String& varying : varyings) {
     keep_alive.push_back(varying.Ascii());
-    varying_strings.push_back(keep_alive.back().data());
+    varying_strings.push_back(keep_alive.back().c_str());
   }
 
   program->SetRequiredTransformFeedbackBufferCount(
@@ -4671,12 +4671,14 @@
   if (!ValidateWebGLProgramOrShader("getUniformIndices", program))
     return result;
 
-  Vector<CString> keep_alive;  // Must keep these instances alive while looking
-                               // at their data
+  Vector<std::string> keep_alive;  // Must keep these instances alive while
+                                   // looking at their data
+  keep_alive.ReserveInitialCapacity(uniform_names.size());
   Vector<const char*> uniform_strings;
+  uniform_strings.ReserveInitialCapacity(uniform_names.size());
   for (const String& uniform_name : uniform_names) {
     keep_alive.push_back(uniform_name.Ascii());
-    uniform_strings.push_back(keep_alive.back().data());
+    uniform_strings.push_back(keep_alive.back().c_str());
   }
 
   result.resize(uniform_names.size());
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context.cc
index 45a03fa..4784eb3 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context.cc
@@ -87,7 +87,7 @@
   if (extensions_util->SupportsExtension("GL_EXT_debug_marker")) {
     String context_label(
         String::Format("WebGLRenderingContext-%p", context_provider));
-    gl->PushGroupMarkerEXT(0, context_label.Ascii().data());
+    gl->PushGroupMarkerEXT(0, context_label.Ascii().c_str());
   }
   return true;
 }
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index 22a73d9..4629ea6 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -4829,13 +4829,13 @@
   if (value < 0) {
     String error_msg = String(param_name) + " < 0";
     SynthesizeGLError(GL_INVALID_VALUE, function_name,
-                      error_msg.Ascii().data());
+                      error_msg.Ascii().c_str());
     return false;
   }
   if (value > static_cast<int64_t>(std::numeric_limits<int>::max())) {
     String error_msg = String(param_name) + " more than 32-bit";
     SynthesizeGLError(GL_INVALID_OPERATION, function_name,
-                      error_msg.Ascii().data());
+                      error_msg.Ascii().c_str());
     return false;
   }
   return true;
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc b/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc
index 5941232..bc03d99 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc
+++ b/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc
@@ -581,8 +581,7 @@
     const GPUPipelineStageDescriptor* webgpu_stage) {
   DCHECK(webgpu_stage);
 
-  CString entry_point_string = webgpu_stage->entryPoint().Ascii();
-
+  CString entry_point_string(webgpu_stage->entryPoint().Ascii().c_str());
   DawnPipelineStageDescriptor dawn_stage;
   dawn_stage.module = webgpu_stage->module()->GetHandle();
   dawn_stage.entryPoint = entry_point_string.data();
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc
index b174b785..492980a 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc
+++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc
@@ -719,9 +719,9 @@
   HandleDidClose(was_clean, code, reason);
 }
 
-void WebSocketChannelImpl::DidReceiveFlowControl(WebSocketHandle* handle,
-                                                 int64_t quota) {
-  NETWORK_DVLOG(1) << this << " DidReceiveFlowControl(" << handle << ", "
+void WebSocketChannelImpl::AddSendFlowControlQuota(WebSocketHandle* handle,
+                                                   int64_t quota) {
+  NETWORK_DVLOG(1) << this << " AddSendFlowControlQuota(" << handle << ", "
                    << quota << ")";
 
   DCHECK(handle_);
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.h b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.h
index b2c8db0..317ba3b 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.h
+++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.h
@@ -174,7 +174,7 @@
                 bool was_clean,
                 uint16_t code,
                 const String& reason) override;
-  void DidReceiveFlowControl(WebSocketHandle*, int64_t quota) override;
+  void AddSendFlowControlQuota(WebSocketHandle*, int64_t quota) override;
   void DidStartClosingHandshake(WebSocketHandle*) override;
 
   // Completion callback. It is called with the results of throttling.
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl_test.cc b/third_party/blink/renderer/modules/websockets/websocket_channel_impl_test.cc
index 7e86713..8ef63ee 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl_test.cc
+++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl_test.cc
@@ -239,7 +239,7 @@
                                 MemEq("baz", 3), 3));
   }
 
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   Channel()->Send("foo");
@@ -272,18 +272,18 @@
                                 MemEq("MNOPQRSTUVWXYZ", 14), 14));
   }
 
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   Channel()->Send("0123456789abcdefg");
   Channel()->Send("hijk");
   Channel()->Send("lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
   checkpoint.Call(1);
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
   checkpoint.Call(2);
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
   checkpoint.Call(3);
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
 
   EXPECT_EQ(62ul, sum_of_consumed_buffered_amount_);
 }
@@ -296,7 +296,7 @@
                                 MemEq("foo", 3), 3));
   }
 
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   Vector<char> foo_vector;
@@ -320,7 +320,7 @@
                                 MemEq("\0\0\0", 3), 3));
   }
 
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   {
@@ -352,7 +352,7 @@
   EXPECT_CALL(*Handle(), Send(true, WebSocketHandle::kMessageTypeBinary,
                               MemEq("\xe7\x8b\x90", 3), 3));
 
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   Vector<char> v;
@@ -367,7 +367,7 @@
   EXPECT_CALL(*Handle(), Send(true, WebSocketHandle::kMessageTypeBinary,
                               MemEq("\x80\xff\xe7", 3), 3));
 
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   Vector<char> v;
@@ -392,7 +392,7 @@
                                 MemEq("\x8b\x90", 2), 2));
   }
 
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   Vector<char> v;
@@ -403,7 +403,7 @@
   Channel()->SendBinaryAsCharVector(std::make_unique<Vector<char>>(v));
   checkpoint.Call(1);
 
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
 
   EXPECT_EQ(18ul, sum_of_consumed_buffered_amount_);
 }
@@ -416,7 +416,7 @@
                                 MemEq("foo", 3), 3));
   }
 
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   DOMArrayBuffer* foo_buffer = DOMArrayBuffer::Create("foo", 3);
@@ -439,7 +439,7 @@
                                 MemEq("a", 1), 1));
   }
 
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   DOMArrayBuffer* foobar_buffer = DOMArrayBuffer::Create("foobar", 6);
@@ -466,7 +466,7 @@
                                 MemEq("\0\0\0", 3), 3));
   }
 
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   {
@@ -494,7 +494,7 @@
   EXPECT_CALL(*Handle(), Send(true, WebSocketHandle::kMessageTypeBinary,
                               MemEq("\xe7\x8b\x90", 3), 3));
 
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   DOMArrayBuffer* b = DOMArrayBuffer::Create("\xe7\x8b\x90", 3);
@@ -508,7 +508,7 @@
   EXPECT_CALL(*Handle(), Send(true, WebSocketHandle::kMessageTypeBinary,
                               MemEq("\x80\xff\xe7", 3), 3));
 
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   DOMArrayBuffer* b = DOMArrayBuffer::Create("\x80\xff\xe7", 3);
@@ -533,7 +533,7 @@
                                 MemEq("\x8b\x90", 2), 2));
   }
 
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   DOMArrayBuffer* b = DOMArrayBuffer::Create(
@@ -543,7 +543,7 @@
   Channel()->Send(*b, 0, 18);
   checkpoint.Call(1);
 
-  HandleClient()->DidReceiveFlowControl(Handle(), 16);
+  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
 
   EXPECT_EQ(18ul, sum_of_consumed_buffered_amount_);
 }
diff --git a/third_party/blink/renderer/modules/websockets/websocket_handle_client.h b/third_party/blink/renderer/modules/websockets/websocket_handle_client.h
index 1f90955d..b353160 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_handle_client.h
+++ b/third_party/blink/renderer/modules/websockets/websocket_handle_client.h
@@ -80,7 +80,7 @@
                         uint16_t code,
                         const String& reason) = 0;
 
-  virtual void DidReceiveFlowControl(WebSocketHandle*, int64_t quota) = 0;
+  virtual void AddSendFlowControlQuota(WebSocketHandle*, int64_t quota) = 0;
 
   // Called when the browser receives a Close frame from the remote
   // server. Not called when the renderer initiates the closing handshake.
diff --git a/third_party/blink/renderer/modules/websockets/websocket_handle_impl.cc b/third_party/blink/renderer/modules/websockets/websocket_handle_impl.cc
index 90aaada9..7b5a0df7 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_handle_impl.cc
+++ b/third_party/blink/renderer/modules/websockets/websocket_handle_impl.cc
@@ -204,12 +204,12 @@
   // |this| can be deleted here.
 }
 
-void WebSocketHandleImpl::OnFlowControl(int64_t quota) {
-  NETWORK_DVLOG(1) << this << " OnFlowControl(" << quota << ")";
+void WebSocketHandleImpl::AddSendFlowControlQuota(int64_t quota) {
+  NETWORK_DVLOG(1) << this << " AddSendFlowControlQuota(" << quota << ")";
   if (!client_)
     return;
 
-  client_->DidReceiveFlowControl(this, quota);
+  client_->AddSendFlowControlQuota(this, quota);
   // |this| can be deleted here.
 }
 
diff --git a/third_party/blink/renderer/modules/websockets/websocket_handle_impl.h b/third_party/blink/renderer/modules/websockets/websocket_handle_impl.h
index ec284a1..482d51d 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_handle_impl.h
+++ b/third_party/blink/renderer/modules/websockets/websocket_handle_impl.h
@@ -73,7 +73,7 @@
   void OnDataFrame(bool fin,
                    network::mojom::blink::WebSocketMessageType,
                    const Vector<uint8_t>& data) override;
-  void OnFlowControl(int64_t quota) override;
+  void AddSendFlowControlQuota(int64_t quota) override;
   void OnDropChannel(bool was_clean,
                      uint16_t code,
                      const String& reason) override;
diff --git a/third_party/blink/renderer/modules/xr/xr.cc b/third_party/blink/renderer/modules/xr/xr.cc
index 3da5920..304ec3a 100644
--- a/third_party/blink/renderer/modules/xr/xr.cc
+++ b/third_party/blink/renderer/modules/xr/xr.cc
@@ -372,10 +372,6 @@
   device::mojom::blink::XRSessionOptionsPtr session_options =
       convertModeToMojo(query->mode);
 
-  // TODO(http://crbug.com/826899) Once device activation is sorted out for
-  // WebXR, either pass in the correct value for metrics to know whether
-  // this was triggered by device activation, or remove the value as soon as
-  // legacy API has been removed.
   outstanding_request_queries_.insert(query);
   device_->RequestSession(
       std::move(session_options),
diff --git a/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.cc b/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.cc
index 0437f899..ac00f79 100644
--- a/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/modules/xr/xr_reference_space_event.h"
 #include "third_party/blink/renderer/modules/xr/xr_rigid_transform.h"
 #include "third_party/blink/renderer/modules/xr/xr_session.h"
+#include "third_party/blink/renderer/modules/xr/xr_utils.h"
 
 namespace blink {
 
@@ -39,11 +40,9 @@
 
   if (display_info && display_info->stageParameters) {
     // Use the transform given by xrDisplayInfo's stageParameters if available.
-    const WTF::Vector<float>& m =
-        display_info->stageParameters->standingTransform;
     floor_level_transform_ = std::make_unique<TransformationMatrix>(
-        m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10],
-        m[11], m[12], m[13], m[14], m[15]);
+        WTFFloatVectorToTransformationMatrix(
+            display_info->stageParameters->standingTransform));
 
     // In order to ensure that the bounds continue to line up with the user's
     // physical environment we need to transform by the inverse of the
diff --git a/third_party/blink/renderer/modules/xr/xr_canvas_input_provider.cc b/third_party/blink/renderer/modules/xr/xr_canvas_input_provider.cc
index 83d6651..b1bc0a1 100644
--- a/third_party/blink/renderer/modules/xr/xr_canvas_input_provider.cc
+++ b/third_party/blink/renderer/modules/xr/xr_canvas_input_provider.cc
@@ -110,12 +110,11 @@
   // projection matrix to get a 3D point in space, which is then returned in
   // matrix form so we can use it as an XRInputSource's pointerMatrix.
   XRViewData& view = session_->views()[0];
-  std::unique_ptr<TransformationMatrix> pointer_transform_matrix =
-      view.UnprojectPointer(element_x, element_y, canvas_->OffsetWidth(),
-                            canvas_->OffsetHeight());
+  TransformationMatrix pointer_transform_matrix = view.UnprojectPointer(
+      element_x, element_y, canvas_->OffsetWidth(), canvas_->OffsetHeight());
 
   // Update the input source's pointer matrix.
-  input_source_->SetPointerTransformMatrix(std::move(pointer_transform_matrix));
+  input_source_->SetPointerTransformMatrix(&pointer_transform_matrix);
 }
 
 void XRCanvasInputProvider::ClearInputSource() {
diff --git a/third_party/blink/renderer/modules/xr/xr_frame.cc b/third_party/blink/renderer/modules/xr/xr_frame.cc
index 01e2c48..bec958f 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame.cc
@@ -31,14 +31,6 @@
 XRFrame::XRFrame(XRSession* session, XRWorldInformation* world_information)
     : world_information_(world_information), session_(session) {}
 
-std::unique_ptr<TransformationMatrix> XRFrame::CloneBasePoseMatrix() const {
-  if (!base_pose_matrix_) {
-    return nullptr;
-  }
-
-  return std::make_unique<TransformationMatrix>(*base_pose_matrix_);
-}
-
 XRViewerPose* XRFrame::getViewerPose(XRReferenceSpace* reference_space,
                                      ExceptionState& exception_state) const {
   if (!is_active_) {
@@ -67,12 +59,12 @@
   session_->LogGetPose();
 
   std::unique_ptr<TransformationMatrix> pose =
-      reference_space->GetViewerPoseMatrix(CloneBasePoseMatrix());
+      reference_space->GetViewerPoseMatrix(base_pose_matrix_.get());
   if (!pose) {
     return nullptr;
   }
 
-  return MakeGarbageCollected<XRViewerPose>(session(), std::move(pose));
+  return MakeGarbageCollected<XRViewerPose>(session(), *pose);
 }
 
 // Return an XRPose that has a transform mapping to space A from space B, while
@@ -103,7 +95,7 @@
     return nullptr;
   }
 
-  return space_A->getPose(space_B, CloneBasePoseMatrix());
+  return space_A->getPose(space_B, base_pose_matrix_.get());
 }
 
 void XRFrame::SetBasePoseMatrix(const TransformationMatrix& base_pose_matrix) {
diff --git a/third_party/blink/renderer/modules/xr/xr_frame.h b/third_party/blink/renderer/modules/xr/xr_frame.h
index ffa5e80..616a47d 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame.h
+++ b/third_party/blink/renderer/modules/xr/xr_frame.h
@@ -37,7 +37,6 @@
   XRWorldInformation* worldInformation() const { return world_information_; }
 
   void SetBasePoseMatrix(const TransformationMatrix&);
-  std::unique_ptr<TransformationMatrix> CloneBasePoseMatrix() const;
 
   void Trace(blink::Visitor*) override;
 
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
index 306110d..bfcbad5 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
@@ -207,8 +207,6 @@
                                     WrapWeakPersistent(this)));
 }
 
-// TODO(lincolnfrog): add a ScheduleNonImmersiveARFrame, if we want camera RAF
-// alignment instead of doc RAF alignment.
 void XRFrameProvider::ScheduleNonImmersiveFrame(
     device::mojom::blink::XRFrameDataRequestOptionsPtr options) {
   TRACE_EVENT0("gpu", __FUNCTION__);
@@ -260,8 +258,6 @@
 
   // We may have lost the immersive session since the last VSync request.
   if (!immersive_session_) {
-    // TODO(https://crbug.com/836496): do we need to include this in the
-    // image size calculation for AR? What about immersive AR (full-screen?)
     return;
   }
 
diff --git a/third_party/blink/renderer/modules/xr/xr_grip_space.cc b/third_party/blink/renderer/modules/xr/xr_grip_space.cc
index 146a388..bc48fddc 100644
--- a/third_party/blink/renderer/modules/xr/xr_grip_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_grip_space.cc
@@ -14,9 +14,8 @@
 XRGripSpace::XRGripSpace(XRSession* session, XRInputSource* source)
     : XRSpace(session), input_source_(source) {}
 
-XRPose* XRGripSpace::getPose(
-    XRSpace* other_space,
-    std::unique_ptr<TransformationMatrix> base_pose_matrix) {
+XRPose* XRGripSpace::getPose(XRSpace* other_space,
+                             const TransformationMatrix* base_pose_matrix) {
   // Grip is only available when using tracked pointer for input.
   if (input_source_->target_ray_mode_ != XRInputSource::kTrackedPointer) {
     return nullptr;
@@ -37,10 +36,9 @@
 
   // Account for any changes made to the reference space's origin offset so
   // that things like teleportation works.
-  grip_pose = std::make_unique<TransformationMatrix>(
-      other_space->InverseOriginOffsetMatrix().Multiply(*grip_pose));
-
-  return MakeGarbageCollected<XRPose>(std::move(grip_pose),
+  TransformationMatrix adjusted_pose =
+      other_space->InverseOriginOffsetMatrix().Multiply(*grip_pose);
+  return MakeGarbageCollected<XRPose>(adjusted_pose,
                                       input_source_->emulatedPosition());
 }
 
diff --git a/third_party/blink/renderer/modules/xr/xr_grip_space.h b/third_party/blink/renderer/modules/xr/xr_grip_space.h
index 1153ef8..f24ad46f 100644
--- a/third_party/blink/renderer/modules/xr/xr_grip_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_grip_space.h
@@ -14,9 +14,8 @@
 class XRGripSpace : public XRSpace {
  public:
   XRGripSpace(XRSession*, XRInputSource*);
-  XRPose* getPose(
-      XRSpace* other_space,
-      std::unique_ptr<TransformationMatrix> base_pose_matrix) override;
+  XRPose* getPose(XRSpace* other_space,
+                  const TransformationMatrix* base_pose_matrix) override;
 
   void Trace(blink::Visitor*) override;
 
diff --git a/third_party/blink/renderer/modules/xr/xr_hit_result.cc b/third_party/blink/renderer/modules/xr/xr_hit_result.cc
index 07b9349..b14b3e1 100644
--- a/third_party/blink/renderer/modules/xr/xr_hit_result.cc
+++ b/third_party/blink/renderer/modules/xr/xr_hit_result.cc
@@ -8,8 +8,8 @@
 #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
 
 namespace blink {
-XRHitResult::XRHitResult(std::unique_ptr<TransformationMatrix> hit_transform)
-    : hit_transform_(std::move(hit_transform)) {}
+XRHitResult::XRHitResult(const TransformationMatrix& hit_transform)
+    : hit_transform_(std::make_unique<TransformationMatrix>(hit_transform)) {}
 
 XRHitResult::~XRHitResult() {}
 
diff --git a/third_party/blink/renderer/modules/xr/xr_hit_result.h b/third_party/blink/renderer/modules/xr/xr_hit_result.h
index 27d8dce..705bffb7 100644
--- a/third_party/blink/renderer/modules/xr/xr_hit_result.h
+++ b/third_party/blink/renderer/modules/xr/xr_hit_result.h
@@ -15,7 +15,7 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  explicit XRHitResult(std::unique_ptr<TransformationMatrix>);
+  explicit XRHitResult(const TransformationMatrix&);
   ~XRHitResult() override;
 
   DOMFloat32Array* hitMatrix() const;
diff --git a/third_party/blink/renderer/modules/xr/xr_input_source.cc b/third_party/blink/renderer/modules/xr/xr_input_source.cc
index ebb9994..7ef1cae 100644
--- a/third_party/blink/renderer/modules/xr/xr_input_source.cc
+++ b/third_party/blink/renderer/modules/xr/xr_input_source.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/modules/xr/xr_session.h"
 #include "third_party/blink/renderer/modules/xr/xr_space.h"
 #include "third_party/blink/renderer/modules/xr/xr_target_ray_space.h"
+#include "third_party/blink/renderer/modules/xr/xr_utils.h"
 
 namespace blink {
 
@@ -87,22 +88,17 @@
     updated_source->SetEmulatedPosition(desc->emulated_position);
 
     if (desc->pointer_offset && desc->pointer_offset->matrix.has_value()) {
-      const WTF::Vector<float>& m = desc->pointer_offset->matrix.value();
-      std::unique_ptr<TransformationMatrix> pointer_matrix =
-          std::make_unique<TransformationMatrix>(
-              m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10],
-              m[11], m[12], m[13], m[14], m[15]);
-      updated_source->SetPointerTransformMatrix(std::move(pointer_matrix));
+      TransformationMatrix pointer_matrix =
+          WTFFloatVectorToTransformationMatrix(
+              desc->pointer_offset->matrix.value());
+      updated_source->SetPointerTransformMatrix(&pointer_matrix);
     }
   }
 
   if (state->grip && state->grip->matrix.has_value()) {
-    const Vector<float>& m = state->grip->matrix.value();
-    std::unique_ptr<TransformationMatrix> grip_matrix =
-        std::make_unique<TransformationMatrix>(
-            m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10],
-            m[11], m[12], m[13], m[14], m[15]);
-    updated_source->SetBasePoseMatrix(std::move(grip_matrix));
+    TransformationMatrix grip_matrix =
+        WTFFloatVectorToTransformationMatrix(state->grip->matrix.value());
+    updated_source->SetBasePoseMatrix(&grip_matrix);
   } else {
     updated_source->SetBasePoseMatrix(nullptr);
   }
@@ -153,15 +149,8 @@
   SetTargetRayMode(other.target_ray_mode_);
   SetHandedness(other.handedness_);
 
-  if (other.base_pose_matrix_) {
-    base_pose_matrix_ = std::make_unique<TransformationMatrix>(
-        *(other.base_pose_matrix_.get()));
-  }
-
-  if (other.pointer_transform_matrix_) {
-    pointer_transform_matrix_ = std::make_unique<TransformationMatrix>(
-        *(other.pointer_transform_matrix_.get()));
-  }
+  SetBasePoseMatrix(other.base_pose_matrix_.get());
+  SetPointerTransformMatrix(other.pointer_transform_matrix_.get());
 }
 
 XRSpace* XRInputSource::gripSpace() const {
@@ -258,13 +247,23 @@
 }
 
 void XRInputSource::SetBasePoseMatrix(
-    std::unique_ptr<TransformationMatrix> base_pose_matrix) {
-  base_pose_matrix_ = std::move(base_pose_matrix);
+    const TransformationMatrix* base_pose_matrix) {
+  if (base_pose_matrix) {
+    base_pose_matrix_ =
+        std::make_unique<TransformationMatrix>(*base_pose_matrix);
+  } else {
+    base_pose_matrix_ = nullptr;
+  }
 }
 
 void XRInputSource::SetPointerTransformMatrix(
-    std::unique_ptr<TransformationMatrix> pointer_transform_matrix) {
-  pointer_transform_matrix_ = std::move(pointer_transform_matrix);
+    const TransformationMatrix* pointer_transform_matrix) {
+  if (pointer_transform_matrix) {
+    pointer_transform_matrix_ =
+        std::make_unique<TransformationMatrix>(*pointer_transform_matrix);
+  } else {
+    pointer_transform_matrix_ = nullptr;
+  }
 }
 
 void XRInputSource::SetGamepadConnected(bool state) {
diff --git a/third_party/blink/renderer/modules/xr/xr_input_source.h b/third_party/blink/renderer/modules/xr/xr_input_source.h
index eb8c23ac..0002950 100644
--- a/third_party/blink/renderer/modules/xr/xr_input_source.h
+++ b/third_party/blink/renderer/modules/xr/xr_input_source.h
@@ -69,7 +69,7 @@
 
   uint32_t source_id() const { return source_id_; }
 
-  void SetPointerTransformMatrix(std::unique_ptr<TransformationMatrix>);
+  void SetPointerTransformMatrix(const TransformationMatrix*);
   void SetGamepadConnected(bool state);
 
   // Gamepad::Client
@@ -90,7 +90,7 @@
   void SetHandedness(Handedness);
   void SetTargetRayMode(TargetRayMode);
   void SetEmulatedPosition(bool emulated_position);
-  void SetBasePoseMatrix(std::unique_ptr<TransformationMatrix>);
+  void SetBasePoseMatrix(const TransformationMatrix*);
 
   // Use to check if the updates that would/should be made by a given
   // XRInputSourceState would invalidate any SameObject properties guaranteed
@@ -116,12 +116,10 @@
 
   bool emulated_position_ = false;
 
-  // TODO(crbug.com/945947): Revisit use of std::unique_ptr.
   std::unique_ptr<TransformationMatrix> base_pose_matrix_;
 
   // This is the transform to apply to the base_pose_matrix_ to get the pointer
   // matrix. In most cases it should be static.
-  // TODO(crbug.com/945947): Revisit use of std::unique_ptr.
   std::unique_ptr<TransformationMatrix> pointer_transform_matrix_;
 
   // gamepad_ uses this to get relative timestamps.
diff --git a/third_party/blink/renderer/modules/xr/xr_plane.cc b/third_party/blink/renderer/modules/xr/xr_plane.cc
index bae1192..2e9543f 100644
--- a/third_party/blink/renderer/modules/xr/xr_plane.cc
+++ b/third_party/blink/renderer/modules/xr/xr_plane.cc
@@ -25,16 +25,16 @@
                  const HeapVector<Member<DOMPointReadOnly>>& polygon)
     : polygon_(polygon),
       orientation_(orientation),
-      pose_matrix_(pose_matrix),
+      pose_matrix_(std::make_unique<TransformationMatrix>(pose_matrix)),
       session_(session) {
   DVLOG(3) << __func__;
 }
 
 XRPose* XRPlane::getPose(XRReferenceSpace* reference_space) const {
-  return MakeGarbageCollected<XRPose>(
-      reference_space->GetViewerPoseMatrix(
-          std::make_unique<TransformationMatrix>(pose_matrix_)),
-      session_->EmulatedPosition());
+  std::unique_ptr<TransformationMatrix> viewer_pose =
+      reference_space->GetViewerPoseMatrix(pose_matrix_.get());
+  return MakeGarbageCollected<XRPose>(*viewer_pose,
+                                      session_->EmulatedPosition());
 }
 
 String XRPlane::orientation() const {
@@ -62,7 +62,8 @@
 
   orientation_ = mojo::ConvertTo<base::Optional<blink::XRPlane::Orientation>>(
       plane_data->orientation);
-  pose_matrix_ = mojo::ConvertTo<blink::TransformationMatrix>(plane_data->pose);
+  pose_matrix_ = std::make_unique<TransformationMatrix>(
+      mojo::ConvertTo<blink::TransformationMatrix>(plane_data->pose));
   polygon_ = mojo::ConvertTo<HeapVector<Member<DOMPointReadOnly>>>(
       plane_data->polygon);
 }
diff --git a/third_party/blink/renderer/modules/xr/xr_plane.h b/third_party/blink/renderer/modules/xr/xr_plane.h
index c4ca8f9..3f7098d 100644
--- a/third_party/blink/renderer/modules/xr/xr_plane.h
+++ b/third_party/blink/renderer/modules/xr/xr_plane.h
@@ -50,7 +50,7 @@
   base::Optional<Orientation> orientation_;
 
   // Plane center's pose in device (mojo) space.
-  TransformationMatrix pose_matrix_;
+  std::unique_ptr<TransformationMatrix> pose_matrix_;
 
   Member<XRSession> session_;
 };
diff --git a/third_party/blink/renderer/modules/xr/xr_pose.cc b/third_party/blink/renderer/modules/xr/xr_pose.cc
index e4de76e..91a5d4c 100644
--- a/third_party/blink/renderer/modules/xr/xr_pose.cc
+++ b/third_party/blink/renderer/modules/xr/xr_pose.cc
@@ -8,10 +8,9 @@
 
 namespace blink {
 
-XRPose::XRPose(std::unique_ptr<TransformationMatrix> pose_model_matrix,
+XRPose::XRPose(const TransformationMatrix& pose_model_matrix,
                bool emulated_position)
-    : transform_(
-          MakeGarbageCollected<XRRigidTransform>(std::move(pose_model_matrix))),
+    : transform_(MakeGarbageCollected<XRRigidTransform>(pose_model_matrix)),
       emulated_position_(emulated_position) {}
 
 void XRPose::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/modules/xr/xr_pose.h b/third_party/blink/renderer/modules/xr/xr_pose.h
index 8003b42..df99305 100644
--- a/third_party/blink/renderer/modules/xr/xr_pose.h
+++ b/third_party/blink/renderer/modules/xr/xr_pose.h
@@ -19,7 +19,7 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  XRPose(std::unique_ptr<TransformationMatrix>, bool);
+  XRPose(const TransformationMatrix&, bool);
   ~XRPose() override = default;
 
   XRRigidTransform* transform() const { return transform_; }
diff --git a/third_party/blink/renderer/modules/xr/xr_ray.cc b/third_party/blink/renderer/modules/xr/xr_ray.cc
index 5be2efb5..4a1c664 100644
--- a/third_party/blink/renderer/modules/xr/xr_ray.cc
+++ b/third_party/blink/renderer/modules/xr/xr_ray.cc
@@ -19,13 +19,12 @@
 
 namespace blink {
 
-XRRay::XRRay(std::unique_ptr<TransformationMatrix> matrix) {
-  Set(std::move(matrix));
+XRRay::XRRay(const TransformationMatrix& matrix) {
+  Set(matrix);
 }
 
 XRRay::XRRay(XRRigidTransform* transform) {
   DOMFloat32Array* m = transform->matrix();
-
   Set(DOMFloat32ArrayToTransformationMatrix(m));
 }
 
@@ -47,9 +46,9 @@
   Set(o, d);
 }
 
-void XRRay::Set(std::unique_ptr<TransformationMatrix> matrix) {
-  FloatPoint3D origin = matrix->MapPoint(FloatPoint3D(0, 0, 0));
-  FloatPoint3D direction = matrix->MapPoint(FloatPoint3D(0, 0, -1));
+void XRRay::Set(const TransformationMatrix& matrix) {
+  FloatPoint3D origin = matrix.MapPoint(FloatPoint3D(0, 0, 0));
+  FloatPoint3D direction = matrix.MapPoint(FloatPoint3D(0, 0, -1));
   direction.Move(-origin.X(), -origin.Y(), -origin.Z());
 
   Set(origin, direction);
diff --git a/third_party/blink/renderer/modules/xr/xr_ray.h b/third_party/blink/renderer/modules/xr/xr_ray.h
index 2652cd24..02f296b 100644
--- a/third_party/blink/renderer/modules/xr/xr_ray.h
+++ b/third_party/blink/renderer/modules/xr/xr_ray.h
@@ -23,7 +23,7 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  explicit XRRay(std::unique_ptr<TransformationMatrix> matrix);
+  explicit XRRay(const TransformationMatrix& matrix);
   explicit XRRay(XRRigidTransform* transform);
   XRRay(DOMPointInit* origin, DOMPointInit* direction);
   ~XRRay() override;
@@ -40,7 +40,7 @@
   void Trace(blink::Visitor*) override;
 
  private:
-  void Set(std::unique_ptr<TransformationMatrix> matrix);
+  void Set(const TransformationMatrix& matrix);
   void Set(FloatPoint3D origin, FloatPoint3D direction);
 
   Member<DOMPointReadOnly> origin_;
diff --git a/third_party/blink/renderer/modules/xr/xr_reference_space.cc b/third_party/blink/renderer/modules/xr/xr_reference_space.cc
index 16256419..d4b9cc5 100644
--- a/third_party/blink/renderer/modules/xr/xr_reference_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_reference_space.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/modules/xr/xr_reference_space_event.h"
 #include "third_party/blink/renderer/modules/xr/xr_rigid_transform.h"
 #include "third_party/blink/renderer/modules/xr/xr_session.h"
+#include "third_party/blink/renderer/modules/xr/xr_utils.h"
 
 namespace blink {
 
@@ -47,17 +48,17 @@
 
 XRPose* XRReferenceSpace::getPose(
     XRSpace* other_space,
-    std::unique_ptr<TransformationMatrix> base_pose_matrix) {
+    const TransformationMatrix* base_pose_matrix) {
   if (type_ == Type::kTypeViewer) {
     std::unique_ptr<TransformationMatrix> viewer_pose_matrix =
-        other_space->GetViewerPoseMatrix(std::move(base_pose_matrix));
+        other_space->GetViewerPoseMatrix(base_pose_matrix);
     if (!viewer_pose_matrix) {
       return nullptr;
     }
-    return MakeGarbageCollected<XRPose>(std::move(viewer_pose_matrix),
+    return MakeGarbageCollected<XRPose>(*viewer_pose_matrix,
                                         session()->EmulatedPosition());
   } else {
-    return XRSpace::getPose(other_space, std::move(base_pose_matrix));
+    return XRSpace::getPose(other_space, base_pose_matrix);
   }
 }
 
@@ -67,11 +68,9 @@
 
   if (display_info && display_info->stageParameters) {
     // Use the transform given by xrDisplayInfo's stageParameters if available.
-    const WTF::Vector<float>& m =
-        display_info->stageParameters->standingTransform;
     floor_level_transform_ = std::make_unique<TransformationMatrix>(
-        m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10],
-        m[11], m[12], m[13], m[14], m[15]);
+        WTFFloatVectorToTransformationMatrix(
+            display_info->stageParameters->standingTransform));
   } else {
     // Otherwise, create a transform based on the default emulated height.
     floor_level_transform_ = std::make_unique<TransformationMatrix>();
diff --git a/third_party/blink/renderer/modules/xr/xr_reference_space.h b/third_party/blink/renderer/modules/xr/xr_reference_space.h
index 06770080..c4e012b34 100644
--- a/third_party/blink/renderer/modules/xr/xr_reference_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_reference_space.h
@@ -34,9 +34,8 @@
   XRReferenceSpace(XRSession*, XRRigidTransform*, Type);
   ~XRReferenceSpace() override;
 
-  XRPose* getPose(
-      XRSpace* other_space,
-      std::unique_ptr<TransformationMatrix> base_pose_matrix) override;
+  XRPose* getPose(XRSpace* other_space,
+                  const TransformationMatrix* base_pose_matrix) override;
   std::unique_ptr<TransformationMatrix> DefaultPose() override;
   std::unique_ptr<TransformationMatrix> TransformBasePose(
       const TransformationMatrix& base_pose) override;
diff --git a/third_party/blink/renderer/modules/xr/xr_rigid_transform.cc b/third_party/blink/renderer/modules/xr/xr_rigid_transform.cc
index 140c04e..5b757df 100644
--- a/third_party/blink/renderer/modules/xr/xr_rigid_transform.cc
+++ b/third_party/blink/renderer/modules/xr/xr_rigid_transform.cc
@@ -20,16 +20,6 @@
   DecomposeMatrix();
 }
 
-// takes ownership of transformationMatrix instead of copying it
-XRRigidTransform::XRRigidTransform(
-    std::unique_ptr<TransformationMatrix> transformationMatrix)
-    : matrix_(std::move(transformationMatrix)) {
-  if (!matrix_) {
-    matrix_ = std::make_unique<TransformationMatrix>();
-  }
-  DecomposeMatrix();
-}
-
 void XRRigidTransform::DecomposeMatrix() {
   // decompose matrix to position and orientation
   TransformationMatrix::DecomposedType decomposed;
@@ -46,7 +36,7 @@
         -decomposed.quaternion_x, -decomposed.quaternion_y,
         -decomposed.quaternion_z, decomposed.quaternion_w);
   } else {
-    // TODO: Is this the correct way to handle a failure here?
+    // TODO(crbug.com/969149): Is this the correct way to handle a failure here?
     position_ = DOMPointReadOnly::Create(0.0, 0.0, 0.0, 1.0);
     orientation_ = DOMPointReadOnly::Create(0.0, 0.0, 0.0, 1.0);
   }
diff --git a/third_party/blink/renderer/modules/xr/xr_rigid_transform.h b/third_party/blink/renderer/modules/xr/xr_rigid_transform.h
index b936975..2863528 100644
--- a/third_party/blink/renderer/modules/xr/xr_rigid_transform.h
+++ b/third_party/blink/renderer/modules/xr/xr_rigid_transform.h
@@ -25,7 +25,6 @@
 
  public:
   explicit XRRigidTransform(const TransformationMatrix&);
-  explicit XRRigidTransform(std::unique_ptr<TransformationMatrix>);
   XRRigidTransform(DOMPointInit*, DOMPointInit*);
   static XRRigidTransform* Create(DOMPointInit*, DOMPointInit*);
 
diff --git a/third_party/blink/renderer/modules/xr/xr_rigid_transform_test.cc b/third_party/blink/renderer/modules/xr/xr_rigid_transform_test.cc
index 15e6aa7d..7f035302 100644
--- a/third_party/blink/renderer/modules/xr/xr_rigid_transform_test.cc
+++ b/third_party/blink/renderer/modules/xr/xr_rigid_transform_test.cc
@@ -77,9 +77,9 @@
 }
 
 TEST(XRRigidTransformTest, Decompose) {
-  XRRigidTransform transform(std::make_unique<TransformationMatrix>(
-      1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 2.0,
-      3.0, 1.0));
+  TransformationMatrix matrix(1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, -1.0,
+                              0.0, 0.0, 1.0, 2.0, 3.0, 1.0);
+  XRRigidTransform transform(matrix);
   const DOMPointReadOnly expected_position(1.0, 2.0, 3.0, 1.0);
   const DOMPointReadOnly expected_orientation(0.7071068, 0.0, 0.0, 0.7071068);
   AssertDOMPointsEqualForTest(transform.position(), &expected_position);
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index 463738d..ea4dec3 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -233,8 +233,6 @@
     const device::mojom::blink::VRStageParametersPtr& stage_parameters) {
   auto display_info = display_info_.Clone();
   display_info->stageParameters = stage_parameters.Clone();
-
-  // TODO(https://crbug.com/922175): Should bubble up to other events
   SetXRDisplayInfo(std::move(display_info));
 }
 
@@ -361,8 +359,6 @@
                           script_state->GetIsolate(), kNoSpaceSpecified));
   }
 
-  // TODO(https://crbug.com/846411): use space.
-
   // Reject the promise if device doesn't support the hit-test API.
   // TODO(https://crbug.com/878936): Get the environment provider without going
   // up to xr_, since it doesn't know which runtime's environment provider
@@ -412,8 +408,8 @@
 
   HeapVector<Member<XRHitResult>> hit_results;
   for (const auto& mojom_result : results.value()) {
-    XRHitResult* hit_result = MakeGarbageCollected<XRHitResult>(
-        std::make_unique<TransformationMatrix>(
+    XRHitResult* hit_result =
+        MakeGarbageCollected<XRHitResult>(TransformationMatrix(
             mojom_result->hit_matrix[0], mojom_result->hit_matrix[1],
             mojom_result->hit_matrix[2], mojom_result->hit_matrix[3],
             mojom_result->hit_matrix[4], mojom_result->hit_matrix[5],
diff --git a/third_party/blink/renderer/modules/xr/xr_session.h b/third_party/blink/renderer/modules/xr/xr_session.h
index e92506b8..6dbfeb1 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.h
+++ b/third_party/blink/renderer/modules/xr/xr_session.h
@@ -149,7 +149,7 @@
     return display_info_;
   }
 
-  // TODO(jacde): Update the mojom to deliver this per-frame.
+  // TODO(crbug.com/969131): Update the mojom to deliver this per-frame.
   bool EmulatedPosition() const {
     if (display_info_) {
       return !display_info_->capabilities->hasPosition;
diff --git a/third_party/blink/renderer/modules/xr/xr_space.cc b/third_party/blink/renderer/modules/xr/xr_space.cc
index 2655d67f9..252b8a09 100644
--- a/third_party/blink/renderer/modules/xr/xr_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_space.cc
@@ -47,9 +47,8 @@
   return identity;
 }
 
-XRPose* XRSpace::getPose(
-    XRSpace* other_space,
-    std::unique_ptr<TransformationMatrix> base_pose_matrix) {
+XRPose* XRSpace::getPose(XRSpace* other_space,
+                         const TransformationMatrix* base_pose_matrix) {
   std::unique_ptr<TransformationMatrix> mojo_from_this =
       GetTransformToMojoSpace();
   if (!mojo_from_this) {
@@ -66,17 +65,17 @@
     return nullptr;
   }
 
-  // TODO(jacde): Update how EmulatedPosition is determined here once spec issue
-  // https://github.com/immersive-web/webxr/issues/534 has been resolved.
+  // TODO(crbug.com/969133): Update how EmulatedPosition is determined here once
+  // spec issue https://github.com/immersive-web/webxr/issues/534 has been
+  // resolved.
   TransformationMatrix this_from_other =
       this_from_mojo.Multiply(*mojo_from_other);
-  return MakeGarbageCollected<XRPose>(
-      std::make_unique<TransformationMatrix>(this_from_other),
-      session()->EmulatedPosition());
+  return MakeGarbageCollected<XRPose>(this_from_other,
+                                      session()->EmulatedPosition());
 }
 
 std::unique_ptr<TransformationMatrix> XRSpace::GetViewerPoseMatrix(
-    std::unique_ptr<TransformationMatrix> base_pose_matrix) {
+    const TransformationMatrix* base_pose_matrix) {
   std::unique_ptr<TransformationMatrix> pose;
 
   // If we don't have a valid base pose, request the reference space's default
diff --git a/third_party/blink/renderer/modules/xr/xr_space.h b/third_party/blink/renderer/modules/xr/xr_space.h
index d7a4e5de..c2c832d 100644
--- a/third_party/blink/renderer/modules/xr/xr_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_space.h
@@ -39,11 +39,11 @@
       const TransformationMatrix& base_input_pose,
       const TransformationMatrix& base_pose);
 
-  virtual XRPose* getPose(
-      XRSpace* other_space,
-      std::unique_ptr<TransformationMatrix> base_pose_matrix);
+  virtual XRPose* getPose(XRSpace* other_space,
+                          const TransformationMatrix* base_pose_matrix);
+
   std::unique_ptr<TransformationMatrix> GetViewerPoseMatrix(
-      std::unique_ptr<TransformationMatrix> base_pose_matrix);
+      const TransformationMatrix* base_pose_matrix);
 
   XRSession* session() const { return session_; }
 
diff --git a/third_party/blink/renderer/modules/xr/xr_target_ray_space.cc b/third_party/blink/renderer/modules/xr/xr_target_ray_space.cc
index e6ba36a..abd0580b 100644
--- a/third_party/blink/renderer/modules/xr/xr_target_ray_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_target_ray_space.cc
@@ -59,7 +59,7 @@
 
 XRPose* XRTargetRaySpace::getPose(
     XRSpace* other_space,
-    std::unique_ptr<TransformationMatrix> base_pose_matrix) {
+    const TransformationMatrix* base_pose_matrix) {
   // If we don't have a valid base pose (most common when tracking is lost),
   // we can't get a target ray pose regardless of the mode.
   if (!base_pose_matrix) {
@@ -94,10 +94,9 @@
 
   // Account for any changes made to the reference space's origin offset so that
   // things like teleportation works.
-  pointer_pose = std::make_unique<TransformationMatrix>(
-      other_space->InverseOriginOffsetMatrix().Multiply(*pointer_pose));
-
-  return MakeGarbageCollected<XRPose>(std::move(pointer_pose),
+  TransformationMatrix adjusted_pose =
+      other_space->InverseOriginOffsetMatrix().Multiply(*pointer_pose);
+  return MakeGarbageCollected<XRPose>(adjusted_pose,
                                       input_source_->emulatedPosition());
 }
 
diff --git a/third_party/blink/renderer/modules/xr/xr_target_ray_space.h b/third_party/blink/renderer/modules/xr/xr_target_ray_space.h
index 6d1c1a4..bf7c704 100644
--- a/third_party/blink/renderer/modules/xr/xr_target_ray_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_target_ray_space.h
@@ -14,9 +14,8 @@
 class XRTargetRaySpace : public XRSpace {
  public:
   XRTargetRaySpace(XRSession*, XRInputSource*);
-  XRPose* getPose(
-      XRSpace* other_space,
-      std::unique_ptr<TransformationMatrix> base_pose_matrix) override;
+  XRPose* getPose(XRSpace* other_space,
+                  const TransformationMatrix* base_pose_matrix) override;
 
   void Trace(blink::Visitor*) override;
 
diff --git a/third_party/blink/renderer/modules/xr/xr_utils.cc b/third_party/blink/renderer/modules/xr/xr_utils.cc
index 923b229..40ed6347 100644
--- a/third_party/blink/renderer/modules/xr/xr_utils.cc
+++ b/third_party/blink/renderer/modules/xr/xr_utils.cc
@@ -26,13 +26,12 @@
   return DOMFloat32Array::Create(array, 16);
 }
 
-std::unique_ptr<TransformationMatrix> DOMFloat32ArrayToTransformationMatrix(
-    DOMFloat32Array* m) {
+TransformationMatrix DOMFloat32ArrayToTransformationMatrix(DOMFloat32Array* m) {
   DCHECK_EQ(m->length(), 16u);
 
   auto* data = m->Data();
 
-  return std::make_unique<TransformationMatrix>(
+  return TransformationMatrix(
       static_cast<double>(data[0]), static_cast<double>(data[1]),
       static_cast<double>(data[2]), static_cast<double>(data[3]),
       static_cast<double>(data[4]), static_cast<double>(data[5]),
@@ -43,6 +42,13 @@
       static_cast<double>(data[14]), static_cast<double>(data[15]));
 }
 
+TransformationMatrix WTFFloatVectorToTransformationMatrix(
+    const WTF::Vector<float>& m) {
+  return TransformationMatrix(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7],
+                              m[8], m[9], m[10], m[11], m[12], m[13], m[14],
+                              m[15]);
+}
+
 // Normalize to have length = 1.0
 DOMPointReadOnly* makeNormalizedQuaternion(double x,
                                            double y,
diff --git a/third_party/blink/renderer/modules/xr/xr_utils.h b/third_party/blink/renderer/modules/xr/xr_utils.h
index a380cf0..4ecf343 100644
--- a/third_party/blink/renderer/modules/xr/xr_utils.h
+++ b/third_party/blink/renderer/modules/xr/xr_utils.h
@@ -5,9 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_UTILS_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_UTILS_H_
 
-#include <memory>
-
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
 
 namespace blink {
 
@@ -17,8 +16,10 @@
 DOMFloat32Array* transformationMatrixToDOMFloat32Array(
     const TransformationMatrix&);
 
-std::unique_ptr<TransformationMatrix> DOMFloat32ArrayToTransformationMatrix(
-    DOMFloat32Array*);
+TransformationMatrix DOMFloat32ArrayToTransformationMatrix(DOMFloat32Array*);
+
+TransformationMatrix WTFFloatVectorToTransformationMatrix(
+    const WTF::Vector<float>&);
 
 DOMPointReadOnly* makeNormalizedQuaternion(double x,
                                            double y,
diff --git a/third_party/blink/renderer/modules/xr/xr_view.cc b/third_party/blink/renderer/modules/xr/xr_view.cc
index 5baeb0d5..02b5e8ee 100644
--- a/third_party/blink/renderer/modules/xr/xr_view.cc
+++ b/third_party/blink/renderer/modules/xr/xr_view.cc
@@ -84,11 +84,10 @@
   offset_.Set(x, y, z);
 }
 
-std::unique_ptr<TransformationMatrix> XRViewData::UnprojectPointer(
-    double x,
-    double y,
-    double canvas_width,
-    double canvas_height) {
+TransformationMatrix XRViewData::UnprojectPointer(double x,
+                                                  double y,
+                                                  double canvas_width,
+                                                  double canvas_height) {
   // Recompute the inverse projection matrix if needed.
   if (inv_projection_dirty_) {
     inv_projection_ = projection_matrix_.Inverse();
@@ -127,10 +126,7 @@
                           -point_in_view_space.Z());
 
   // LookAt matrices are view matrices (inverted), so invert before returning.
-  std::unique_ptr<TransformationMatrix> pointer =
-      std::make_unique<TransformationMatrix>(inv_pointer.Inverse());
-
-  return pointer;
+  return inv_pointer.Inverse();
 }
 
 void XRViewData::UpdatePoseMatrix(const TransformationMatrix& pose_matrix) {
diff --git a/third_party/blink/renderer/modules/xr/xr_view.h b/third_party/blink/renderer/modules/xr/xr_view.h
index 04117b0..596433b 100644
--- a/third_party/blink/renderer/modules/xr/xr_view.h
+++ b/third_party/blink/renderer/modules/xr/xr_view.h
@@ -65,10 +65,10 @@
   const FloatPoint3D& offset() const { return offset_; }
   void UpdateOffset(float x, float y, float z);
 
-  std::unique_ptr<TransformationMatrix> UnprojectPointer(double x,
-                                                         double y,
-                                                         double canvas_width,
-                                                         double canvas_height);
+  TransformationMatrix UnprojectPointer(double x,
+                                        double y,
+                                        double canvas_width,
+                                        double canvas_height);
 
   XRView::XREye Eye() const { return eye_; }
   const TransformationMatrix& Transform() const { return transform_; }
diff --git a/third_party/blink/renderer/modules/xr/xr_viewer_pose.cc b/third_party/blink/renderer/modules/xr/xr_viewer_pose.cc
index c9dcbd3..43d1361 100644
--- a/third_party/blink/renderer/modules/xr/xr_viewer_pose.cc
+++ b/third_party/blink/renderer/modules/xr/xr_viewer_pose.cc
@@ -10,10 +10,9 @@
 
 namespace blink {
 
-XRViewerPose::XRViewerPose(
-    XRSession* session,
-    std::unique_ptr<TransformationMatrix> pose_model_matrix)
-    : XRPose(std::move(pose_model_matrix), session->EmulatedPosition()) {
+XRViewerPose::XRViewerPose(XRSession* session,
+                           const TransformationMatrix& pose_model_matrix)
+    : XRPose(pose_model_matrix, session->EmulatedPosition()) {
   WTF::Vector<XRViewData>& view_data = session->views();
 
   // Snapshot the session's current views.
diff --git a/third_party/blink/renderer/modules/xr/xr_viewer_pose.h b/third_party/blink/renderer/modules/xr/xr_viewer_pose.h
index 1a27fc6..652335f 100644
--- a/third_party/blink/renderer/modules/xr/xr_viewer_pose.h
+++ b/third_party/blink/renderer/modules/xr/xr_viewer_pose.h
@@ -18,7 +18,7 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  XRViewerPose(XRSession*, std::unique_ptr<TransformationMatrix>);
+  XRViewerPose(XRSession*, const TransformationMatrix&);
   ~XRViewerPose() override = default;
 
   const HeapVector<Member<XRView>>& views() const { return views_; }
diff --git a/third_party/blink/renderer/modules/xr/xr_viewer_pose.idl b/third_party/blink/renderer/modules/xr/xr_viewer_pose.idl
index ae08508..ecff9175 100644
--- a/third_party/blink/renderer/modules/xr/xr_viewer_pose.idl
+++ b/third_party/blink/renderer/modules/xr/xr_viewer_pose.idl
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 // https://immersive-web.github.io/webxr/#xrviewerpose-interface
-// TODO(https://crbug.com/915021): Update definition to match spec.
 [
     SecureContext,
     Exposed=Window,
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
index 863f5b3..c5aba89 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
@@ -82,8 +82,9 @@
     return nullptr;
   }
 
-  // TODO: In the future this should be communicated by the drawing buffer and
-  // indicate whether the depth buffers are being supplied to the XR compositor.
+  // TODO(crbug.com/941753): In the future this should be communicated by the
+  // drawing buffer and indicate whether the depth buffers are being supplied to
+  // the XR compositor.
   bool compositor_supports_depth_values = false;
   bool want_ignore_depth_values = initializer->ignoreDepthValues();
 
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 8143a422..e857b35 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -857,6 +857,8 @@
     "graphics/dark_mode_bitmap_image_classifier.h",
     "graphics/dark_mode_color_classifier.cc",
     "graphics/dark_mode_color_classifier.h",
+    "graphics/dark_mode_color_filter.cc",
+    "graphics/dark_mode_color_filter.h",
     "graphics/dark_mode_filter.cc",
     "graphics/dark_mode_filter.h",
     "graphics/dark_mode_settings.h",
@@ -1695,6 +1697,7 @@
     "graphics/compositor_element_id_test.cc",
     "graphics/contiguous_container_test.cc",
     "graphics/dark_mode_bitmap_image_classifier_test.cc",
+    "graphics/dark_mode_filter_test.cc",
     "graphics/decoding_image_generator_test.cc",
     "graphics/deferred_image_decoder_test_wo_platform.cc",
     "graphics/filters/fe_composite_test.cc",
diff --git a/third_party/blink/renderer/platform/bindings/parkable_string_test.cc b/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
index 846a200..ca57e9e 100644
--- a/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
+++ b/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
@@ -7,9 +7,11 @@
 #include <thread>
 #include <vector>
 
+#include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/memory_allocator_dump.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "base/trace_event/process_memory_dump.h"
@@ -20,6 +22,9 @@
 #include "third_party/blink/renderer/platform/bindings/parkable_string_manager.h"
 #include "third_party/blink/renderer/platform/memory_pressure_listener.h"
 
+using ThreadPoolExecutionMode =
+    base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode;
+
 namespace blink {
 
 namespace {
@@ -38,14 +43,21 @@
 
 class ParkableStringTestBase : public ::testing::Test {
  public:
-  ParkableStringTestBase()
+  ParkableStringTestBase(ThreadPoolExecutionMode thread_pool_execution_mode =
+                             ThreadPoolExecutionMode::DEFAULT)
       : ::testing::Test(),
         scoped_task_environment_(
-            base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME),
+            base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
+            thread_pool_execution_mode),
         scoped_feature_list_() {}
 
  protected:
-  void RunPostedTasks() { scoped_task_environment_.RunUntilIdle(); }
+  virtual
+
+      void
+      RunPostedTasks() {
+    scoped_task_environment_.RunUntilIdle();
+  }
 
   bool ParkAndWait(const ParkableString& string) {
     bool success =
@@ -57,7 +69,6 @@
   void WaitForDelayedParking() {
     scoped_task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(
         ParkableStringManager::kParkingDelayInSeconds));
-    RunPostedTasks();
   }
 
   void CheckOnlyCpuCostTaskRemains() {
@@ -77,7 +88,6 @@
     CHECK_EQ(0u, ParkableStringManager::Instance().Size());
     // Delayed tasks may remain, clear the queues.
     scoped_task_environment_.FastForwardUntilNoTasksRemain();
-    RunPostedTasks();
   }
 
   base::test::ScopedTaskEnvironment scoped_task_environment_;
@@ -99,7 +109,6 @@
   void WaitForStatisticsRecording() {
     scoped_task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(
         ParkableStringManager::kStatisticsRecordingDelayInSeconds));
-    RunPostedTasks();
   }
 
   ParkableString CreateAndParkAll() {
@@ -619,11 +628,17 @@
 }
 
 #if defined(ADDRESS_SANITIZER)
+#define EXPECT_ASAN_DEATH(statement, regex) EXPECT_DEATH(statement, regex)
+#else
+#define EXPECT_ASAN_DEATH(statement, regex) \
+  GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, )
+#endif
+
 TEST_F(ParkableStringTest, AsanPoisoningTest) {
   ParkableString parkable(MakeLargeString().ReleaseImpl());
   const LChar* data = parkable.ToString().Characters8();
   EXPECT_TRUE(ParkAndWait(parkable));
-  EXPECT_DEATH(EXPECT_NE(0, data[10]), "");
+  EXPECT_ASAN_DEATH(EXPECT_NE(0, data[10]), "");
 }
 
 // Non-regression test for crbug.com/905137.
@@ -638,7 +653,6 @@
   }
   RunPostedTasks();
 }
-#endif  // defined(ADDRESS_SANITIZER)
 
 TEST_F(ParkableStringTest, Compression) {
   base::HistogramTester histogram_tester;
@@ -814,14 +828,13 @@
 
 class ParkableStringForegroundParkingTest : public ParkableStringTestBase {
  public:
-  ParkableStringForegroundParkingTest() : ParkableStringTestBase() {}
+  using ParkableStringTestBase::ParkableStringTestBase;
 
  protected:
   void WaitForAging() {
     EXPECT_GT(scoped_task_environment_.GetPendingMainThreadTaskCount(), 0u);
     scoped_task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(
         ParkableStringManager::kAgingIntervalInSeconds));
-    RunPostedTasks();
   }
 
   void SetUp() override {
@@ -831,10 +844,7 @@
   }
 
   void TearDown() override {
-    while (scoped_task_environment_.GetPendingMainThreadTaskCount() != 0) {
-      WaitForAging();
-    }
-
+    scoped_task_environment_.FastForwardUntilNoTasksRemain();
     ParkableStringTestBase::TearDown();
   }
 };
@@ -961,19 +971,6 @@
   EXPECT_EQ(2u, scoped_task_environment_.GetPendingMainThreadTaskCount());
 }
 
-TEST_F(ParkableStringForegroundParkingTest, AgingParkingInProgress) {
-  ParkableString parkable(MakeLargeString().ReleaseImpl());
-
-  WaitForAging();
-  parkable.Impl()->Park(ParkableStringImpl::ParkingMode::kAlways);
-  // Aging should work if the string is being parked.
-  WaitForAging();
-  // The aging task is rescheduled.
-  EXPECT_EQ(2u, scoped_task_environment_.GetPendingMainThreadTaskCount());
-
-  EXPECT_TRUE(parkable.Impl()->is_parked());
-}
-
 TEST_F(ParkableStringForegroundParkingTest,
        NoBackgroundParkingWhenForegroundIsEnabled) {
   ParkableString parkable(MakeLargeString().ReleaseImpl());
@@ -1047,4 +1044,38 @@
       100 * compressed_size / original_size, 1);
 }
 
+class ParkableStringForegroundParkingTestWithQueuedThreadPool
+    : public ParkableStringForegroundParkingTest {
+ public:
+  ParkableStringForegroundParkingTestWithQueuedThreadPool()
+      : ParkableStringForegroundParkingTest(ThreadPoolExecutionMode::QUEUED) {}
+};
+
+TEST_F(ParkableStringForegroundParkingTestWithQueuedThreadPool,
+       AgingParkingInProgress) {
+  ParkableString parkable(MakeLargeString().ReleaseImpl());
+
+  WaitForAging();
+  parkable.Impl()->Park(ParkableStringImpl::ParkingMode::kAlways);
+
+  // Advance the main thread until aging occurs. This uses RunLoop combined with
+  // ThreadPoolExecutionMode::QUEUED to force the 2-seconds-delayed aging task
+  // on the main thread to kick in before the immediate async compression task
+  // completes.
+  base::RunLoop run_loop;
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, run_loop.QuitClosure(),
+      base::TimeDelta::FromSeconds(
+          ParkableStringManager::kAgingIntervalInSeconds));
+  run_loop.Run();
+
+  // The aging task is rescheduled.
+  EXPECT_EQ(2u, scoped_task_environment_.GetPendingMainThreadTaskCount());
+
+  // Complete asynchronous work.
+  RunPostedTasks();
+
+  EXPECT_TRUE(parkable.Impl()->is_parked());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/cursor.cc b/third_party/blink/renderer/platform/cursor.cc
index 8e45f8e1..cec1f650 100644
--- a/third_party/blink/renderer/platform/cursor.cc
+++ b/third_party/blink/renderer/platform/cursor.cc
@@ -254,6 +254,16 @@
   return c;
 }
 
+const Cursor& MiddlePanningVerticalCursor() {
+  DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kMiddlePanningVertical));
+  return c;
+}
+
+const Cursor& MiddlePanningHorizontalCursor() {
+  DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kMiddlePanningHorizontal));
+  return c;
+}
+
 const Cursor& EastPanningCursor() {
   DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kEastPanning));
   return c;
@@ -358,5 +368,9 @@
 STATIC_ASSERT_ENUM(WebCursorInfo::kTypeZoomOut, Cursor::kZoomOut);
 STATIC_ASSERT_ENUM(WebCursorInfo::kTypeGrab, Cursor::kGrab);
 STATIC_ASSERT_ENUM(WebCursorInfo::kTypeGrabbing, Cursor::kGrabbing);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeMiddlePanningVertical,
+                   Cursor::kMiddlePanningVertical);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeMiddlePanningHorizontal,
+                   Cursor::kMiddlePanningHorizontal);
 STATIC_ASSERT_ENUM(WebCursorInfo::kTypeCustom, Cursor::kCustom);
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/cursor.h b/third_party/blink/renderer/platform/cursor.h
index 0646bec..d3a56a02 100644
--- a/third_party/blink/renderer/platform/cursor.h
+++ b/third_party/blink/renderer/platform/cursor.h
@@ -85,6 +85,8 @@
     kZoomOut,
     kGrab,
     kGrabbing,
+    kMiddlePanningVertical,
+    kMiddlePanningHorizontal,
     kCustom
   };
 
@@ -148,6 +150,8 @@
 PLATFORM_EXPORT const Cursor& ColumnResizeCursor();
 PLATFORM_EXPORT const Cursor& RowResizeCursor();
 PLATFORM_EXPORT const Cursor& MiddlePanningCursor();
+PLATFORM_EXPORT const Cursor& MiddlePanningVerticalCursor();
+PLATFORM_EXPORT const Cursor& MiddlePanningHorizontalCursor();
 PLATFORM_EXPORT const Cursor& EastPanningCursor();
 PLATFORM_EXPORT const Cursor& NorthPanningCursor();
 PLATFORM_EXPORT const Cursor& NorthEastPanningCursor();
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index cbdd9619..acf7a67 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -354,10 +354,6 @@
   RuntimeEnabledFeatures::SetPaymentRequestHasEnrolledInstrumentEnabled(enable);
 }
 
-void WebRuntimeFeatures::EnablePaymentRetry(bool enable) {
-  RuntimeEnabledFeatures::SetPaymentRetryEnabled(enable);
-}
-
 void WebRuntimeFeatures::EnablePerformanceManagerInstrumentation(bool enable) {
   RuntimeEnabledFeatures::SetPerformanceManagerInstrumentationEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/fonts/font_description.cc b/third_party/blink/renderer/platform/fonts/font_description.cc
index b94fbb1..24e8e83 100644
--- a/third_party/blink/renderer/platform/fonts/font_description.cc
+++ b/third_party/blink/renderer/platform/fonts/font_description.cc
@@ -474,8 +474,8 @@
 String FontDescription::FamilyDescription::ToString() const {
   return String::Format(
       "generic_family=%s, family=[%s]",
-      FontDescription::ToString(generic_family).Ascii().data(),
-      family.ToString().Ascii().data());
+      FontDescription::ToString(generic_family).Ascii().c_str(),
+      family.ToString().Ascii().c_str());
 }
 
 static const char* ToBooleanString(bool value) {
@@ -497,34 +497,34 @@
       "synthetic_bold=%s, synthetic_italic=%s, subpixel_positioning=%s, "
       "subpixel_ascent_descent=%s, variant_numeric=[%s], "
       "variant_east_asian=[%s]",
-      family_list_.ToString().Ascii().data(),
-      (feature_settings_ ? feature_settings_->ToString().Ascii().data() : ""),
-      (variation_settings_ ? variation_settings_->ToString().Ascii().data()
+      family_list_.ToString().Ascii().c_str(),
+      (feature_settings_ ? feature_settings_->ToString().Ascii().c_str() : ""),
+      (variation_settings_ ? variation_settings_->ToString().Ascii().c_str()
                            : ""),
       // TODO(wkorman): Locale has additional internal fields such as
       // hyphenation and script. Consider adding a more detailed
       // string method.
-      (locale_ ? locale_->LocaleString().Ascii().data() : ""), specified_size_,
+      (locale_ ? locale_->LocaleString().Ascii().c_str() : ""), specified_size_,
       computed_size_, adjusted_size_, size_adjust_, letter_spacing_,
-      word_spacing_, font_selection_request_.ToString().Ascii().data(),
+      word_spacing_, font_selection_request_.ToString().Ascii().c_str(),
       blink::ToString(
           static_cast<TypesettingFeatures>(fields_.typesetting_features_))
           .Ascii()
           .data(),
-      blink::ToString(Orientation()).Ascii().data(),
-      blink::ToString(WidthVariant()).Ascii().data(),
-      FontDescription::ToString(VariantCaps()).Ascii().data(),
+      blink::ToString(Orientation()).Ascii().c_str(),
+      blink::ToString(WidthVariant()).Ascii().c_str(),
+      FontDescription::ToString(VariantCaps()).Ascii().c_str(),
       ToBooleanString(IsAbsoluteSize()),
-      FontDescription::ToString(GenericFamily()).Ascii().data(),
-      FontDescription::ToString(Kerning()).Ascii().data(),
-      GetVariantLigatures().ToString().Ascii().data(), KeywordSize(),
-      blink::ToString(FontSmoothing()).Ascii().data(),
-      blink::ToString(TextRendering()).Ascii().data(),
+      FontDescription::ToString(GenericFamily()).Ascii().c_str(),
+      FontDescription::ToString(Kerning()).Ascii().c_str(),
+      GetVariantLigatures().ToString().Ascii().c_str(), KeywordSize(),
+      blink::ToString(FontSmoothing()).Ascii().c_str(),
+      blink::ToString(TextRendering()).Ascii().c_str(),
       ToBooleanString(IsSyntheticBold()), ToBooleanString(IsSyntheticItalic()),
       ToBooleanString(UseSubpixelPositioning()),
       ToBooleanString(SubpixelAscentDescent()),
-      VariantNumeric().ToString().Ascii().data(),
-      VariantEastAsian().ToString().Ascii().data());
+      VariantNumeric().ToString().Ascii().c_str(),
+      VariantEastAsian().ToString().Ascii().c_str());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/font_selection_types.cc b/third_party/blink/renderer/platform/fonts/font_selection_types.cc
index 81efbd72..651aabb 100644
--- a/third_party/blink/renderer/platform/fonts/font_selection_types.cc
+++ b/third_party/blink/renderer/platform/fonts/font_selection_types.cc
@@ -79,8 +79,8 @@
 
 String FontSelectionRequest::ToString() const {
   return String::Format(
-      "weight=%s, width=%s, slope=%s", weight.ToString().Ascii().data(),
-      width.ToString().Ascii().data(), slope.ToString().Ascii().data());
+      "weight=%s, width=%s, slope=%s", weight.ToString().Ascii().c_str(),
+      width.ToString().Ascii().data(), slope.ToString().Ascii().c_str());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/font_variant_east_asian.cc b/third_party/blink/renderer/platform/fonts/font_variant_east_asian.cc
index 635cdb6e..9de5b08e 100644
--- a/third_party/blink/renderer/platform/fonts/font_variant_east_asian.cc
+++ b/third_party/blink/renderer/platform/fonts/font_variant_east_asian.cc
@@ -44,8 +44,8 @@
 
 String FontVariantEastAsian::ToString() const {
   return String::Format(
-      "form=%s, width=%s, ruby=%s", ToString(Form()).Ascii().data(),
-      ToString(Width()).Ascii().data(), Ruby() ? "true" : "false");
+      "form=%s, width=%s, ruby=%s", ToString(Form()).Ascii().c_str(),
+      ToString(Width()).Ascii().c_str(), Ruby() ? "true" : "false");
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/font_variant_numeric.cc b/third_party/blink/renderer/platform/fonts/font_variant_numeric.cc
index 179d2d2..097c3279 100644
--- a/third_party/blink/renderer/platform/fonts/font_variant_numeric.cc
+++ b/third_party/blink/renderer/platform/fonts/font_variant_numeric.cc
@@ -70,11 +70,11 @@
   return String::Format(
       "numeric_figure=%s, numeric_spacing=%s, numeric_fraction=%s, ordinal=%s, "
       "slashed_zero=%s",
-      ToString(NumericFigureValue()).Ascii().data(),
-      ToString(NumericSpacingValue()).Ascii().data(),
-      ToString(NumericFractionValue()).Ascii().data(),
-      ToString(OrdinalValue()).Ascii().data(),
-      ToString(SlashedZeroValue()).Ascii().data());
+      ToString(NumericFigureValue()).Ascii().c_str(),
+      ToString(NumericSpacingValue()).Ascii().c_str(),
+      ToString(NumericFractionValue()).Ascii().c_str(),
+      ToString(OrdinalValue()).Ascii().c_str(),
+      ToString(SlashedZeroValue()).Ascii().c_str());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/linux/font_cache_linux.cc b/third_party/blink/renderer/platform/fonts/linux/font_cache_linux.cc
index 2a7ce629..04c1970 100644
--- a/third_party/blink/renderer/platform/fonts/linux/font_cache_linux.cc
+++ b/third_party/blink/renderer/platform/fonts/linux/font_cache_linux.cc
@@ -124,7 +124,7 @@
 
   FontCache::PlatformFallbackFont fallback_font;
   FontCache::GetFontForCharacter(
-      c, font_description.LocaleOrDefault().Ascii().data(), &fallback_font);
+      c, font_description.LocaleOrDefault().Ascii().c_str(), &fallback_font);
   if (fallback_font.name.IsEmpty())
     return nullptr;
 
diff --git a/third_party/blink/renderer/platform/geometry/double_rect.cc b/third_party/blink/renderer/platform/geometry/double_rect.cc
index 4af26661..c657aeb 100644
--- a/third_party/blink/renderer/platform/geometry/double_rect.cc
+++ b/third_party/blink/renderer/platform/geometry/double_rect.cc
@@ -52,8 +52,8 @@
 }
 
 String DoubleRect::ToString() const {
-  return String::Format("%s %s", Location().ToString().Ascii().data(),
-                        Size().ToString().Ascii().data());
+  return String::Format("%s %s", Location().ToString().Ascii().c_str(),
+                        Size().ToString().Ascii().c_str());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/geometry/float_quad.cc b/third_party/blink/renderer/platform/geometry/float_quad.cc
index 4e05609..b7ae991 100644
--- a/third_party/blink/renderer/platform/geometry/float_quad.cc
+++ b/third_party/blink/renderer/platform/geometry/float_quad.cc
@@ -253,10 +253,10 @@
 }
 
 String FloatQuad::ToString() const {
-  return String::Format("%s; %s; %s; %s", p1_.ToString().Ascii().data(),
-                        p2_.ToString().Ascii().data(),
-                        p3_.ToString().Ascii().data(),
-                        p4_.ToString().Ascii().data());
+  return String::Format("%s; %s; %s; %s", p1_.ToString().Ascii().c_str(),
+                        p2_.ToString().Ascii().c_str(),
+                        p3_.ToString().Ascii().c_str(),
+                        p4_.ToString().Ascii().c_str());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/geometry/float_rect.cc b/third_party/blink/renderer/platform/geometry/float_rect.cc
index f66aa83..3b0b298b 100644
--- a/third_party/blink/renderer/platform/geometry/float_rect.cc
+++ b/third_party/blink/renderer/platform/geometry/float_rect.cc
@@ -271,8 +271,8 @@
 }
 
 String FloatRect::ToString() const {
-  return String::Format("%s %s", Location().ToString().Ascii().data(),
-                        Size().ToString().Ascii().data());
+  return String::Format("%s %s", Location().ToString().Ascii().c_str(),
+                        Size().ToString().Ascii().c_str());
 }
 
 WTF::TextStream& operator<<(WTF::TextStream& ts, const FloatRect& r) {
diff --git a/third_party/blink/renderer/platform/geometry/int_rect.cc b/third_party/blink/renderer/platform/geometry/int_rect.cc
index 0dac687..82929c7 100644
--- a/third_party/blink/renderer/platform/geometry/int_rect.cc
+++ b/third_party/blink/renderer/platform/geometry/int_rect.cc
@@ -172,8 +172,8 @@
 }
 
 String IntRect::ToString() const {
-  return String::Format("%s %s", Location().ToString().Ascii().data(),
-                        Size().ToString().Ascii().data());
+  return String::Format("%s %s", Location().ToString().Ascii().c_str(),
+                        Size().ToString().Ascii().c_str());
 }
 
 bool IntRect::IsValid() const {
diff --git a/third_party/blink/renderer/platform/geometry/layout_point.cc b/third_party/blink/renderer/platform/geometry/layout_point.cc
index 8ead868..e80bce8df 100644
--- a/third_party/blink/renderer/platform/geometry/layout_point.cc
+++ b/third_party/blink/renderer/platform/geometry/layout_point.cc
@@ -23,8 +23,8 @@
 }
 
 String LayoutPoint::ToString() const {
-  return String::Format("%s,%s", X().ToString().Ascii().data(),
-                        Y().ToString().Ascii().data());
+  return String::Format("%s,%s", X().ToString().Ascii().c_str(),
+                        Y().ToString().Ascii().c_str());
 }
 
 WTF::TextStream& operator<<(WTF::TextStream& ts, const LayoutPoint& point) {
diff --git a/third_party/blink/renderer/platform/geometry/layout_rect.cc b/third_party/blink/renderer/platform/geometry/layout_rect.cc
index 7d45391..aa0c524 100644
--- a/third_party/blink/renderer/platform/geometry/layout_rect.cc
+++ b/third_party/blink/renderer/platform/geometry/layout_rect.cc
@@ -161,8 +161,8 @@
 }
 
 String LayoutRect::ToString() const {
-  return String::Format("%s %s", Location().ToString().Ascii().data(),
-                        Size().ToString().Ascii().data());
+  return String::Format("%s %s", Location().ToString().Ascii().c_str(),
+                        Size().ToString().Ascii().c_str());
 }
 
 WTF::TextStream& operator<<(WTF::TextStream& ts, const LayoutRect& rect) {
diff --git a/third_party/blink/renderer/platform/geometry/layout_rect_outsets.cc b/third_party/blink/renderer/platform/geometry/layout_rect_outsets.cc
index 8c14be5d..d023293 100644
--- a/third_party/blink/renderer/platform/geometry/layout_rect_outsets.cc
+++ b/third_party/blink/renderer/platform/geometry/layout_rect_outsets.cc
@@ -57,9 +57,9 @@
 
 String LayoutRectOutsets::ToString() const {
   return String::Format(
-      "top %s; right %s; bottom %s; left %s", Top().ToString().Ascii().data(),
-      Right().ToString().Ascii().data(), Bottom().ToString().Ascii().data(),
-      Left().ToString().Ascii().data());
+      "top %s; right %s; bottom %s; left %s", Top().ToString().Ascii().c_str(),
+      Right().ToString().Ascii().c_str(), Bottom().ToString().Ascii().c_str(),
+      Left().ToString().Ascii().c_str());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/geometry/layout_size.cc b/third_party/blink/renderer/platform/geometry/layout_size.cc
index 5c7c985..844047d4 100644
--- a/third_party/blink/renderer/platform/geometry/layout_size.cc
+++ b/third_party/blink/renderer/platform/geometry/layout_size.cc
@@ -14,8 +14,8 @@
 }
 
 String LayoutSize::ToString() const {
-  return String::Format("%sx%s", Width().ToString().Ascii().data(),
-                        Height().ToString().Ascii().data());
+  return String::Format("%sx%s", Width().ToString().Ascii().c_str(),
+                        Height().ToString().Ascii().c_str());
 }
 
 WTF::TextStream& operator<<(WTF::TextStream& ts, const LayoutSize& size) {
diff --git a/third_party/blink/renderer/platform/geometry/layout_unit.h b/third_party/blink/renderer/platform/geometry/layout_unit.h
index f073986f..b6dbc76 100644
--- a/third_party/blink/renderer/platform/geometry/layout_unit.h
+++ b/third_party/blink/renderer/platform/geometry/layout_unit.h
@@ -202,7 +202,9 @@
     return value_ > 0 ? LayoutUnit() : *this;
   }
 
-  bool HasFraction() const { return RawValue() % kFixedPointDenominator; }
+  constexpr bool HasFraction() const {
+    return RawValue() % kFixedPointDenominator;
+  }
 
   LayoutUnit Fraction() const {
     // Compute fraction using the mod operator to preserve the sign of the value
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
index f00e019a..964506b 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
@@ -606,8 +606,6 @@
 }
 
 void Canvas2DLayerBridge::DidDraw(const FloatRect& rect) {
-  if (snapshot_state_ == kDidAcquireSnapshot)
-    snapshot_state_ = kDrawnToAfterSnapshot;
   if (!is_deferral_enabled_)
     return;
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
index 74d3d31f..8d05f0b 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
@@ -131,9 +131,6 @@
   bool HasRecordedDrawCommands() { return have_recorded_draw_commands_; }
 
   scoped_refptr<StaticBitmapImage> NewImageSnapshot(AccelerationHint);
-  bool WasDrawnToAfterSnapshot() const {
-    return snapshot_state_ == kDrawnToAfterSnapshot;
-  }
 
   // The values of the enum entries must not change because they are used for
   // usage metrics histograms. New values can be added to the end.
@@ -205,7 +202,6 @@
   enum SnapshotState {
     kInitialSnapshotState,
     kDidAcquireSnapshot,
-    kDrawnToAfterSnapshot,
   };
   mutable SnapshotState snapshot_state_;
 
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_color_filter.cc b/third_party/blink/renderer/platform/graphics/dark_mode_color_filter.cc
new file mode 100644
index 0000000..fd4d84f
--- /dev/null
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_color_filter.cc
@@ -0,0 +1,103 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/dark_mode_color_filter.h"
+
+#include "base/logging.h"
+#include "third_party/blink/renderer/platform/graphics/lab_color_space.h"
+#include "third_party/skia/include/core/SkColorFilter.h"
+#include "third_party/skia/include/effects/SkHighContrastFilter.h"
+#include "third_party/skia/include/effects/SkTableColorFilter.h"
+
+namespace blink {
+namespace {
+
+sk_sp<SkColorFilter> SkColorFilterFromSettings(
+    SkHighContrastConfig::InvertStyle invert_style,
+    const DarkModeSettings& settings) {
+  SkHighContrastConfig config;
+  config.fInvertStyle = invert_style;
+  config.fGrayscale = settings.grayscale;
+  config.fContrast = settings.contrast;
+  return SkHighContrastFilter::Make(config);
+}
+
+class SkColorFilterWrapper : public DarkModeColorFilter {
+ public:
+  SkColorFilterWrapper(sk_sp<SkColorFilter> filter) : filter_(filter) {}
+
+  Color InvertColor(const Color& color) const override {
+    return Color(filter_->filterColor(color.Rgb()));
+  }
+
+  sk_sp<SkColorFilter> ToSkColorFilter() const override { return filter_; }
+
+ private:
+  sk_sp<SkColorFilter> filter_;
+};
+
+class LabColorFilter : public DarkModeColorFilter {
+ public:
+  LabColorFilter() : transformer_(LabColorSpace::RGBLABTransformer()) {
+    SkHighContrastConfig config;
+    config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
+    config.fGrayscale = false;
+    config.fContrast = 0.0;
+    filter_ = SkHighContrastFilter::Make(config);
+  }
+
+  Color InvertColor(const Color& color) const override {
+    blink::FloatPoint3D rgb = {color.Red() / 255.0f, color.Green() / 255.0f,
+                               color.Blue() / 255.0f};
+    blink::FloatPoint3D lab = transformer_.sRGBToLab(rgb);
+    float invertedL = std::min(110.0f - lab.X(), 100.0f);
+    lab.SetX(invertedL);
+    rgb = transformer_.LabToSRGB(lab);
+
+    return Color(static_cast<unsigned int>(rgb.X() * 255 + 0.5),
+                 static_cast<unsigned int>(rgb.Y() * 255 + 0.5),
+                 static_cast<unsigned int>(rgb.Z() * 255 + 0.5), color.Alpha());
+  }
+
+  sk_sp<SkColorFilter> ToSkColorFilter() const override { return filter_; }
+
+ private:
+  const LabColorSpace::RGBLABTransformer transformer_;
+  sk_sp<SkColorFilter> filter_;
+};
+
+}  // namespace
+
+std::unique_ptr<DarkModeColorFilter> DarkModeColorFilter::FromSettings(
+    const DarkModeSettings& settings) {
+  switch (settings.mode) {
+    case DarkMode::kOff:
+      return nullptr;
+
+    case DarkMode::kSimpleInvertForTesting:
+      uint8_t identity[256], invert[256];
+      for (int i = 0; i < 256; ++i) {
+        identity[i] = i;
+        invert[i] = 255 - i;
+      }
+      return std::make_unique<SkColorFilterWrapper>(
+          SkTableColorFilter::MakeARGB(identity, invert, invert, invert));
+
+    case DarkMode::kInvertBrightness:
+      return std::make_unique<SkColorFilterWrapper>(SkColorFilterFromSettings(
+          SkHighContrastConfig::InvertStyle::kInvertBrightness, settings));
+
+    case DarkMode::kInvertLightness:
+      return std::make_unique<SkColorFilterWrapper>(SkColorFilterFromSettings(
+          SkHighContrastConfig::InvertStyle::kInvertLightness, settings));
+
+    case DarkMode::kInvertLightnessLAB:
+      return std::make_unique<LabColorFilter>();
+  }
+  NOTREACHED();
+}
+
+DarkModeColorFilter::~DarkModeColorFilter() {}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_color_filter.h b/third_party/blink/renderer/platform/graphics/dark_mode_color_filter.h
new file mode 100644
index 0000000..ed7c578
--- /dev/null
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_color_filter.h
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_COLOR_FILTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_COLOR_FILTER_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/dark_mode_settings.h"
+#include "third_party/blink/renderer/platform/graphics/lab_color_space.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkColorFilter;
+
+namespace blink {
+
+// Contains logic specific to modifying colors drawn when dark mode is active.
+class PLATFORM_EXPORT DarkModeColorFilter {
+ public:
+  static std::unique_ptr<DarkModeColorFilter> FromSettings(
+      const DarkModeSettings& settings);
+
+  virtual ~DarkModeColorFilter();
+  virtual Color InvertColor(const Color& color) const = 0;
+  virtual sk_sp<SkColorFilter> ToSkColorFilter() const = 0;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_COLOR_FILTER_H_
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc b/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
index 35c3366..f621340 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
@@ -4,16 +4,40 @@
 
 #include "third_party/blink/renderer/platform/graphics/dark_mode_filter.h"
 
+#include <cmath>
+
+#include "base/logging.h"
 #include "base/optional.h"
-#include "third_party/blink/renderer/platform/graphics/dark_mode_settings.h"
+#include "third_party/blink/renderer/platform/graphics/dark_mode_color_filter.h"
 #include "third_party/skia/include/core/SkColorFilter.h"
 #include "third_party/skia/include/effects/SkColorMatrix.h"
-#include "third_party/skia/include/effects/SkHighContrastFilter.h"
-#include "third_party/skia/include/effects/SkTableColorFilter.h"
 
 namespace blink {
 namespace {
 
+#if DCHECK_IS_ON()
+
+// Floats that differ by this amount or less are considered to be equal.
+const float kFloatEqualityEpsilon = 0.0001;
+
+bool AreFloatsEqual(float a, float b) {
+  return std::fabs(a - b) <= kFloatEqualityEpsilon;
+}
+
+void VerifySettingsAreUnchanged(const DarkModeSettings& a,
+                                const DarkModeSettings& b) {
+  if (a.mode == DarkMode::kOff)
+    return;
+
+  DCHECK_EQ(a.image_policy, b.image_policy);
+  DCHECK_EQ(a.text_policy, b.text_policy);
+  DCHECK_EQ(a.grayscale, b.grayscale);
+  DCHECK(AreFloatsEqual(a.contrast, b.contrast));
+  DCHECK(AreFloatsEqual(a.image_grayscale_percent, b.image_grayscale_percent));
+}
+
+#endif  // DCHECK_IS_ON()
+
 bool ShouldApplyToImage(const DarkModeSettings& settings,
                         const FloatRect& src_rect,
                         Image* image) {
@@ -27,7 +51,8 @@
   }
 }
 
-// |grayscale_percent| should be between 0.0 and 1.0.
+// TODO(gilmanmh): If grayscaling images in dark mode proves popular among
+// users, consider experimenting with different grayscale algorithms.
 sk_sp<SkColorFilter> MakeGrayscaleFilter(float grayscale_percent) {
   DCHECK_GE(grayscale_percent, 0.0f);
   DCHECK_LE(grayscale_percent, 1.0f);
@@ -40,62 +65,43 @@
 }  // namespace
 
 DarkModeFilter::DarkModeFilter()
-    : default_filter_(nullptr), image_filter_(nullptr) {
-  settings_.mode = DarkMode::kOff;
-  settings_.image_policy = DarkModeImagePolicy::kFilterNone;
+    : color_filter_(nullptr), image_filter_(nullptr) {
+  DarkModeSettings default_settings;
+  default_settings.mode = DarkMode::kOff;
+  UpdateSettings(default_settings);
 }
 
-void DarkModeFilter::UpdateSettings(const DarkModeSettings& new_settings) {
-  settings_ = new_settings;
+DarkModeFilter::~DarkModeFilter() {}
 
-  SkHighContrastConfig config;
-  transformer_ = base::nullopt;
-  switch (settings_.mode) {
-    case DarkMode::kOff:
-      default_filter_.reset(nullptr);
-      image_filter_.reset(nullptr);
-      return;
-    case DarkMode::kSimpleInvertForTesting: {
-      uint8_t identity[256], invert[256];
-      for (int i = 0; i < 256; ++i) {
-        identity[i] = i;
-        invert[i] = 255 - i;
-      }
-      default_filter_ =
-          SkTableColorFilter::MakeARGB(identity, invert, invert, invert);
-      image_filter_.reset(nullptr);
-      return;
-    }
-    case DarkMode::kInvertBrightness:
-      config.fInvertStyle =
-          SkHighContrastConfig::InvertStyle::kInvertBrightness;
-      break;
-    case DarkMode::kInvertLightness:
-      config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
-      break;
-    case DarkMode::kInvertLightnessLAB:
-      transformer_ = LabColorSpace::RGBLABTransformer();
-      config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
-      break;
+void DarkModeFilter::UpdateSettings(const DarkModeSettings& new_settings) {
+  // Dark mode can be activated or deactivated on a per-page basis, depending on
+  // whether the original page theme is already dark. However, there is
+  // currently no mechanism to change the other settings after starting Chrome.
+  // As such, if the mode doesn't change, we don't need to do anything.
+  if (settings_.mode == new_settings.mode) {
+#if DCHECK_IS_ON()
+    VerifySettingsAreUnchanged(settings_, new_settings);
+#endif
+    return;
   }
 
-  config.fGrayscale = settings_.grayscale;
-  config.fContrast = settings_.contrast;
-  default_filter_ = SkHighContrastFilter::Make(config);
+  settings_ = new_settings;
+  color_filter_ = DarkModeColorFilter::FromSettings(settings_);
+  if (!color_filter_) {
+    image_filter_ = nullptr;
+    return;
+  }
 
   if (settings_.image_grayscale_percent > 0.0f)
     image_filter_ = MakeGrayscaleFilter(settings_.image_grayscale_percent);
   else
-    image_filter_.reset(nullptr);
+    image_filter_ = color_filter_->ToSkColorFilter();
 }
 
-Color DarkModeFilter::ApplyIfNeeded(const Color& color) {
-  if (!default_filter_)
+Color DarkModeFilter::InvertColorIfNeeded(const Color& color) {
+  if (!IsDarkModeActive())
     return color;
-  if (!transformer_)
-    return Color(default_filter_->filterColor(color.Rgb()));
-
-  return InvertColor(color);
+  return color_filter_->InvertColor(color);
 }
 
 // TODO(gilmanmh): Investigate making |image| a const reference. This code
@@ -104,47 +110,33 @@
 void DarkModeFilter::ApplyToImageFlagsIfNeeded(const FloatRect& src_rect,
                                                Image* image,
                                                cc::PaintFlags* flags) {
-  sk_sp<SkColorFilter> filter = image_filter_;
-  if (!filter)
-    filter = default_filter_;
-
-  if (!filter || !ShouldApplyToImage(settings(), src_rect, image))
+  if (!image_filter_ || !ShouldApplyToImage(settings(), src_rect, image))
     return;
-  flags->setColorFilter(std::move(filter));
+  flags->setColorFilter(image_filter_);
 }
 
 base::Optional<cc::PaintFlags> DarkModeFilter::ApplyToFlagsIfNeeded(
     const cc::PaintFlags& flags) {
-  if (!default_filter_)
+  if (!IsDarkModeActive())
     return base::nullopt;
 
   cc::PaintFlags dark_mode_flags = flags;
   if (flags.HasShader()) {
-    dark_mode_flags.setColorFilter(default_filter_);
+    dark_mode_flags.setColorFilter(color_filter_->ToSkColorFilter());
   } else {
-    auto invertedColor = ApplyIfNeeded(flags.getColor());
+    Color inverted_color = color_filter_->InvertColor(flags.getColor());
     dark_mode_flags.setColor(
-        SkColorSetARGB(invertedColor.Alpha(), invertedColor.Red(),
-                       invertedColor.Green(), invertedColor.Blue()));
+        SkColorSetARGB(inverted_color.Alpha(), inverted_color.Red(),
+                       inverted_color.Green(), inverted_color.Blue()));
   }
 
   return base::make_optional<cc::PaintFlags>(std::move(dark_mode_flags));
 }
 
-Color DarkModeFilter::InvertColor(const Color& color) const {
-  blink::FloatPoint3D rgb = {color.Red() / 255.0f, color.Green() / 255.0f,
-                             color.Blue() / 255.0f};
-  blink::FloatPoint3D lab = transformer_->sRGBToLab(rgb);
-  float invertedL = std::min(110.0f - lab.X(), 100.0f);
-  lab.SetX(invertedL);
-  rgb = transformer_->LabToSRGB(lab);
-
-  return Color(static_cast<unsigned int>(rgb.X() * 255 + 0.5),
-               static_cast<unsigned int>(rgb.Y() * 255 + 0.5),
-               static_cast<unsigned int>(rgb.Z() * 255 + 0.5), color.Alpha());
-}
-
 bool DarkModeFilter::ShouldInvertTextColor(const Color& color) const {
+  if (!IsDarkModeActive())
+    return false;
+
   if (settings_.text_policy == DarkModeTextPolicy::kInvertAll)
     return true;
 
@@ -157,4 +149,8 @@
   return true;
 }
 
+bool DarkModeFilter::IsDarkModeActive() const {
+  return !!color_filter_;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_filter.h b/third_party/blink/renderer/platform/graphics/dark_mode_filter.h
index 8c56a26f..2c6700d8 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_filter.h
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_filter.h
@@ -5,46 +5,51 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_FILTER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_FILTER_H_
 
+#include <memory>
+
 #include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/graphics/dark_mode_settings.h"
 #include "third_party/blink/renderer/platform/graphics/image.h"
-#include "third_party/blink/renderer/platform/graphics/lab_color_space.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 
 class SkColorFilter;
 
 namespace blink {
 
-class DarkModeFilter {
+class DarkModeColorFilter;
+
+class PLATFORM_EXPORT DarkModeFilter {
  public:
+  // Dark mode is disabled by default. Enable it by calling UpdateSettings()
+  // with a mode other than DarkMode::kOff.
   DarkModeFilter();
+  ~DarkModeFilter();
 
   const DarkModeSettings& settings() const { return settings_; }
   void UpdateSettings(const DarkModeSettings& new_settings);
 
-  Color ApplyIfNeeded(const Color& color);
+  Color InvertColorIfNeeded(const Color& color);
 
   // |image| and |flags| must not be null.
   void ApplyToImageFlagsIfNeeded(const FloatRect& src_rect,
                                  Image* image,
                                  cc::PaintFlags* flags);
 
-  // |flags| must not be null.
   base::Optional<cc::PaintFlags> ApplyToFlagsIfNeeded(
       const cc::PaintFlags& flags);
 
-  Color InvertColor(const Color& color) const;
-
   bool ShouldInvertTextColor(const Color& color) const;
 
+  bool IsDarkModeActive() const;
+
  private:
   DarkModeSettings settings_;
 
-  sk_sp<SkColorFilter> default_filter_;
+  std::unique_ptr<DarkModeColorFilter> color_filter_;
   sk_sp<SkColorFilter> image_filter_;
-  base::Optional<LabColorSpace::RGBLABTransformer> transformer_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_filter_test.cc b/third_party/blink/renderer/platform/graphics/dark_mode_filter_test.cc
new file mode 100644
index 0000000..f0dad9e
--- /dev/null
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_filter_test.cc
@@ -0,0 +1,55 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/dark_mode_filter.h"
+
+#include "base/optional.h"
+#include "cc/paint/paint_flags.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/dark_mode_settings.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace blink {
+namespace {
+
+// TODO(gilmanmh): Add expectations for image inversion to each test.
+TEST(DarkModeFilterTest, DoNotApplyFilterWhenDarkModeIsOff) {
+  DarkModeFilter filter;
+
+  DarkModeSettings settings;
+  settings.mode = DarkMode::kOff;
+  filter.UpdateSettings(settings);
+
+  EXPECT_EQ(Color::kWhite, filter.InvertColorIfNeeded(Color::kWhite));
+  EXPECT_EQ(Color::kBlack, filter.InvertColorIfNeeded(Color::kBlack));
+
+  EXPECT_EQ(base::nullopt, filter.ApplyToFlagsIfNeeded(cc::PaintFlags()));
+
+  EXPECT_FALSE(filter.ShouldInvertTextColor(Color::kWhite));
+  EXPECT_FALSE(filter.ShouldInvertTextColor(Color::kBlack));
+}
+
+TEST(DarkModeFilterTest, ApplyDarkModeToColorsFlagsAndText) {
+  DarkModeFilter filter;
+
+  DarkModeSettings settings;
+  settings.mode = DarkMode::kSimpleInvertForTesting;
+  filter.UpdateSettings(settings);
+
+  EXPECT_EQ(Color::kBlack, filter.InvertColorIfNeeded(Color::kWhite));
+  EXPECT_EQ(Color::kWhite, filter.InvertColorIfNeeded(Color::kBlack));
+
+  cc::PaintFlags flags;
+  flags.setColor(SK_ColorWHITE);
+  auto flags_or_nullopt = filter.ApplyToFlagsIfNeeded(flags);
+  ASSERT_NE(flags_or_nullopt, base::nullopt);
+  EXPECT_EQ(SK_ColorBLACK, flags_or_nullopt.value().getColor());
+
+  EXPECT_TRUE(filter.ShouldInvertTextColor(Color::kWhite));
+  EXPECT_TRUE(filter.ShouldInvertTextColor(Color::kBlack));
+}
+
+}  // namespace
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.cc b/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.cc
index 760ffa1..6a606dd 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.cc
@@ -63,7 +63,7 @@
     return true;
 
   if (requestable_extensions_.Contains(name)) {
-    gl_->RequestExtensionCHROMIUM(name.Ascii().data());
+    gl_->RequestExtensionCHROMIUM(name.Ascii().c_str());
     enabled_extensions_.clear();
     requestable_extensions_.clear();
     InitializeExtensions();
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index 3e3eaadd..8af7eff 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -315,7 +315,7 @@
   if (ContextDisabled()) {
     // Clients expect endRecording() to always return a non-null paint record.
     // Cache an empty one to minimize overhead when disabled.
-    DEFINE_STATIC_LOCAL(sk_sp<PaintRecord>, empty_paint_record,
+    DEFINE_STATIC_LOCAL(const sk_sp<PaintRecord>, empty_paint_record,
                         (CreateEmptyPaintRecord()));
     return empty_paint_record;
   }
@@ -384,15 +384,15 @@
 void GraphicsContext::DrawFocusRingPath(const SkPath& path,
                                         const Color& color,
                                         float width) {
-  DrawPlatformFocusRing(path, canvas_,
-                        dark_mode_filter_.ApplyIfNeeded(color).Rgb(), width);
+  DrawPlatformFocusRing(
+      path, canvas_, dark_mode_filter_.InvertColorIfNeeded(color).Rgb(), width);
 }
 
 void GraphicsContext::DrawFocusRingRect(const SkRect& rect,
                                         const Color& color,
                                         float width) {
-  DrawPlatformFocusRing(rect, canvas_,
-                        dark_mode_filter_.ApplyIfNeeded(color).Rgb(), width);
+  DrawPlatformFocusRing(
+      rect, canvas_, dark_mode_filter_.InvertColorIfNeeded(color).Rgb(), width);
 }
 
 void GraphicsContext::DrawFocusRing(const Path& focus_ring_path,
@@ -506,7 +506,7 @@
   if (ContextDisabled())
     return;
 
-  Color shadow_color = dark_mode_filter_.ApplyIfNeeded(orig_shadow_color);
+  Color shadow_color = dark_mode_filter_.InvertColorIfNeeded(orig_shadow_color);
 
   FloatRect hole_rect(rect.Rect());
   hole_rect.Inflate(-shadow_spread);
@@ -1159,7 +1159,7 @@
       canvas_->drawDRRect(outer, inner, ImmutableState()->FillFlags());
     } else {
       PaintFlags flags(ImmutableState()->FillFlags());
-      flags.setColor(dark_mode_filter_.ApplyIfNeeded(color).Rgb());
+      flags.setColor(dark_mode_filter_.InvertColorIfNeeded(color).Rgb());
       canvas_->drawDRRect(outer, inner, flags);
     }
 
@@ -1172,7 +1172,7 @@
   stroke_r_rect.inset(stroke_width / 2, stroke_width / 2);
 
   PaintFlags stroke_flags(ImmutableState()->FillFlags());
-  stroke_flags.setColor(dark_mode_filter_.ApplyIfNeeded(color).Rgb());
+  stroke_flags.setColor(dark_mode_filter_.InvertColorIfNeeded(color).Rgb());
   stroke_flags.setStyle(PaintFlags::kStroke_Style);
   stroke_flags.setStrokeWidth(stroke_width);
 
@@ -1367,7 +1367,7 @@
     return;
 
   PaintFlags flags(ImmutableState()->FillFlags());
-  flags.setColor(dark_mode_filter_.ApplyIfNeeded(color).Rgb());
+  flags.setColor(dark_mode_filter_.InvertColorIfNeeded(color).Rgb());
   canvas_->drawDRRect(SkRRect::MakeRect(rect), rounded_hole_rect, flags);
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/graphics_types.h b/third_party/blink/renderer/platform/graphics/graphics_types.h
index bfa5415..97e98ed 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_types.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_types.h
@@ -131,9 +131,7 @@
   kDisableDeferralReasonExpensiveOverdrawHeuristic = 1,
   kDisableDeferralReasonUsingTextureBackedPattern = 2,
   kDisableDeferralReasonDrawImageOfVideo = 3,
-  kDisableDeferralReasonDrawImageOfAnimated2dCanvas = 4,
   kDisableDeferralReasonSubPixelTextAntiAliasingSupport = 5,
-  kDisableDeferralDrawImageWithTextureBackedSourceImage = 6,
   kDisableDeferralReasonLowEndDevice = 7,
   kDisableDeferralReasonCount,
 };
diff --git a/third_party/blink/renderer/platform/graphics/paint/property_tree_state.cc b/third_party/blink/renderer/platform/graphics/paint/property_tree_state.cc
index 4958b2d..3be0d74 100644
--- a/third_party/blink/renderer/platform/graphics/paint/property_tree_state.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/property_tree_state.cc
@@ -9,23 +9,23 @@
 namespace blink {
 
 const PropertyTreeState& PropertyTreeState::Uninitialized() {
-  DEFINE_STATIC_REF(TransformPaintPropertyNode, transform,
+  DEFINE_STATIC_REF(const TransformPaintPropertyNode, transform,
                     TransformPaintPropertyNode::Create(
                         TransformPaintPropertyNode::Root(), {}));
-  DEFINE_STATIC_REF(ClipPaintPropertyNode, clip,
+  DEFINE_STATIC_REF(const ClipPaintPropertyNode, clip,
                     ClipPaintPropertyNode::Create(ClipPaintPropertyNode::Root(),
                                                   {transform}));
-  DEFINE_STATIC_REF(EffectPaintPropertyNode, effect,
+  DEFINE_STATIC_REF(const EffectPaintPropertyNode, effect,
                     EffectPaintPropertyNode::Create(
                         EffectPaintPropertyNode::Root(), {transform}));
-  DEFINE_STATIC_LOCAL(PropertyTreeState, uninitialized,
+  DEFINE_STATIC_LOCAL(const PropertyTreeState, uninitialized,
                       (*transform, *clip, *effect));
   return uninitialized;
 }
 
 const PropertyTreeState& PropertyTreeState::Root() {
   DEFINE_STATIC_LOCAL(
-      PropertyTreeState, root,
+      const PropertyTreeState, root,
       (TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
        EffectPaintPropertyNode::Root()));
   return root;
diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.cc b/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.cc
index 53a3506..94a82ce 100644
--- a/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.cc
+++ b/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.cc
@@ -14,7 +14,7 @@
 
 namespace blink {
 
-scoped_refptr<SharedBuffer> ReadFile(const char* file_name) {
+scoped_refptr<SharedBuffer> ReadFile(StringView file_name) {
   StringBuilder file_path;
   file_path.Append(test::BlinkWebTestsDir());
   file_path.Append(file_name);
diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h b/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h
index 51759b9..c231462 100644
--- a/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h
+++ b/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
 #include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 class SkBitmap;
@@ -27,7 +28,7 @@
     buffer[i] = static_cast<char>(i);
 }
 
-scoped_refptr<SharedBuffer> ReadFile(const char* file_name);
+scoped_refptr<SharedBuffer> ReadFile(StringView file_name);
 scoped_refptr<SharedBuffer> ReadFile(const char* dir, const char* file_name);
 unsigned HashBitmap(const SkBitmap&);
 void CreateDecodingBaseline(DecoderCreator,
diff --git a/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc
index 1b5c03b..54210639 100644
--- a/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc
@@ -1257,7 +1257,7 @@
   String path = "/images/resources/png-16bit/";
   for (PNGSample& png_sample : png_samples) {
     String full_path = path + png_sample.filename;
-    png_sample.png_contents = ReadFile(full_path.Ascii().data());
+    png_sample.png_contents = ReadFile(full_path);
     auto decoder = Create16BitPNGDecoder();
     TestHighBitDepthPNGDecoding(png_sample, decoder.get());
   }
@@ -1271,7 +1271,7 @@
   String path = "/images/resources/png-16bit/";
   for (PNGSample& png_sample : png_samples) {
     String full_path = path + png_sample.filename;
-    png_sample.png_contents = ReadFile(full_path.Ascii().data());
+    png_sample.png_contents = ReadFile(full_path);
     ASSERT_TRUE(png_sample.png_contents.get());
 
     std::unique_ptr<ImageDecoder> decoders[] = {CreatePNGDecoder(),
diff --git a/third_party/blink/renderer/platform/loader/cors/cors.cc b/third_party/blink/renderer/platform/loader/cors/cors.cc
index 4dbddf8..e56b1691 100644
--- a/third_party/blink/renderer/platform/loader/cors/cors.cc
+++ b/third_party/blink/renderer/platform/loader/cors/cors.cc
@@ -49,7 +49,7 @@
   if (result.IsNull())
     return base::nullopt;
 
-  return std::string(result.Ascii().data());
+  return result.Ascii();
 }
 
 std::unique_ptr<net::HttpRequestHeaders> CreateNetHttpRequestHeaders(
@@ -61,8 +61,7 @@
        i != end; ++i) {
     DCHECK(!i->key.IsNull());
     DCHECK(!i->value.IsNull());
-    request_headers->SetHeader(std::string(i->key.Ascii().data()),
-                               std::string(i->value.Ascii().data()));
+    request_headers->SetHeader(i->key.Ascii(), i->value.Ascii());
   }
   return request_headers;
 }
@@ -107,9 +106,7 @@
         return;
       }
 
-      const CString& name = value_.Substring(token_start, token_size).Ascii();
-      output.emplace(name.data(), name.length());
-
+      output.insert(value_.Substring(token_start, token_size).Ascii());
       ConsumeSpaces();
 
       if (pos_ == value_.length())
@@ -243,8 +240,7 @@
     return network::CorsErrorStatus(*error);
 
   base::Optional<network::CorsErrorStatus> status;
-  status = result->EnsureAllowedCrossOriginMethod(
-      std::string(request_method.Ascii().data()));
+  status = result->EnsureAllowedCrossOriginMethod(request_method.Ascii());
   if (status)
     return status;
 
@@ -255,8 +251,8 @@
   if (status)
     return status;
 
-  GetPerThreadPreflightCache().AppendEntry(std::string(origin.Ascii().data()),
-                                           request_url, std::move(result));
+  GetPerThreadPreflightCache().AppendEntry(origin.Ascii(), request_url,
+                                           std::move(result));
   return base::nullopt;
 }
 
@@ -272,8 +268,7 @@
   // |is_revalidating| is not needed for blink-side CORS.
   constexpr bool is_revalidating = false;
   return GetPerThreadPreflightCache().CheckIfRequestCanSkipPreflight(
-      std::string(origin.Ascii().data()), url, credentials_mode,
-      std::string(method.Ascii().data()),
+      origin.Ascii(), url, credentials_mode, method.Ascii(),
       *CreateNetHttpRequestHeaders(request_header_map), is_revalidating);
 }
 
@@ -412,7 +407,7 @@
   if (response.WasFetchedViaServiceWorker()) {
     WebHTTPHeaderSet header_set;
     for (const auto& header : response.CorsExposedHeaderNames())
-      header_set.emplace(header.Ascii().data(), header.Ascii().length());
+      header_set.insert(header.Ascii());
     return header_set;
   }
 
@@ -424,10 +419,8 @@
   if (credentials_mode != network::mojom::FetchCredentialsMode::kInclude &&
       header_set.find("*") != header_set.end()) {
     header_set.clear();
-    for (const auto& header : response.HttpHeaderFields()) {
-      CString name = header.key.Ascii();
-      header_set.emplace(name.data(), name.length());
-    }
+    for (const auto& header : response.HttpHeaderFields())
+      header_set.insert(header.key.Ascii());
   }
   return header_set;
 }
@@ -446,7 +439,7 @@
                                       "last-modified",
                                       "pragma",
                                   }));
-  return allowed_cross_origin_response_headers.find(name.Ascii().data()) !=
+  return allowed_cross_origin_response_headers.find(name.Ascii()) !=
          allowed_cross_origin_response_headers.end();
 }
 
diff --git a/third_party/blink/renderer/platform/loader/link_header_test.cc b/third_party/blink/renderer/platform/loader/link_header_test.cc
index d7583f9c..3fc22e0 100644
--- a/third_party/blink/renderer/platform/loader/link_header_test.cc
+++ b/third_party/blink/renderer/platform/loader/link_header_test.cc
@@ -166,10 +166,10 @@
   LinkHeader& header = header_set[0];
   EXPECT_EQ(test_case.valid, header.Valid());
   if (test_case.valid) {
-    EXPECT_STREQ(test_case.url, header.Url().Ascii().data());
-    EXPECT_STREQ(test_case.rel, header.Rel().Ascii().data());
-    EXPECT_STREQ(test_case.as, header.As().Ascii().data());
-    EXPECT_STREQ(test_case.media, header.Media().Ascii().data());
+    EXPECT_EQ(test_case.url, header.Url().Ascii());
+    EXPECT_EQ(test_case.rel, header.Rel().Ascii());
+    EXPECT_EQ(test_case.as, header.As().Ascii());
+    EXPECT_EQ(test_case.media, header.Media().Ascii());
   }
 }
 
@@ -270,13 +270,13 @@
   LinkHeaderSet header_set(test_case.header_value);
   ASSERT_GE(header_set.size(), 1u);
   LinkHeader& header = header_set[0];
-  EXPECT_STREQ(test_case.url, header.Url().Ascii().data());
-  EXPECT_STREQ(test_case.rel, header.Rel().Ascii().data());
+  EXPECT_EQ(test_case.url, header.Url().Ascii());
+  EXPECT_EQ(test_case.rel, header.Rel().Ascii());
   EXPECT_EQ(test_case.valid, header.Valid());
   if (!test_case.crossorigin)
     EXPECT_TRUE(header.CrossOrigin().IsNull());
   else
-    EXPECT_STREQ(test_case.crossorigin, header.CrossOrigin().Ascii().data());
+    EXPECT_EQ(test_case.crossorigin, header.CrossOrigin().Ascii());
 }
 
 INSTANTIATE_TEST_SUITE_P(LinkHeaderTest,
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 27bdf80..fa4ff32 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -600,6 +600,10 @@
       status: "experimental"
     },
     {
+      name: "FileHandling",
+      depends_on: ["NativeFileSystem"]
+    },
+    {
       name: "FileSystem",
       status: "stable",
     },
@@ -1131,7 +1135,7 @@
     },
     {
       name: "PaymentRetry",
-      status: "stable",
+      status: "experimental",
     },
     {
       name: "PerformanceManagerInstrumentation",
@@ -1429,10 +1433,6 @@
       name: "SmsReceiver",
       status: "experimental",
     },
-    {
-      name: "SpeechSynthesisEventCharLength",
-      status: "experimental",
-    },
     // Used as argument in attribute of stable-release functions/interfaces
     // where a runtime-enabled feature name is required for correct IDL syntax.
     // This is a global flag; do not change its status.
diff --git a/third_party/blink/renderer/platform/scheduler/common/metrics_helper.cc b/third_party/blink/renderer/platform/scheduler/common/metrics_helper.cc
index 41925e2..3f38fc8a 100644
--- a/third_party/blink/renderer/platform/scheduler/common/metrics_helper.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/metrics_helper.cc
@@ -38,6 +38,7 @@
     case WebThreadType::kSharedWorkerThread:
     case WebThreadType::kUnspecifiedWorkerThread:
     case WebThreadType::kTestThread:
+    case WebThreadType::kAudioEncoderThread:
       return scheduling_metrics::ThreadType::kRendererOtherBlinkThread;
     case WebThreadType::kCount:
       NOTREACHED();
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_unittest.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_unittest.cc
index a0db48e..08c07353 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_unittest.cc
@@ -16,6 +16,15 @@
 #include "third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 
+// TODO(crbug.com/960984): Fix memory leaks in tests and re-enable on LSAN.
+#ifdef LEAK_SANITIZER
+#define MAYBE_PausableTasks DISABLED_PausableTasks
+#define MAYBE_NestedPauseHandlesTasks DISABLED_NestedPauseHandlesTasks
+#else
+#define MAYBE_PausableTasks PausableTasks
+#define MAYBE_NestedPauseHandlesTasks NestedPauseHandlesTasks
+#endif
+
 using testing::ElementsAre;
 using testing::ElementsAreArray;
 
@@ -272,7 +281,7 @@
                          base::TimeTicks() + base::TimeDelta::FromSeconds(41)));
 }
 
-TEST_F(WorkerSchedulerTest, PausableTasks) {
+TEST_F(WorkerSchedulerTest, MAYBE_PausableTasks) {
   std::vector<std::string> run_order;
   auto pause_handle = worker_scheduler_->Pause();
   // Tests interlacing pausable, throttable and unpausable tasks and
@@ -291,7 +300,7 @@
   EXPECT_THAT(run_order, testing::ElementsAre("T3", "T1", "T2"));
 }
 
-TEST_F(WorkerSchedulerTest, NestedPauseHandlesTasks) {
+TEST_F(WorkerSchedulerTest, MAYBE_NestedPauseHandlesTasks) {
   std::vector<std::string> run_order;
   auto pause_handle = worker_scheduler_->Pause();
   {
diff --git a/third_party/blink/renderer/platform/testing/picture_matchers.cc b/third_party/blink/renderer/platform/testing/picture_matchers.cc
index d38c327..35127b0 100644
--- a/third_party/blink/renderer/platform/testing/picture_matchers.cc
+++ b/third_party/blink/renderer/platform/testing/picture_matchers.cc
@@ -107,8 +107,7 @@
         if (listener->IsInterested()) {
           *listener << "at index " << index << " which draws "
                     << actual_rect_with_color.rect << " with color "
-                    << actual_rect_with_color.color.Serialized().Ascii().data()
-                    << "\n";
+                    << actual_rect_with_color.color.Serialized() << "\n";
         }
         return false;
       }
@@ -122,8 +121,7 @@
     for (unsigned index = 0; index < rects_with_color_.size(); index++) {
       const auto& rect_with_color = rects_with_color_[index];
       *os << "at index " << index << " rect draws " << rect_with_color.rect
-          << " with color " << rect_with_color.color.Serialized().Ascii().data()
-          << "\n";
+          << " with color " << rect_with_color.color.Serialized() << "\n";
     }
   }
 
diff --git a/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.cc b/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.cc
index 3e708886..5ec2fd4a 100644
--- a/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.cc
+++ b/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.cc
@@ -263,7 +263,7 @@
 
   for (const auto& key_value_pair : protocol_to_response_info_) {
     String protocol = key_value_pair.key;
-    if (url.ProtocolIs(protocol.Ascii().data())) {
+    if (url.ProtocolIs(protocol.Ascii().c_str())) {
       *response_info = key_value_pair.value;
       return true;
     }
diff --git a/third_party/blink/renderer/platform/text/date_time_format_test.cc b/third_party/blink/renderer/platform/text/date_time_format_test.cc
index 2cb06529..f2b12ed 100644
--- a/third_party/blink/renderer/platform/text/date_time_format_test.cc
+++ b/third_party/blink/renderer/platform/text/date_time_format_test.cc
@@ -192,7 +192,7 @@
 
 std::ostream& operator<<(std::ostream& os,
                          const DateTimeFormatTest::Tokens& tokens) {
-  return os << tokens.ToString().Ascii().data();
+  return os << tokens.ToString();
 }
 
 TEST_F(DateTimeFormatTest, CommonPattern) {
diff --git a/third_party/blink/renderer/platform/text/hyphenation_test.cc b/third_party/blink/renderer/platform/text/hyphenation_test.cc
index ea3e269..6494c3b 100644
--- a/third_party/blink/renderer/platform/text/hyphenation_test.cc
+++ b/third_party/blink/renderer/platform/text/hyphenation_test.cc
@@ -41,8 +41,7 @@
     // Because the mojo service to open hyphenation dictionaries is not
     // accessible from the unit test, open the dictionary file directly for
     // testing.
-    std::string filename =
-        std::string("hyph-") + locale.Ascii().data() + ".hyb";
+    std::string filename = "hyph-" + locale.Ascii() + ".hyb";
 #if defined(OS_ANDROID)
     base::FilePath path("/system/usr/hyphen-data");
 #else
diff --git a/third_party/blink/renderer/platform/text/layout_locale.cc b/third_party/blink/renderer/platform/text/layout_locale.cc
index 6106d2a..8e705279 100644
--- a/third_party/blink/renderer/platform/text/layout_locale.cc
+++ b/third_party/blink/renderer/platform/text/layout_locale.cc
@@ -62,12 +62,14 @@
 }
 
 const char* LayoutLocale::LocaleForSkFontMgr() const {
-  if (string_for_sk_font_mgr_.IsNull()) {
-    string_for_sk_font_mgr_ = ToSkFontMgrLocale(script_);
-    if (string_for_sk_font_mgr_.IsNull())
+  if (string_for_sk_font_mgr_.empty()) {
+    const char* sk_font_mgr_locale = ToSkFontMgrLocale(script_);
+    string_for_sk_font_mgr_ =
+        sk_font_mgr_locale ? sk_font_mgr_locale : std::string();
+    if (string_for_sk_font_mgr_.empty())
       string_for_sk_font_mgr_ = string_.Ascii();
   }
-  return string_for_sk_font_mgr_.data();
+  return string_for_sk_font_mgr_.c_str();
 }
 
 void LayoutLocale::ComputeScriptForHan() const {
diff --git a/third_party/blink/renderer/platform/text/layout_locale.h b/third_party/blink/renderer/platform/text/layout_locale.h
index f10fda7..676c2e9 100644
--- a/third_party/blink/renderer/platform/text/layout_locale.h
+++ b/third_party/blink/renderer/platform/text/layout_locale.h
@@ -46,7 +46,7 @@
     return locale ? locale->string_ : g_null_atom;
   }
   operator const AtomicString&() const { return string_; }
-  CString Ascii() const { return string_.Ascii(); }
+  std::string Ascii() const { return string_.Ascii(); }
 
   const hb_language_impl_t* HarfbuzzLanguage() const {
     return harfbuzz_language_;
@@ -78,7 +78,7 @@
   void ComputeScriptForHan() const;
 
   AtomicString string_;
-  mutable CString string_for_sk_font_mgr_;
+  mutable std::string string_for_sk_font_mgr_;
   mutable scoped_refptr<Hyphenation> hyphenation_;
 
   // hb_language_t is defined in hb.h, which not all files can include.
diff --git a/third_party/blink/renderer/platform/text/layout_locale_test.cc b/third_party/blink/renderer/platform/text/layout_locale_test.cc
index 5421e9f..c6d549a 100644
--- a/third_party/blink/renderer/platform/text/layout_locale_test.cc
+++ b/third_party/blink/renderer/platform/text/layout_locale_test.cc
@@ -16,9 +16,9 @@
   EXPECT_EQ(g_empty_atom, LayoutLocale::Get(g_empty_atom)->LocaleString());
 
   EXPECT_STRCASEEQ("en-us",
-                   LayoutLocale::Get("en-us")->LocaleString().Ascii().data());
+                   LayoutLocale::Get("en-us")->LocaleString().Ascii().c_str());
   EXPECT_STRCASEEQ("ja-jp",
-                   LayoutLocale::Get("ja-jp")->LocaleString().Ascii().data());
+                   LayoutLocale::Get("ja-jp")->LocaleString().Ascii().c_str());
 
   LayoutLocale::ClearForTesting();
 }
diff --git a/third_party/blink/renderer/platform/text/text_encoding_detector.cc b/third_party/blink/renderer/platform/text/text_encoding_detector.cc
index 0eeb38d..ea93c32 100644
--- a/third_party/blink/renderer/platform/text/text_encoding_detector.cc
+++ b/third_party/blink/renderer/platform/text/text_encoding_detector.cc
@@ -63,7 +63,7 @@
   int consumed_bytes;
   bool is_reliable;
   Encoding encoding = CompactEncDet::DetectEncoding(
-      data, length, hint_url.GetString().Ascii().data(), nullptr, nullptr,
+      data, length, hint_url.GetString().Ascii().c_str(), nullptr, nullptr,
       EncodingNameAliasToEncoding(hint_encoding_name), language,
       CompactEncDet::WEB_CORPUS,
       false,  // Include 7-bit encodings to detect ISO-2022-JP
diff --git a/third_party/blink/renderer/platform/web_thread_type.cc b/third_party/blink/renderer/platform/web_thread_type.cc
index 0d2a7d68..4a4b21d 100644
--- a/third_party/blink/renderer/platform/web_thread_type.cc
+++ b/third_party/blink/renderer/platform/web_thread_type.cc
@@ -39,6 +39,8 @@
       return "HRTF database loader thread";
     case WebThreadType::kTestThread:
       return "test thread";
+    case WebThreadType::kAudioEncoderThread:
+      return "Audio encoder thread";
     case WebThreadType::kCount:
       NOTREACHED();
       return nullptr;
diff --git a/third_party/blink/renderer/platform/weborigin/origin_access_entry.cc b/third_party/blink/renderer/platform/weborigin/origin_access_entry.cc
index 51eccc4f62..5a040c61 100644
--- a/third_party/blink/renderer/platform/weborigin/origin_access_entry.cc
+++ b/third_party/blink/renderer/platform/weborigin/origin_access_entry.cc
@@ -41,8 +41,8 @@
     const SecurityOrigin& origin,
     network::mojom::CorsDomainMatchMode match_mode,
     network::mojom::CorsOriginAccessMatchPriority priority)
-    : private_(origin.Protocol().Ascii().data(),
-               origin.Domain().Ascii().data(),
+    : private_(origin.Protocol().Ascii(),
+               origin.Domain().Ascii(),
                origin.EffectivePort(),
                match_mode,
                network::mojom::CorsPortMatchMode::kAllowOnlySpecifiedPort,
@@ -68,7 +68,7 @@
 
 network::cors::OriginAccessEntry::MatchResult OriginAccessEntry::MatchesDomain(
     const SecurityOrigin& origin) const {
-  return private_.MatchesDomain(origin.Host().Ascii().data());
+  return private_.MatchesDomain(origin.Host().Ascii());
 }
 
 bool OriginAccessEntry::HostIsIPAddress() const {
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin.cc b/third_party/blink/renderer/platform/weborigin/security_origin.cc
index 354ab35..db34a13 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin.cc
@@ -501,7 +501,7 @@
   // We special-case "[::1]" here because `net::HostStringIsLocalhost` expects a
   // canonicalization that excludes the braces; a simple string comparison is
   // simpler than trying to adjust Blink's canonicalization.
-  return host_ == "[::1]" || net::HostStringIsLocalhost(host_.Ascii().data());
+  return host_ == "[::1]" || net::HostStringIsLocalhost(host_.Ascii());
 }
 
 String SecurityOrigin::ToString() const {
diff --git a/third_party/blink/renderer/platform/wtf/decimal.cc b/third_party/blink/renderer/platform/wtf/decimal.cc
index 0d84a5a2..a349b07 100644
--- a/third_party/blink/renderer/platform/wtf/decimal.cc
+++ b/third_party/blink/renderer/platform/wtf/decimal.cc
@@ -999,12 +999,11 @@
 
 std::ostream& operator<<(std::ostream& ostream, const Decimal& decimal) {
   Decimal::EncodedData data = decimal.Value();
-  return ostream << "encode("
-                 << String::Number(data.Coefficient()).Ascii().data() << ", "
-                 << String::Number(data.Exponent()).Ascii().data() << ", "
+  return ostream << "encode(" << String::Number(data.Coefficient()).Ascii()
+                 << ", " << String::Number(data.Exponent()).Ascii() << ", "
                  << (data.GetSign() == Decimal::kNegative ? "Negative"
                                                           : "Positive")
-                 << ")=" << decimal.ToString().Ascii().data();
+                 << ")=" << decimal.ToString().Ascii();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/wtf/text/atomic_string.h b/third_party/blink/renderer/platform/wtf/text/atomic_string.h
index 1d26e74..1b517531 100644
--- a/third_party/blink/renderer/platform/wtf/text/atomic_string.h
+++ b/third_party/blink/renderer/platform/wtf/text/atomic_string.h
@@ -214,7 +214,7 @@
   static AtomicString FromUTF8(const char*, size_t length);
   static AtomicString FromUTF8(const char*);
 
-  CString Ascii() const { return string_.Ascii(); }
+  std::string Ascii() const { return string_.Ascii(); }
   std::string Latin1() const { return string_.Latin1(); }
   CString Utf8(UTF8ConversionMode mode = kLenientUTF8Conversion) const {
     return string_.Utf8(mode);
diff --git a/third_party/blink/renderer/platform/wtf/text/string_impl.cc b/third_party/blink/renderer/platform/wtf/text/string_impl.cc
index b72f6c5f..0be863a 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_impl.cc
+++ b/third_party/blink/renderer/platform/wtf/text/string_impl.cc
@@ -97,8 +97,7 @@
 
 #if DCHECK_IS_ON()
 std::string StringImpl::AsciiForDebugging() const {
-  CString ascii = String(IsolatedCopy()->Substring(0, 128)).Ascii();
-  return std::string(ascii.data(), ascii.length());
+  return String(IsolatedCopy()->Substring(0, 128)).Ascii();
 }
 #endif
 
diff --git a/third_party/blink/renderer/platform/wtf/text/wtf_string.cc b/third_party/blink/renderer/platform/wtf/text/wtf_string.cc
index c02322c..4add9193 100644
--- a/third_party/blink/renderer/platform/wtf/text/wtf_string.cc
+++ b/third_party/blink/renderer/platform/wtf/text/wtf_string.cc
@@ -403,42 +403,32 @@
     result.push_back(Substring(start_pos));
 }
 
-CString String::Ascii() const {
+std::string String::Ascii() const {
   // Printable ASCII characters 32..127 and the null character are
   // preserved, characters outside of this range are converted to '?'.
 
   unsigned length = this->length();
-  if (!length) {
-    char* character_buffer;
-    return CString::CreateUninitialized(length, character_buffer);
-  }
+  if (!length)
+    return std::string();
 
+  std::string ascii(length, '\0');
   if (this->Is8Bit()) {
     const LChar* characters = this->Characters8();
 
-    char* character_buffer;
-    CString result = CString::CreateUninitialized(length, character_buffer);
-
     for (unsigned i = 0; i < length; ++i) {
       LChar ch = characters[i];
-      character_buffer[i] = ch && (ch < 0x20 || ch > 0x7f) ? '?' : ch;
+      ascii[i] = ch && (ch < 0x20 || ch > 0x7f) ? '?' : ch;
     }
-
-    return result;
+    return ascii;
   }
 
   const UChar* characters = this->Characters16();
-
-  char* character_buffer;
-  CString result = CString::CreateUninitialized(length, character_buffer);
-
   for (unsigned i = 0; i < length; ++i) {
     UChar ch = characters[i];
-    character_buffer[i] =
-        ch && (ch < 0x20 || ch > 0x7f) ? '?' : static_cast<char>(ch);
+    ascii[i] = ch && (ch < 0x20 || ch > 0x7f) ? '?' : static_cast<char>(ch);
   }
 
-  return result;
+  return ascii;
 }
 
 std::string String::Latin1() const {
diff --git a/third_party/blink/renderer/platform/wtf/text/wtf_string.h b/third_party/blink/renderer/platform/wtf/text/wtf_string.h
index 082f0a6..c8207f3c 100644
--- a/third_party/blink/renderer/platform/wtf/text/wtf_string.h
+++ b/third_party/blink/renderer/platform/wtf/text/wtf_string.h
@@ -167,7 +167,7 @@
 
   bool Is8Bit() const { return impl_->Is8Bit(); }
 
-  CString Ascii() const WARN_UNUSED_RESULT;
+  std::string Ascii() const WARN_UNUSED_RESULT;
   std::string Latin1() const WARN_UNUSED_RESULT;
   CString Utf8(UTF8ConversionMode = kLenientUTF8Conversion) const
       WARN_UNUSED_RESULT;
diff --git a/third_party/blink/renderer/platform/wtf/text/wtf_string_test.cc b/third_party/blink/renderer/platform/wtf/text/wtf_string_test.cc
index 22f1db8..d69bb4a 100644
--- a/third_party/blink/renderer/platform/wtf/text/wtf_string_test.cc
+++ b/third_party/blink/renderer/platform/wtf/text/wtf_string_test.cc
@@ -44,19 +44,14 @@
 }
 
 TEST(StringTest, ASCII) {
-  CString output;
-
   // Null String.
-  output = String().Ascii();
-  EXPECT_STREQ("", output.data());
+  EXPECT_EQ("", String().Ascii());
 
   // Empty String.
-  output = g_empty_string.Ascii();
-  EXPECT_STREQ("", output.data());
+  EXPECT_EQ("", g_empty_string.Ascii());
 
   // Regular String.
-  output = String("foobar").Ascii();
-  EXPECT_STREQ("foobar", output.data());
+  EXPECT_EQ("foobar", String("foobar").Ascii());
 }
 
 namespace {
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index de407b5..415b820 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -582,6 +582,16 @@
     },
     {
         'paths': [
+            'third_party/blink/renderer/modules/mediarecorder/',
+        ],
+        'allowed': [
+            'base::data',
+            'media::.+',
+            'libopus::.+',
+        ]
+    },
+    {
+        'paths': [
             'third_party/blink/renderer/modules/mediastream/',
         ],
         'allowed': [
diff --git a/third_party/blink/tools/blinkpy/third_party/README.chromium b/third_party/blink/tools/blinkpy/third_party/README.chromium
index fb6c057df..773092d 100644
--- a/third_party/blink/tools/blinkpy/third_party/README.chromium
+++ b/third_party/blink/tools/blinkpy/third_party/README.chromium
@@ -32,7 +32,7 @@
 Name: web-platform-tests - Test Suites for Web Platform specifications
 Short Name: wpt
 URL: https://github.com/web-platform-tests/wpt/
-Version: 3d27143b5c6cbce8a9acc476b10c416aecd42a2d
+Version: 7edf9eabfae3550a4dd2a48625e68b02a734de15
 License: LICENSES FOR W3C TEST SUITES (http://www.w3.org/Consortium/Legal/2008/04-testsuite-copyright.html)
 License File: wpt/wpt/LICENSE.md
 Security Critical: no
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/WPTWhiteList b/third_party/blink/tools/blinkpy/third_party/wpt/WPTWhiteList
index 0c3a99f..e648b81 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/WPTWhiteList
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/WPTWhiteList
@@ -9,6 +9,7 @@
 ./tools/lint/commands.json
 ./tools/lint/fnmatch.py
 ./tools/lint/lint.py
+./tools/lint/rules.py
 ./tools/localpaths.py
 ./tools/manifest/XMLParser.py
 ./tools/manifest/__init__.py
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/checkout.sh b/third_party/blink/tools/blinkpy/third_party/wpt/checkout.sh
index 59555ad..ffc40e2 100755
--- a/third_party/blink/tools/blinkpy/third_party/wpt/checkout.sh
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/checkout.sh
@@ -8,8 +8,8 @@
 cd $DIR
 
 TARGET_DIR=$DIR/wpt
-REMOTE_REPO="https://chromium.googlesource.com/external/github.com/web-platform-tests/wpt.git"
-WPT_HEAD=3d27143b5c6cbce8a9acc476b10c416aecd42a2d
+REMOTE_REPO="https://github.com/web-platform-tests/wpt.git"
+WPT_HEAD=7edf9eabfae3550a4dd2a48625e68b02a734de15
 
 function clone {
   # Remove existing repo if already exists.
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/chromium.patch b/third_party/blink/tools/blinkpy/third_party/wpt/chromium.patch
index ea26cc3..c65fd3b 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/chromium.patch
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/chromium.patch
@@ -1,8 +1,8 @@
 diff --git a/tools/lint/lint.py b/tools/lint/lint.py
-index c1ffd50fb2..3cf81413fe 100644
+index 6a71dc860f..5b156b1c45 100644
 --- a/tools/lint/lint.py
 +++ b/tools/lint/lint.py
-@@ -817,6 +817,7 @@ def create_parser():
+@@ -818,6 +818,7 @@ def create_parser():
                          help="Output markdown")
      parser.add_argument("--repo-root", help="The WPT directory. Use this"
                          "option if the lint script exists outside the repository")
@@ -10,7 +10,7 @@
      parser.add_argument("--all", action="store_true", help="If no paths are passed, try to lint the whole "
                          "working directory, not just files that changed")
      return parser
-@@ -838,16 +839,21 @@ def main(**kwargs):
+@@ -841,17 +842,22 @@ def main(**kwargs):
  
      paths = lint_paths(kwargs, repo_root)
  
@@ -20,9 +20,11 @@
 +    return lint(repo_root, paths, output_format, ignore_glob)
  
 -def lint(repo_root, paths, output_format):
+-    # type: (str, List[str], str) -> int
 +
 +def lint(repo_root, paths, output_format, ignore_glob):
-     error_count = defaultdict(int)
++    # type: (str, List[str], str, str) -> int
+     error_count = defaultdict(int)  # type: Dict[Text, int]
      last = None
  
      with open(os.path.join(repo_root, "lint.whitelist")) as f:
@@ -35,7 +37,7 @@
                       "markdown": output_errors_markdown,
                       "normal": output_errors_text}[output_format]
 diff --git a/tools/wpt/paths b/tools/wpt/paths
-index 0ef13c96e3..93c97dc02b 100644
+index 4528222fbf..93c97dc02b 100644
 --- a/tools/wpt/paths
 +++ b/tools/wpt/paths
 @@ -1,6 +1,4 @@
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/.gitignore b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/.gitignore
index cc4bd700..60d0687 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/.gitignore
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/.gitignore
@@ -2,6 +2,7 @@
 *.py[co]
 .cache/
 .coverage*
+.mypy_cache/
 .pytest_cache/
 .tox/
 .virtualenv/
@@ -46,3 +47,6 @@
 /css/dist_last
 /css/tools/cache
 /webaudio/idl/*
+
+# w3c-test.org PR-branch mirroring
+submissions/
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/CONTRIBUTING.md b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/CONTRIBUTING.md
index 427ec682..baa208e3 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/CONTRIBUTING.md
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/CONTRIBUTING.md
@@ -1,32 +1,4 @@
-Grant of License
-----------------
-
-By contributing to this repository, you and the company you represent, if the
-company holds any copyrights in the contribution, grant to the W3C a perpetual,
-non-exclusive, royalty-free, world-wide right and license under all of your
-copyrights in this contribution to copy, publish, use, and modify the
-contribution and to distribute the contribution under a BSD License or one with
-more restrictive terms, as well as a right and license of the same scope to any
-derivative works prepared by the W3C and based on or incorporating all or part
-of the contribution. You further agree that any derivative works of this
-contribution prepared by the W3C shall be solely owned by the W3C.
-
-You state, to the best of your knowledge, that you, or the company you
-represent, have all rights necessary to contribute the materials.
-
-W3C will retain attribution of initial authorship to you. The W3C makes no
-a-priori commitment to support or distribute contributions.
-
-Disclaimer
-----------
-
-All content from this repository is provided as is, and W3C makes no
-representations or warranties, express or implied, including, but not limited
-to, warranties of merchantability, fitness for a particular purpose,
-non-infringement, or title; nor that the contents of this repository are
-suitable for any purpose. We make no representations, express or implied, that
-the content of this repository or the use thereof indicates conformance to a
-specification. All content is provided as-is to help reach interoperability.
+All contributions are licensed under the terms of the LICENSE.md file.
 
 Documentation
 -------------
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/LICENSE.md b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/LICENSE.md
index 6b346a52..ad4858c 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/LICENSE.md
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/LICENSE.md
@@ -1,33 +1,11 @@
-# Dual-License for W3C Test Suites
+# The 3-Clause BSD License
 
-All documents in this Repository are licensed by contributors to be distributed under both the [W3C Test Suite License](#w3c-test-suite-license) and the [W3C 3-clause BSD License](#w3c-3-clause-bsd-license), reproduced below. The choice of license is up to the licensee. For more information, see [Licenses for W3C Test Suites](https://www.w3.org/Consortium/Legal/2008/04-testsuite-copyright.html)
-
-# W3C Test Suite License
-
-This document, Test Suites and other documents that link to this statement are provided by the copyright holders under the following license: By using and/or copying this document, or the W3C document from which this statement is linked, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions:
-
-Permission to copy, and distribute the contents of this document, or the W3C document from which this statement is linked, in any medium for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the document, or portions thereof, that you use:
-
-*    A link or URL to the original W3C document.
-*    The pre-existing copyright notice of the original author, or if it doesn't exist, a notice (hypertext is preferred, but a textual representation is permitted) of the form: "Copyright © [$date-of-document] World Wide Web Consortium, (MIT, ERCIM, Keio, Beihang) and others. All Rights Reserved. http://www.w3.org/Consortium/Legal/2008/04-testsuite-copyright.html"
-*    If it exists, the STATUS of the W3C document.
-
-When space permits, inclusion of the full text of this NOTICE should be provided. We request that authorship attribution be provided in any software, documents, or other items or products that you create pursuant to the implementation of the contents of this document, or any portion thereof.
-
-No right to create modifications or derivatives of W3C documents is granted pursuant to this license. However, if additional requirements (documented in the Copyright FAQ) are satisfied, the right to create modifications or derivatives is sometimes granted by the W3C to individuals complying with those requirements.
-
-If a Test Suite distinguishes the test harness (or, framework for navigation) and the actual tests, permission is given to remove or alter the harness or navigation if the Test Suite in question allows to do so. The tests themselves shall NOT be changed in any way.
-
-The name and trademarks of W3C and other copyright holders may NOT be used in advertising or publicity pertaining to this document or other documents that link to this statement without specific, written prior permission. Title to copyright in this document will at all times remain with copyright holders. Permission is given to use the trademarked string "W3C" within claims of performance concerning W3C Specifications or features described therein, and there only, if the test suite so authorizes.
-
-THIS WORK IS PROVIDED BY W3C, MIT, ERCIM, KEIO, BEIHANG, THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL W3C, MIT, ERCIM, KEIO, BEIHANG, THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# W3C 3-clause BSD License
+Copyright 2019 web-platform-tests contributors
 
 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 
-*    Redistributions of works must retain the original copyright notice, this list of conditions and the following disclaimer.
-*    Redistributions in binary form must reproduce the original copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-*    Neither the name of the W3C nor the names of its contributors may be used to endorse or promote products derived from this work without specific prior written permission.
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
 
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/serve.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/serve.py
deleted file mode 100644
index db92a67f..0000000
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/serve.py
+++ /dev/null
@@ -1,12 +0,0 @@
-import sys
-import logging
-
-try:
-    from tools.serve import serve
-except ImportError:
-    logging.error("tools.serve not found.  Did you forget to run "
-                  '"git submodule update --init --recursive"?')
-    sys.exit(2)
-
-def main():
-    serve.main()
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/gitignore/gitignore.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/gitignore/gitignore.py
index 9b661308..d5ea4a6a8 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/gitignore/gitignore.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/gitignore/gitignore.py
@@ -4,11 +4,29 @@
 from six import itervalues, iteritems
 from collections import defaultdict
 
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from typing import Any
+    from typing import Dict
+    from typing import Iterable
+    from typing import List
+    from typing import MutableMapping
+    from typing import Optional
+    from typing import Pattern
+    from typing import Tuple
+    from typing import TypeVar
+    from typing import Union
+    from typing import cast
+
+    T = TypeVar('T')
+
 
 end_space = re.compile(r"([^\\]\s)*$")
 
 
 def fnmatch_translate(pat):
+    # type: (str) -> Tuple[bool, Pattern[str]]
     parts = []
     seq = None
     i = 0
@@ -94,9 +112,10 @@
 
 
 def parse_line(line):
+    # type: (str) -> Optional[Tuple[bool, bool, bool, Union[Tuple[str, ...], Tuple[bool, Pattern[str]]]]]
     line = line.rstrip()
     if not line or line[0] == "#":
-        return
+        return None
 
     invert = line[0] == "!"
     if invert:
@@ -110,7 +129,7 @@
     # Could make a special case for **/foo, but we don't have any patterns like that
     if not invert and not pattern_re.match(line):
         literal = True
-        pattern = tuple(line.rsplit("/", 1))
+        pattern = tuple(line.rsplit("/", 1))  # type: Union[Tuple[str, ...], Tuple[bool, Pattern[str]]]
     else:
         pattern = fnmatch_translate(line)
         literal = False
@@ -120,8 +139,9 @@
 
 class PathFilter(object):
     def __init__(self, root, extras=None, cache=None):
+        # type: (str, Optional[List[str]], Optional[MutableMapping[str, bool]]) -> None
         if root:
-            ignore_path = os.path.join(root, ".gitignore")
+            ignore_path = os.path.join(root, ".gitignore")  # type: Optional[str]
         else:
             ignore_path = None
         if not ignore_path and not extras:
@@ -129,22 +149,23 @@
             return
         self.trivial = False
 
-        self.literals_file = defaultdict(dict)
-        self.literals_dir = defaultdict(dict)
-        self.patterns_file = []
-        self.patterns_dir = []
-        self.cache = cache or {}
+        self.literals_file = defaultdict(dict)  # type: Dict[Optional[str], Dict[str, List[Tuple[bool, Pattern[str]]]]]
+        self.literals_dir = defaultdict(dict)  # type: Dict[Optional[str], Dict[str, List[Tuple[bool, Pattern[str]]]]]
+        self.patterns_file = []  # type: List[Tuple[Tuple[bool, Pattern[str]], List[Tuple[bool, Pattern[str]]]]]
+        self.patterns_dir = []  # type: List[Tuple[Tuple[bool, Pattern[str]], List[Tuple[bool, Pattern[str]]]]]
+        self.cache = cache or {}  # type: MutableMapping[str, bool]
 
         if extras is None:
             extras = []
 
         if ignore_path and os.path.exists(ignore_path):
-            args = ignore_path, extras
+            args = ignore_path, extras  # type: Tuple[Optional[str], List[str]]
         else:
             args = None, extras
         self._read_ignore(*args)
 
     def _read_ignore(self, ignore_path, extras):
+        # type: (Optional[str], List[str]) -> None
         if ignore_path is not None:
             with open(ignore_path) as f:
                 for line in f:
@@ -153,6 +174,7 @@
             self._read_line(line)
 
     def _read_line(self, line):
+        # type: (str) -> None
         parsed = parse_line(line)
         if not parsed:
             return
@@ -163,12 +185,14 @@
             # that we can match patterns out of order and check if they were later
             # overriden by an exclude rule
             assert not literal
+            if MYPY:
+                rule = cast(Tuple[bool, Pattern[str]], rule)
             if not dir_only:
                 rules_iter = itertools.chain(
                     itertools.chain(*(iteritems(item) for item in itervalues(self.literals_dir))),
                     itertools.chain(*(iteritems(item) for item in itervalues(self.literals_file))),
                     self.patterns_dir,
-                    self.patterns_file)
+                    self.patterns_file)  # type: Iterable[Tuple[Any, List[Tuple[bool, Pattern[str]]]]]
             else:
                 rules_iter = itertools.chain(
                     itertools.chain(*(iteritems(item) for item in itervalues(self.literals_dir))),
@@ -178,27 +202,34 @@
                 rules[1].append(rule)
         else:
             if literal:
+                if MYPY:
+                    rule = cast(Tuple[str, ...], rule)
                 if len(rule) == 1:
-                    dir_name, pattern = None, rule[0]
+                    dir_name, pattern = None, rule[0]  # type: Tuple[Optional[str], str]
                 else:
                     dir_name, pattern = rule
                 self.literals_dir[dir_name][pattern] = []
                 if not dir_only:
                     self.literals_file[dir_name][pattern] = []
             else:
+                if MYPY:
+                    rule = cast(Tuple[bool, Pattern[str]], rule)
                 self.patterns_dir.append((rule, []))
                 if not dir_only:
                     self.patterns_file.append((rule, []))
 
-    def filter(self, iterator):
-        empty = {}
+    def filter(self,
+               iterator  # type: Iterable[Tuple[str, List[Tuple[str, T]], List[Tuple[str, T]]]]
+               ):
+        # type: (...) -> Iterable[Tuple[str, List[Tuple[str, T]], List[Tuple[str, T]]]]
+        empty = {}  # type: Dict[Any, Any]
         for dirpath, dirnames, filenames in iterator:
             orig_dirpath = dirpath
             if os.path.sep != "/":
                 dirpath = dirpath.replace(os.path.sep, "/")
 
-            keep_dirs = []
-            keep_files = []
+            keep_dirs = []  # type: List[Tuple[str, T]]
+            keep_files = []  # type: List[Tuple[str, T]]
 
             for iter_items, literals, patterns, target, suffix in [
                     (dirnames, self.literals_dir, self.patterns_dir, keep_dirs, "/"),
@@ -216,7 +247,8 @@
                     for rule_dir in [None, dirpath]:
                         if name in literals.get(rule_dir, empty):
                             exclude = literals[rule_dir][name]
-                            if not any(rule.match(path) for rule in exclude):
+                            if not any(rule.match(name if name_only else path)
+                                       for name_only, rule in exclude):
                                 # Skip this item
                                 self.cache[path] = True
                                 break
@@ -240,7 +272,10 @@
             assert ".git" not in dirnames
             yield orig_dirpath, dirnames, keep_files
 
-    def __call__(self, iterator):
+    def __call__(self,
+                 iterator  # type: Iterable[Tuple[str, List[Tuple[str, T]], List[Tuple[str, T]]]]
+                 ):
+        # type: (...) -> Iterable[Tuple[str, List[Tuple[str, T]], List[Tuple[str, T]]]]
         if self.trivial:
             return iterator
 
@@ -248,4 +283,5 @@
 
 
 def has_ignore(dirpath):
+    # type: (str) -> bool
     return os.path.exists(os.path.join(dirpath, ".gitignore"))
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/lint/fnmatch.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/lint/fnmatch.py
index 2fe3eb0..3d9ee23 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/lint/fnmatch.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/lint/fnmatch.py
@@ -3,17 +3,26 @@
 import fnmatch as _stdlib_fnmatch
 import os
 
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from typing import AnyStr
+    from typing import Iterable
+    from typing import List
+
 
 __all__ = ["fnmatch", "fnmatchcase", "filter", "translate"]
 
 
 def fnmatch(name, pat):
+    # type: (AnyStr, AnyStr) -> bool
     name = os.path.normcase(name)
     pat = os.path.normcase(pat)
     return fnmatchcase(name, pat)
 
 
 def fnmatchcase(name, pat):
+    # type: (AnyStr, AnyStr) -> bool
     if '?' not in pat and '[' not in pat:
         wildcards = pat.count("*")
         if wildcards == 0:
@@ -26,6 +35,7 @@
 
 
 def filter(names, pat):
+    # type: (Iterable[AnyStr], AnyStr) -> List[AnyStr]
     return [n for n in names if fnmatch(n, pat)]
 
 
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/lint/lint.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/lint/lint.py
index bcf08d9b..5b156b1 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/lint/lint.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/lint/lint.py
@@ -4,6 +4,7 @@
 import argparse
 import ast
 import json
+import logging
 import os
 import re
 import subprocess
@@ -13,33 +14,54 @@
 from collections import defaultdict
 
 from . import fnmatch
+from . import rules
 from .. import localpaths
 from ..gitignore.gitignore import PathFilter
 from ..wpt import testfiles
 from ..manifest.vcs import walk
 
-from manifest.sourcefile import SourceFile, js_meta_re, python_meta_re, space_chars, get_any_variants, get_default_any_variants
-from six import binary_type, iteritems, itervalues
+from ..manifest.sourcefile import SourceFile, js_meta_re, python_meta_re, space_chars, get_any_variants, get_default_any_variants
+from six import binary_type, iteritems, itervalues, with_metaclass
 from six.moves import range
 from six.moves.urllib.parse import urlsplit, urljoin
 
-import logging
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from typing import Any
+    from typing import Dict
+    from typing import IO
+    from typing import Iterable
+    from typing import List
+    from typing import Optional
+    from typing import Sequence
+    from typing import Set
+    from typing import Text
+    from typing import Tuple
+    from typing import Type
+    from typing import Union
 
-logger = None
+    Whitelist = Dict[Text, Dict[Text, Set[Optional[int]]]]
+
+
+logger = None  # type: Optional[logging.Logger]
 
 def setup_logging(prefix=False):
+    # type: (bool) -> None
     global logger
     if logger is None:
         logger = logging.getLogger(os.path.basename(os.path.splitext(__file__)[0]))
-        handler = logging.StreamHandler(sys.stdout)
+        handler = logging.StreamHandler(sys.stdout)  # type: logging.Handler
         # Only add a handler if the parent logger is missing a handler
-        if logger.parent and len(logger.parent.handlers) == 0:
+        parent = logger.parent
+        assert isinstance(parent, logging.Logger)
+        if parent and len(parent.handlers) == 0:
             handler = logging.StreamHandler(sys.stdout)
             logger.addHandler(handler)
     if prefix:
         format = logging.BASIC_FORMAT
     else:
-        format = "%(message)s"
+        format = str("%(message)s")
     formatter = logging.Formatter(format)
     for handler in logger.handlers:
         handler.setFormatter(formatter)
@@ -63,7 +85,8 @@
 %s: %s"""
 
 def all_filesystem_paths(repo_root, subdir=None):
-    path_filter = PathFilter(repo_root, extras=[".git/"])
+    # type: (str, Optional[str]) -> Iterable[str]
+    path_filter = PathFilter(repo_root, extras=[str(".git/")])
     if subdir:
         expanded_path = subdir
     else:
@@ -78,6 +101,7 @@
 
 
 def _all_files_equal(paths):
+    # type: (Iterable[str]) -> bool
     """
     Checks all the paths are files that are byte-for-byte identical
 
@@ -116,25 +140,25 @@
 
 
 def check_path_length(repo_root, path):
+    # type: (str, str) -> List[rules.Error]
     if len(path) + 1 > 150:
-        return [("PATH LENGTH", "/%s longer than maximum path length (%d > 150)" % (path, len(path) + 1), path, None)]
+        return [rules.PathLength.error(path, (path, len(path) + 1))]
     return []
 
 
 def check_worker_collision(repo_root, path):
+    # type: (str, str) -> List[rules.Error]
     endings = [(".any.html", ".any.js"),
                (".any.worker.html", ".any.js"),
                (".worker.html", ".worker.js")]
     for path_ending, generated in endings:
         if path.endswith(path_ending):
-            return [("WORKER COLLISION",
-                     "path ends with %s which collides with generated tests from %s files" % (path_ending, generated),
-                     path,
-                     None)]
+            return [rules.WorkerCollision.error(path, (path_ending, generated))]
     return []
 
 
 def check_gitignore_file(repo_root, path):
+    # type: (str, str) -> List[rules.Error]
     if not path.endswith(".gitignore"):
         return []
 
@@ -150,20 +174,19 @@
         path_parts[:3] == ["css", "tools", "apiclient"]):
         return []
 
-    return [("GITIGNORE",
-             ".gitignore found outside the root",
-             path,
-             None)]
+    return [rules.GitIgnoreFile.error(path)]
 
 
 def check_ahem_copy(repo_root, path):
+    # type: (str, str) -> List[rules.Error]
     lpath = path.lower()
     if "ahem" in lpath and lpath.endswith(".ttf"):
-        return [("AHEM COPY", "Don't add extra copies of Ahem, use /fonts/Ahem.ttf", path, None)]
+        return [rules.AhemCopy.error(path)]
     return []
 
 
 def check_git_ignore(repo_root, paths):
+    # type: (str, List[str]) -> List[rules.Error]
     errors = []
     with tempfile.TemporaryFile('w+') as f:
         f.write('\n'.join(paths))
@@ -177,8 +200,7 @@
                 # If the matching filter reported by check-ignore is a special-case exception,
                 # that's fine. Otherwise, it requires a new special-case exception.
                 if filter_string[0] != '!':
-                    errors += [("IGNORED PATH", "%s matches an ignore filter in .gitignore - "
-                                "please add a .gitignore exception" % path, path, None)]
+                    errors.append(rules.IgnoredPath.error(path, (path,)))
         except subprocess.CalledProcessError:
             # Nonzero return code means that no match exists.
             pass
@@ -191,6 +213,7 @@
 
 
 def check_css_globally_unique(repo_root, paths):
+    # type: (str, List[str]) -> List[rules.Error]
     """
     Checks that CSS filenames are sufficiently unique
 
@@ -208,13 +231,16 @@
     :returns: a list of errors found in ``paths``
 
     """
-    test_files = defaultdict(set)
-    ref_files = defaultdict(set)
-    support_files = defaultdict(set)
+    test_files = defaultdict(set)  # type: Dict[Union[bytes, Text], Set[str]]
+    ref_files = defaultdict(set)  # type: Dict[Union[bytes, Text], Set[str]]
+    support_files = defaultdict(set)  # type: Dict[Union[bytes, Text], Set[str]]
 
     for path in paths:
         if os.name == "nt":
-            path = path.replace("\\", "/")
+            if isinstance(path, binary_type):
+                path = path.replace(b"\\", b"/")
+            else:
+                path = path.replace(u"\\", u"/")
 
         if not path.startswith("css/"):
             continue
@@ -234,13 +260,17 @@
                 any(parts[:len(non_test_path)] == list(non_test_path) for non_test_path in source_file.dir_path_non_test)):
                 continue
 
-            name = path[offset+1:]
-            support_files[name].add(path)
+            support_name = path[offset+1:]
+            support_files[support_name].add(path)
         elif source_file.name_is_reference:
             ref_files[source_file.name].add(path)
         else:
-            name = source_file.name.replace('-manual', '')
-            test_files[name].add(path)
+            test_name = source_file.name  # type: Union[bytes, Text]
+            if isinstance(test_name, bytes):
+                test_name = test_name.replace(b'-manual', b'')
+            else:
+                test_name = test_name.replace(u'-manual', u'')
+            test_files[test_name].add(path)
 
     errors = []
 
@@ -248,7 +278,7 @@
         if len(colliding) > 1:
             if not _all_files_equal([os.path.join(repo_root, x) for x in colliding]):
                 # Only compute by_spec if there are prima-facie collisions because of cost
-                by_spec = defaultdict(set)
+                by_spec = defaultdict(set)  # type: Dict[Text, Set[str]]
                 for path in colliding:
                     source_file = SourceFile(repo_root, path, "/")
                     for link in source_file.spec_links:
@@ -261,52 +291,49 @@
                             continue
                         by_spec[spec].add(path)
 
-                for spec, paths in iteritems(by_spec):
-                    if not _all_files_equal([os.path.join(repo_root, x) for x in paths]):
-                        for x in paths:
-                            errors.append(("CSS-COLLIDING-TEST-NAME",
-                                           "The filename %s in the %s testsuite is shared by: %s"
-                                           % (name,
-                                              spec,
-                                              ", ".join(sorted(paths))),
-                                           x,
-                                           None))
+                for spec, spec_paths in iteritems(by_spec):
+                    if not _all_files_equal([os.path.join(repo_root, x) for x in spec_paths]):
+                        for x in spec_paths:
+                            context1 = (name, spec, ", ".join(sorted(spec_paths)))
+                            errors.append(rules.CSSCollidingTestName.error(x,
+                                                                           context1))
 
-    for error_name, d in [("CSS-COLLIDING-REF-NAME", ref_files),
-                          ("CSS-COLLIDING-SUPPORT-NAME", support_files)]:
+    for rule_class, d in [(rules.CSSCollidingRefName, ref_files),
+                          (rules.CSSCollidingSupportName, support_files)]:
         for name, colliding in iteritems(d):
             if len(colliding) > 1:
                 if not _all_files_equal([os.path.join(repo_root, x) for x in colliding]):
+                    context2 = (name, ", ".join(sorted(colliding)))
+
                     for x in colliding:
-                        errors.append((error_name,
-                                       "The filename %s is shared by: %s" % (name,
-                                                                             ", ".join(sorted(colliding))),
-                                       x,
-                                       None))
+                        errors.append(rule_class.error(x, context2))
 
     return errors
 
 
 def parse_whitelist(f):
+    # type: (IO[bytes]) -> Tuple[Whitelist, Set[Text]]
     """
     Parse the whitelist file given by `f`, and return the parsed structure.
     """
 
-    data = defaultdict(lambda:defaultdict(set))
-    ignored_files = set()
+    data = defaultdict(lambda:defaultdict(set))  # type: Whitelist
+    ignored_files = set()  # type: Set[Text]
 
     for line in f:
         line = line.strip()
         if not line or line.startswith("#"):
             continue
         parts = [item.strip() for item in line.split(":")]
-        if len(parts) == 2:
-            parts.append(None)
-        else:
-            parts[-1] = int(parts[-1])
 
-        error_types, file_match, line_number = parts
-        error_types = {item.strip() for item in error_types.split(",")}
+        if len(parts) == 2:
+            error_types_s, file_match = parts
+            line_number = None  # type: Optional[int]
+        else:
+            error_types_s, file_match, line_number_s = parts
+            line_number = int(line_number_s)
+
+        error_types = {item.strip() for item in error_types_s.split(",")}
         file_match = os.path.normcase(file_match)
 
         if "*" in error_types:
@@ -319,6 +346,7 @@
 
 
 def filter_whitelist_errors(data, errors):
+    # type: (Whitelist, Sequence[rules.Error]) -> List[rules.Error]
     """
     Filter out those errors that are whitelisted in `data`.
     """
@@ -341,130 +369,50 @@
 
     return [item for i, item in enumerate(errors) if not whitelisted[i]]
 
-class Regexp(object):
-    pattern = None
-    file_extensions = None
-    error = None
-    _re = None
 
-    def __init__(self):
-        self._re = re.compile(self.pattern)
-
-    def applies(self, path):
-        return (self.file_extensions is None or
-                os.path.splitext(path)[1] in self.file_extensions)
-
-    def search(self, line):
-        return self._re.search(line)
-
-class TrailingWhitespaceRegexp(Regexp):
-    pattern = b"[ \t\f\v]$"
-    error = "TRAILING WHITESPACE"
-    description = "Whitespace at EOL"
-
-class TabsRegexp(Regexp):
-    pattern = b"^\t"
-    error = "INDENT TABS"
-    description = "Tabs used for indentation"
-
-class CRRegexp(Regexp):
-    pattern = b"\r$"
-    error = "CR AT EOL"
-    description = "CR character in line separator"
-
-class SetTimeoutRegexp(Regexp):
-    pattern = b"setTimeout\s*\("
-    error = "SET TIMEOUT"
-    file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
-    description = "setTimeout used; step_timeout should typically be used instead"
-
-class W3CTestOrgRegexp(Regexp):
-    pattern = b"w3c\-test\.org"
-    error = "W3C-TEST.ORG"
-    description = "External w3c-test.org domain used"
-
-class WebPlatformTestRegexp(Regexp):
-    pattern = b"web\-platform\.test"
-    error = "WEB-PLATFORM.TEST"
-    description = "Internal web-platform.test domain used"
-
-class Webidl2Regexp(Regexp):
-    pattern = b"webidl2\.js"
-    error = "WEBIDL2.JS"
-    description = "Legacy webidl2.js script used"
-
-class ConsoleRegexp(Regexp):
-    pattern = b"console\.[a-zA-Z]+\s*\("
-    error = "CONSOLE"
-    file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
-    description = "Console logging API used"
-
-class GenerateTestsRegexp(Regexp):
-    pattern = b"generate_tests\s*\("
-    error = "GENERATE_TESTS"
-    file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
-    description = "generate_tests used"
-
-class PrintRegexp(Regexp):
-    pattern = b"print(?:\s|\s*\()"
-    error = "PRINT STATEMENT"
-    file_extensions = [".py"]
-    description = "Print function used"
-
-class LayoutTestsRegexp(Regexp):
-    pattern = b"eventSender|testRunner|window\.internals"
-    error = "LAYOUTTESTS APIS"
-    file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
-    description = "eventSender/testRunner/window.internals used; these are LayoutTests-specific APIs (WebKit/Blink)"
-
-class SpecialPowersRegexp(Regexp):
-    pattern = b"SpecialPowers"
-    error = "SPECIALPOWERS API"
-    file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
-    description = "SpecialPowers used; this is gecko-specific and not supported in wpt"
-
-
-regexps = [item() for item in
-           [TrailingWhitespaceRegexp,
-            TabsRegexp,
-            CRRegexp,
-            SetTimeoutRegexp,
-            W3CTestOrgRegexp,
-            WebPlatformTestRegexp,
-            Webidl2Regexp,
-            ConsoleRegexp,
-            GenerateTestsRegexp,
-            PrintRegexp,
-            LayoutTestsRegexp,
-            SpecialPowersRegexp]]
+regexps = [item() for item in  # type: ignore
+           [rules.TrailingWhitespaceRegexp,
+            rules.TabsRegexp,
+            rules.CRRegexp,
+            rules.SetTimeoutRegexp,
+            rules.W3CTestOrgRegexp,
+            rules.WebPlatformTestRegexp,
+            rules.Webidl2Regexp,
+            rules.ConsoleRegexp,
+            rules.GenerateTestsRegexp,
+            rules.PrintRegexp,
+            rules.LayoutTestsRegexp,
+            rules.SpecialPowersRegexp]]
 
 def check_regexp_line(repo_root, path, f):
-    errors = []
+    # type: (str, str, IO[bytes]) -> List[rules.Error]
+    errors = []  # type: List[rules.Error]
 
     applicable_regexps = [regexp for regexp in regexps if regexp.applies(path)]
 
     for i, line in enumerate(f):
         for regexp in applicable_regexps:
             if regexp.search(line):
-                errors.append((regexp.error, regexp.description, path, i+1))
+                errors.append((regexp.name, regexp.description, path, i+1))
 
     return errors
 
 def check_parsed(repo_root, path, f):
+    # type: (str, str, IO[bytes]) -> List[rules.Error]
     source_file = SourceFile(repo_root, path, "/", contents=f.read())
 
-    errors = []
+    errors = []  # type: List[rules.Error]
 
     if path.startswith("css/"):
         if (source_file.type == "support" and
             not source_file.name_is_non_test and
             not source_file.name_is_reference):
-            return [("SUPPORT-WRONG-DIR", "Support file not in support directory", path, None)]
+            return [rules.SupportWrongDir.error(path)]
 
         if (source_file.type != "support" and
             not source_file.name_is_reference and
             not source_file.spec_links):
-            return [("MISSING-LINK", "Testcase file must have a link to a spec", path, None)]
+            return [rules.MissingLink.error(path)]
 
     if source_file.name_is_non_test:
         return []
@@ -473,13 +421,13 @@
         return []
 
     if source_file.root is None:
-        return [("PARSE-FAILED", "Unable to parse file", path, None)]
+        return [rules.ParseFailed.error(path)]
 
     if source_file.type == "manual" and not source_file.name_is_manual:
-        errors.append(("CONTENT-MANUAL", "Manual test whose filename doesn't end in '-manual'", path, None))
+        errors.append(rules.ContentManual.error(path))
 
     if source_file.type == "visual" and not source_file.name_is_visual:
-        errors.append(("CONTENT-VISUAL", "Visual test whose filename doesn't end in '-visual'", path, None))
+        errors.append(rules.ContentVisual.error(path))
 
     about_blank_parts = urlsplit("about:blank")
     for reftest_node in source_file.reftest_nodes:
@@ -490,18 +438,14 @@
             continue
 
         if (parts.scheme or parts.netloc):
-            errors.append(("ABSOLUTE-URL-REF",
-                     "Reference test with a reference file specified via an absolute URL: '%s'" % href, path, None))
+            errors.append(rules.AbsoluteUrlRef.error(path, (href,)))
             continue
 
         ref_url = urljoin(source_file.url, href)
         ref_parts = urlsplit(ref_url)
 
         if source_file.url == ref_url:
-            errors.append(("SAME-FILE-REF",
-                           "Reference test which points at itself as a reference",
-                           path,
-                           None))
+            errors.append(rules.SameFileRef.error(path))
             continue
 
         assert ref_parts.path != ""
@@ -510,45 +454,39 @@
         reference_rel = reftest_node.attrib.get("rel", "")
 
         if not os.path.isfile(reference_file):
-            errors.append(("NON-EXISTENT-REF",
-                     "Reference test with a non-existent '%s' relationship reference: '%s'" % (reference_rel, href), path, None))
+            errors.append(rules.NonexistentRef.error(path,
+                                                     (reference_rel, href)))
 
     if len(source_file.timeout_nodes) > 1:
-        errors.append(("MULTIPLE-TIMEOUT", "More than one meta name='timeout'", path, None))
+        errors.append(rules.MultipleTimeout.error(path))
 
     for timeout_node in source_file.timeout_nodes:
         timeout_value = timeout_node.attrib.get("content", "").lower()
         if timeout_value != "long":
-            errors.append(("INVALID-TIMEOUT", "Invalid timeout value %s" % timeout_value, path, None))
+            errors.append(rules.InvalidTimeout.error(path, (timeout_value,)))
 
     if source_file.testharness_nodes:
         if len(source_file.testharness_nodes) > 1:
-            errors.append(("MULTIPLE-TESTHARNESS",
-                           "More than one <script src='/resources/testharness.js'>", path, None))
+            errors.append(rules.MultipleTestharness.error(path))
 
         testharnessreport_nodes = source_file.root.findall(".//{http://www.w3.org/1999/xhtml}script[@src='/resources/testharnessreport.js']")
         if not testharnessreport_nodes:
-            errors.append(("MISSING-TESTHARNESSREPORT",
-                           "Missing <script src='/resources/testharnessreport.js'>", path, None))
+            errors.append(rules.MissingTestharnessReport.error(path))
         else:
             if len(testharnessreport_nodes) > 1:
-                errors.append(("MULTIPLE-TESTHARNESSREPORT",
-                               "More than one <script src='/resources/testharnessreport.js'>", path, None))
+                errors.append(rules.MultipleTestharnessReport.error(path))
 
         testharnesscss_nodes = source_file.root.findall(".//{http://www.w3.org/1999/xhtml}link[@href='/resources/testharness.css']")
         if testharnesscss_nodes:
-            errors.append(("PRESENT-TESTHARNESSCSS",
-                           "Explicit link to testharness.css present", path, None))
+            errors.append(rules.PresentTestharnessCSS.error(path))
 
         for element in source_file.variant_nodes:
             if "content" not in element.attrib:
-                errors.append(("VARIANT-MISSING",
-                               "<meta name=variant> missing 'content' attribute", path, None))
+                errors.append(rules.VariantMissing.error(path))
             else:
                 variant = element.attrib["content"]
                 if variant != "" and variant[0] not in ("?", "#"):
-                    errors.append(("MALFORMED-VARIANT",
-                               "%s <meta name=variant> 'content' attribute must be the empty string or start with '?' or '#'" % path, None))
+                    errors.append(rules.MalformedVariant.error(path, (path,)))
 
         seen_elements = {"timeout": False,
                          "testharness": False,
@@ -562,8 +500,7 @@
             if source_file.timeout_nodes and elem == source_file.timeout_nodes[0]:
                 seen_elements["timeout"] = True
                 if seen_elements["testharness"]:
-                    errors.append(("LATE-TIMEOUT",
-                                   "<meta name=timeout> seen after testharness.js script", path, None))
+                    errors.append(rules.LateTimeout.error(path))
 
             elif elem == source_file.testharness_nodes[0]:
                 seen_elements["testharness"] = True
@@ -571,52 +508,64 @@
             elif testharnessreport_nodes and elem == testharnessreport_nodes[0]:
                 seen_elements["testharnessreport"] = True
                 if not seen_elements["testharness"]:
-                    errors.append(("EARLY-TESTHARNESSREPORT",
-                                   "testharnessreport.js script seen before testharness.js script", path, None))
+                    errors.append(rules.EarlyTestharnessReport.error(path))
 
             if all(seen_elements[name] for name in required_elements):
                 break
 
     if source_file.testdriver_nodes:
         if len(source_file.testdriver_nodes) > 1:
-            errors.append(("MULTIPLE-TESTDRIVER",
-                           "More than one <script src='/resources/testdriver.js'>", path, None))
+            errors.append(rules.MultipleTestdriver.error(path))
 
         testdriver_vendor_nodes = source_file.root.findall(".//{http://www.w3.org/1999/xhtml}script[@src='/resources/testdriver-vendor.js']")
         if not testdriver_vendor_nodes:
-            errors.append(("MISSING-TESTDRIVER-VENDOR",
-                           "Missing <script src='/resources/testdriver-vendor.js'>", path, None))
+            errors.append(rules.MissingTestdriverVendor.error(path))
         else:
             if len(testdriver_vendor_nodes) > 1:
-                errors.append(("MULTIPLE-TESTDRIVER-VENDOR",
-                               "More than one <script src='/resources/testdriver-vendor.js'>", path, None))
+                errors.append(rules.MultipleTestdriverVendor.error(path))
 
     for element in source_file.root.findall(".//{http://www.w3.org/1999/xhtml}script[@src]"):
         src = element.attrib["src"]
-        for name in ["testharness", "testharnessreport", "testdriver", "testdriver-vendor"]:
-            if "%s.js" % name == src or ("/%s.js" % name in src and src != "/resources/%s.js" % name):
-                errors.append(("%s-PATH" % name.upper(), "%s.js script seen with incorrect path" % name, path, None))
+
+        def incorrect_path(script, src):
+            # type: (Text, Text) -> bool
+            return (script == src or
+                ("/%s" % script in src and src != "/resources/%s" % script))
+
+        if incorrect_path("testharness.js", src):
+            errors.append(rules.TestharnessPath.error(path))
+
+        if incorrect_path("testharnessreport.js", src):
+            errors.append(rules.TestharnessReportPath.error(path))
+
+        if incorrect_path("testdriver.js", src):
+            errors.append(rules.TestdriverPath.error(path))
+
+        if incorrect_path("testdriver-vendor.js", src):
+            errors.append(rules.TestdriverVendorPath.error(path))
 
     return errors
 
-class ASTCheck(object):
-    __metaclass__ = abc.ABCMeta
-    error = None
-    description = None
+class ASTCheck(with_metaclass(abc.ABCMeta)):
+    @abc.abstractproperty
+    def rule(self):
+        # type: () -> Type[rules.Rule]
+        pass
 
     @abc.abstractmethod
     def check(self, root):
+        # type: (ast.AST) -> List[int]
         pass
 
 class OpenModeCheck(ASTCheck):
-    error = "OPEN-NO-MODE"
-    description = "File opened without providing an explicit mode (note: binary files must be read with 'b' in the mode flags)"
+    rule = rules.OpenNoMode
 
     def check(self, root):
+        # type: (ast.AST) -> List[int]
         errors = []
         for node in ast.walk(root):
             if isinstance(node, ast.Call):
-                if hasattr(node.func, "id") and node.func.id in ("open", "file"):
+                if hasattr(node.func, "id") and node.func.id in ("open", "file"):  # type: ignore
                     if (len(node.args) < 2 and
                         all(item.arg != "mode" for item in node.keywords)):
                         errors.append(node.lineno)
@@ -625,26 +574,28 @@
 ast_checkers = [item() for item in [OpenModeCheck]]
 
 def check_python_ast(repo_root, path, f):
+    # type: (str, str, IO[bytes]) -> List[rules.Error]
     if not path.endswith(".py"):
         return []
 
     try:
         root = ast.parse(f.read())
     except SyntaxError as e:
-        return [("PARSE-FAILED", "Unable to parse file", path, e.lineno)]
+        return [rules.ParseFailed.error(path, line_no=e.lineno)]
 
     errors = []
     for checker in ast_checkers:
         for lineno in checker.check(root):
-            errors.append((checker.error, checker.description, path, lineno))
+            errors.append(checker.rule.error(path, line_no=lineno))
     return errors
 
 
-broken_js_metadata = re.compile(b"//\s*META:")
-broken_python_metadata = re.compile(b"#\s*META:")
+broken_js_metadata = re.compile(br"//\s*META:")
+broken_python_metadata = re.compile(br"#\s*META:")
 
 
 def check_global_metadata(value):
+    # type: (str) -> Iterable[Tuple[Type[rules.Rule], Tuple[Any, ...]]]
     global_values = {item.strip() for item in value.split(b",") if item.strip()}
 
     included_variants = set.union(get_default_any_variants(),
@@ -654,22 +605,25 @@
         if global_value.startswith(b"!"):
             excluded_value = global_value[1:]
             if not get_any_variants(excluded_value):
-                yield ("UNKNOWN-GLOBAL-METADATA", "Unexpected value for global metadata")
+                yield (rules.UnknownGlobalMetadata, ())
 
             elif excluded_value in global_values:
-                yield ("BROKEN-GLOBAL-METADATA", "Cannot specify both %s and %s" % (global_value, excluded_value))
+                yield (rules.BrokenGlobalMetadata,
+                       (("Cannot specify both %s and %s" % (global_value, excluded_value)),))
 
             else:
                 excluded_variants = get_any_variants(excluded_value)
                 if not (excluded_variants & included_variants):
-                    yield ("BROKEN-GLOBAL-METADATA", "Cannot exclude %s if it is not included" % (excluded_value,))
+                    yield (rules.BrokenGlobalMetadata,
+                           (("Cannot exclude %s if it is not included" % (excluded_value,)),))
 
         else:
             if not get_any_variants(global_value):
-                yield ("UNKNOWN-GLOBAL-METADATA", "Unexpected value for global metadata")
+                yield (rules.UnknownGlobalMetadata, ())
 
 
 def check_script_metadata(repo_root, path, f):
+    # type: (str, str, IO[bytes]) -> List[rules.Error]
     if path.endswith((".worker.js", ".any.js")):
         meta_re = js_meta_re
         broken_metadata = broken_js_metadata
@@ -688,10 +642,12 @@
         if m:
             key, value = m.groups()
             if key == b"global":
-                errors.extend((kind, message, path, idx + 1) for (kind, message) in check_global_metadata(value))
+                for rule_class, context in check_global_metadata(value):
+                    errors.append(rule_class.error(path, context, idx + 1))
             elif key == b"timeout":
                 if value != b"long":
-                    errors.append(("UNKNOWN-TIMEOUT-METADATA", "Unexpected value for timeout metadata", path, idx + 1))
+                    errors.append(rules.UnknownTimeoutMetadata.error(path,
+                                                                     line_no=idx + 1))
             elif key == b"title":
                 pass
             elif key == b"script":
@@ -699,22 +655,25 @@
             elif key == b"variant":
                 pass
             else:
-                errors.append(("UNKNOWN-METADATA", "Unexpected kind of metadata", path, idx + 1))
+                errors.append(rules.UnknownMetadata.error(path,
+                                                          line_no=idx + 1))
         else:
             done = True
 
         if done:
             if meta_re.match(line):
-                errors.append(("STRAY-METADATA", "Metadata comments should start the file", path, idx + 1))
+                errors.append(rules.StrayMetadata.error(path, line_no=idx + 1))
             elif meta_re.search(line):
-                errors.append(("INDENTED-METADATA", "Metadata comments should start the line", path, idx + 1))
+                errors.append(rules.IndentedMetadata.error(path,
+                                                           line_no=idx + 1))
             elif broken_metadata.search(line):
-                errors.append(("BROKEN-METADATA", "Metadata comment is not formatted correctly", path, idx + 1))
+                errors.append(rules.BrokenMetadata.error(path, line_no=idx + 1))
 
     return errors
 
 
 def check_path(repo_root, path):
+    # type: (str, str) -> List[rules.Error]
     """
     Runs lints that check the file path.
 
@@ -730,6 +689,7 @@
 
 
 def check_all_paths(repo_root, paths):
+    # type: (str, List[str]) -> List[rules.Error]
     """
     Runs lints that check all paths globally.
 
@@ -745,6 +705,7 @@
 
 
 def check_file_contents(repo_root, path, f):
+    # type: (str, str, IO[bytes]) -> List[rules.Error]
     """
     Runs lints that check the file contents.
 
@@ -762,6 +723,8 @@
 
 
 def output_errors_text(errors):
+    # type: (List[rules.Error]) -> None
+    assert logger is not None
     for error_type, description, path, line_number in errors:
         pos_string = path
         if line_number:
@@ -770,8 +733,10 @@
 
 
 def output_errors_markdown(errors):
+    # type: (List[rules.Error]) -> None
     if not errors:
         return
+    assert logger is not None
     heading = """Got lint errors:
 
 | Error Type | Position | Message |
@@ -786,15 +751,18 @@
 
 
 def output_errors_json(errors):
+    # type: (List[rules.Error]) -> None
     for error_type, error, path, line_number in errors:
         print(json.dumps({"path": path, "lineno": line_number,
                           "rule": error_type, "message": error}))
 
 
 def output_error_count(error_count):
+    # type: (Dict[Text, int]) -> None
     if not error_count:
         return
 
+    assert logger is not None
     by_type = " ".join("%s: %d" % item for item in error_count.items())
     count = sum(error_count.values())
     logger.info("")
@@ -805,15 +773,17 @@
 
 
 def changed_files(wpt_root):
+    # type: (str) -> List[Text]
     revish = testfiles.get_revish(revish=None)
-    changed, _ = testfiles.files_changed(revish, set(), include_uncommitted=True, include_new=True)
+    changed, _ = testfiles.files_changed(revish, None, include_uncommitted=True, include_new=True)
     return [os.path.relpath(item, wpt_root) for item in changed]
 
 
 def lint_paths(kwargs, wpt_root):
-    if kwargs.get("paths"):
+    # type: (Dict[str, Any], str) -> List[str]
+    if kwargs.get(str("paths")):
         paths = []
-        for path in kwargs.get("paths"):
+        for path in kwargs.get(str("paths"), []):
             if os.path.isdir(path):
                 path_dir = list(all_filesystem_paths(wpt_root, path))
                 paths.extend(path_dir)
@@ -821,7 +791,7 @@
                 paths.append(os.path.relpath(os.path.abspath(path), wpt_root))
 
 
-    elif kwargs["all"]:
+    elif kwargs[str("all")]:
         paths = list(all_filesystem_paths(wpt_root))
     else:
         changed_paths = changed_files(wpt_root)
@@ -831,13 +801,14 @@
             if path == "lint.whitelist" or path.startswith("tools/lint/"):
                 force_all = True
                 break
-        paths = (list(changed_paths) if not force_all
+        paths = (list(changed_paths) if not force_all  # type: ignore
                  else list(all_filesystem_paths(wpt_root)))
 
     return paths
 
 
 def create_parser():
+    # type: () -> argparse.ArgumentParser
     parser = argparse.ArgumentParser()
     parser.add_argument("paths", nargs="*",
                         help="List of paths to lint")
@@ -854,15 +825,17 @@
 
 
 def main(**kwargs):
-    if kwargs.get("json") and kwargs.get("markdown"):
+    # type: (**Any) -> int
+    assert logger is not None
+    if kwargs.get(str("json")) and kwargs.get(str("markdown")):
         logger.critical("Cannot specify --json and --markdown")
         sys.exit(2)
 
-    repo_root = kwargs.get('repo_root') or localpaths.repo_root
-    output_format = {(True, False): "json",
-                     (False, True): "markdown",
-                     (False, False): "normal"}[(kwargs.get("json", False),
-                                                kwargs.get("markdown", False))]
+    repo_root = kwargs.get(str('repo_root')) or localpaths.repo_root
+    output_format = {(True, False): str("json"),
+                     (False, True): str("markdown"),
+                     (False, False): str("normal")}[(kwargs.get(str("json"), False),
+                                                     kwargs.get(str("markdown"), False))]
 
     if output_format == "markdown":
         setup_logging(True)
@@ -875,7 +848,8 @@
 
 
 def lint(repo_root, paths, output_format, ignore_glob):
-    error_count = defaultdict(int)
+    # type: (str, List[str], str, str) -> int
+    error_count = defaultdict(int)  # type: Dict[Text, int]
     last = None
 
     with open(os.path.join(repo_root, "lint.whitelist")) as f:
@@ -889,6 +863,7 @@
                      "normal": output_errors_text}[output_format]
 
     def process_errors(errors):
+        # type: (List[rules.Error]) -> Optional[Tuple[Text, Text]]
         """
         Filters and prints the errors, and updates the ``error_count`` object.
 
@@ -932,6 +907,8 @@
     if output_format in ("normal", "markdown"):
         output_error_count(error_count)
         if error_count:
+            assert last is not None
+            assert logger is not None
             for line in (ERROR_MSG % (last[0], last[1], last[0], last[1])).split("\n"):
                 logger.info(line)
     return sum(itervalues(error_count))
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/lint/rules.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/lint/rules.py
new file mode 100644
index 0000000..685bac34
--- /dev/null
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/lint/rules.py
@@ -0,0 +1,358 @@
+from __future__ import unicode_literals
+
+import abc
+import os
+import re
+
+import six
+
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from typing import Any, List, Match, Optional, Pattern, Text, Tuple, cast
+    Error = Tuple[Text, Text, Text, Optional[int]]
+
+
+class Rule(six.with_metaclass(abc.ABCMeta)):
+    @abc.abstractproperty
+    def name(self):
+        # type: () -> Text
+        pass
+
+    @abc.abstractproperty
+    def description(self):
+        # type: () -> Text
+        pass
+
+    to_fix = None  # type: Optional[Text]
+
+    @classmethod
+    def error(cls, path, context=(), line_no=None):
+        # type: (Text, Tuple[Any, ...], Optional[int]) -> Error
+        if MYPY:
+            name = cast(Text, cls.name)
+            description = cast(Text, cls.description)
+        else:
+            name = cls.name
+            description = cls.description
+        description = description % context
+        return (name, description, path, line_no)
+
+
+class MissingLink(Rule):
+    name = "MISSING-LINK"
+    description = "Testcase file must have a link to a spec"
+    to_fix = """
+        Ensure that there is a `<link rel="help" href="[url]">` for the spec.
+        `MISSING-LINK` is designed to ensure that the CSS build tool can find
+        the tests. Note that the CSS build system is primarily used by
+        [test.csswg.org/](http://test.csswg.org/), which doesn't use
+        `wptserve`, so `*.any.js` and similar tests won't work there; stick
+        with the `.html` equivalent.
+    """
+
+
+class PathLength(Rule):
+    name = "PATH LENGTH"
+    description = "/%s longer than maximum path length (%d > 150)"
+
+
+class WorkerCollision(Rule):
+    name = "WORKER COLLISION"
+    description = ("path ends with %s which collides with generated tests "
+        "from %s files")
+
+
+class GitIgnoreFile(Rule):
+    name = "GITIGNORE"
+    description = ".gitignore found outside the root"
+
+
+class AhemCopy(Rule):
+    name = "AHEM COPY"
+    description = "Don't add extra copies of Ahem, use /fonts/Ahem.ttf"
+
+
+# TODO: Add tests for this rule
+class IgnoredPath(Rule):
+    name = "IGNORED PATH"
+    description = ("%s matches an ignore filter in .gitignore - "
+        "please add a .gitignore exception")
+
+
+class CSSCollidingTestName(Rule):
+    name = "CSS-COLLIDING-TEST-NAME"
+    description = "The filename %s in the %s testsuite is shared by: %s"
+
+
+class CSSCollidingRefName(Rule):
+    name = "CSS-COLLIDING-REF-NAME"
+    description = "The filename %s is shared by: %s"
+
+
+class CSSCollidingSupportName(Rule):
+    name = "CSS-COLLIDING-SUPPORT-NAME"
+    description = "The filename %s is shared by: %s"
+
+
+class SupportWrongDir(Rule):
+    name = "SUPPORT-WRONG-DIR"
+    description = "Support file not in support directory"
+
+
+class ParseFailed(Rule):
+    name = "PARSE-FAILED"
+    description = "Unable to parse file"
+
+
+class ContentManual(Rule):
+    name = "CONTENT-MANUAL"
+    description = "Manual test whose filename doesn't end in '-manual'"
+
+
+class ContentVisual(Rule):
+    name = "CONTENT-VISUAL"
+    description = "Visual test whose filename doesn't end in '-visual'"
+
+
+class AbsoluteUrlRef(Rule):
+    name = "ABSOLUTE-URL-REF"
+    description = ("Reference test with a reference file specified via an "
+        "absolute URL: '%s'")
+
+
+class SameFileRef(Rule):
+    name = "SAME-FILE-REF"
+    description = "Reference test which points at itself as a reference"
+
+
+class NonexistentRef(Rule):
+    name = "NON-EXISTENT-REF"
+    description = ("Reference test with a non-existent '%s' relationship "
+        "reference: '%s'")
+
+
+class MultipleTimeout(Rule):
+    name = "MULTIPLE-TIMEOUT"
+    description = "More than one meta name='timeout'"
+
+
+class InvalidTimeout(Rule):
+    name = "INVALID-TIMEOUT"
+    description = "Invalid timeout value %s"
+
+
+class MultipleTestharness(Rule):
+    name = "MULTIPLE-TESTHARNESS"
+    description = "More than one <script src='/resources/testharness.js'>"
+
+
+class MissingTestharnessReport(Rule):
+    name = "MISSING-TESTHARNESSREPORT"
+    description = "Missing <script src='/resources/testharnessreport.js'>"
+
+
+class MultipleTestharnessReport(Rule):
+    name = "MULTIPLE-TESTHARNESSREPORT"
+    description = "More than one <script src='/resources/testharnessreport.js'>"
+
+
+class PresentTestharnessCSS(Rule):
+    name = "PRESENT-TESTHARNESSCSS"
+    description = "Explicit link to testharness.css present"
+
+
+class VariantMissing(Rule):
+    name = "VARIANT-MISSING"
+    description = "<meta name=variant> missing 'content' attribute"
+
+
+class MalformedVariant(Rule):
+    name = "MALFORMED-VARIANT"
+    description = ("%s <meta name=variant> 'content' attribute must be the "
+        "empty string or start with '?' or '#'")
+
+
+class LateTimeout(Rule):
+    name = "LATE-TIMEOUT"
+    description = "<meta name=timeout> seen after testharness.js script"
+
+
+class EarlyTestharnessReport(Rule):
+    name = "EARLY-TESTHARNESSREPORT"
+    description = "testharnessreport.js script seen before testharness.js script"
+
+
+class MultipleTestdriver(Rule):
+    name = "MULTIPLE-TESTDRIVER"
+    description = "More than one <script src='/resources/testdriver.js'>"
+
+
+class MissingTestdriverVendor(Rule):
+    name = "MISSING-TESTDRIVER-VENDOR"
+    description = "Missing <script src='/resources/testdriver-vendor.js'>"
+
+
+class MultipleTestdriverVendor(Rule):
+    name = "MULTIPLE-TESTDRIVER-VENDOR"
+    description = "More than one <script src='/resources/testdriver-vendor.js'>"
+
+
+class TestharnessPath(Rule):
+    name = "TESTHARNESS-PATH"
+    description = "testharness.js script seen with incorrect path"
+
+
+class TestharnessReportPath(Rule):
+    name = "TESTHARNESSREPORT-PATH"
+    description = "testharnessreport.js script seen with incorrect path"
+
+
+class TestdriverPath(Rule):
+    name = "TESTDRIVER-PATH"
+    description = "testdriver.js script seen with incorrect path"
+
+
+class TestdriverVendorPath(Rule):
+    name = "TESTDRIVER-VENDOR-PATH"
+    description = "testdriver-vendor.js script seen with incorrect path"
+
+
+class OpenNoMode(Rule):
+    name = "OPEN-NO-MODE"
+    description = "File opened without providing an explicit mode (note: binary files must be read with 'b' in the mode flags)"
+
+
+class UnknownGlobalMetadata(Rule):
+    name = "UNKNOWN-GLOBAL-METADATA"
+    description = "Unexpected value for global metadata"
+
+
+class BrokenGlobalMetadata(Rule):
+    name = "BROKEN-GLOBAL-METADATA"
+    description = "Invalid global metadata: %s"
+
+
+class UnknownTimeoutMetadata(Rule):
+    name = "UNKNOWN-TIMEOUT-METADATA"
+    description = "Unexpected value for timeout metadata"
+
+
+class UnknownMetadata(Rule):
+    name = "UNKNOWN-METADATA"
+    description = "Unexpected kind of metadata"
+
+
+class StrayMetadata(Rule):
+    name = "STRAY-METADATA"
+    description = "Metadata comments should start the file"
+
+
+class IndentedMetadata(Rule):
+    name = "INDENTED-METADATA"
+    description = "Metadata comments should start the line"
+
+
+class BrokenMetadata(Rule):
+    name = "BROKEN-METADATA"
+    description = "Metadata comment is not formatted correctly"
+
+
+class Regexp(six.with_metaclass(abc.ABCMeta)):
+    @abc.abstractproperty
+    def pattern(self):
+        # type: () -> bytes
+        pass
+
+    @abc.abstractproperty
+    def name(self):
+        # type: () -> Text
+        pass
+
+    @abc.abstractproperty
+    def description(self):
+        # type: () -> Text
+        pass
+
+    file_extensions = None  # type: Optional[List[Text]]
+
+    def __init__(self):
+        # type: () -> None
+        self._re = re.compile(self.pattern)  # type: Pattern[bytes]
+
+    def applies(self, path):
+        # type: (str) -> bool
+        return (self.file_extensions is None or
+                os.path.splitext(path)[1] in self.file_extensions)
+
+    def search(self, line):
+        # type: (bytes) -> Optional[Match[bytes]]
+        return self._re.search(line)
+
+
+class TabsRegexp(Regexp):
+    pattern = b"^\t"
+    name = "INDENT TABS"
+    description = "Tabs used for indentation"
+
+class CRRegexp(Regexp):
+    pattern = b"\r$"
+    name = "CR AT EOL"
+    description = "CR character in line separator"
+
+class SetTimeoutRegexp(Regexp):
+    pattern = br"setTimeout\s*\("
+    name = "SET TIMEOUT"
+    file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
+    description = "setTimeout used; step_timeout should typically be used instead"
+
+class W3CTestOrgRegexp(Regexp):
+    pattern = br"w3c\-test\.org"
+    name = "W3C-TEST.ORG"
+    description = "External w3c-test.org domain used"
+
+class WebPlatformTestRegexp(Regexp):
+    pattern = br"web\-platform\.test"
+    name = "WEB-PLATFORM.TEST"
+    description = "Internal web-platform.test domain used"
+
+class Webidl2Regexp(Regexp):
+    pattern = br"webidl2\.js"
+    name = "WEBIDL2.JS"
+    description = "Legacy webidl2.js script used"
+
+class ConsoleRegexp(Regexp):
+    pattern = br"console\.[a-zA-Z]+\s*\("
+    name = "CONSOLE"
+    file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
+    description = "Console logging API used"
+
+class GenerateTestsRegexp(Regexp):
+    pattern = br"generate_tests\s*\("
+    name = "GENERATE_TESTS"
+    file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
+    description = "generate_tests used"
+
+class PrintRegexp(Regexp):
+    pattern = br"print(?:\s|\s*\()"
+    name = "PRINT STATEMENT"
+    file_extensions = [".py"]
+    description = "Print function used"
+
+class LayoutTestsRegexp(Regexp):
+    pattern = br"(eventSender|testRunner|internals)\."
+    name = "LAYOUTTESTS APIS"
+    file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
+    description = "eventSender/testRunner/internals used; these are LayoutTests-specific APIs (WebKit/Blink)"
+
+class SpecialPowersRegexp(Regexp):
+    pattern = b"SpecialPowers"
+    name = "SPECIALPOWERS API"
+    file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
+    description = "SpecialPowers used; this is gecko-specific and not supported in wpt"
+
+class TrailingWhitespaceRegexp(Regexp):
+    name = "TRAILING WHITESPACE"
+    description = "Whitespace at EOL"
+    pattern = b"[ \t\f\v]$"
+    to_fix = """Remove trailing whitespace from all lines in the file."""
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/XMLParser.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/XMLParser.py
index 523f544..cc0a069 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/XMLParser.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/XMLParser.py
@@ -1,20 +1,32 @@
+import sys
 from os.path import dirname, join
 
 from collections import OrderedDict
 
 from xml.parsers import expat
-import xml.etree.ElementTree as etree
+import xml.etree.ElementTree as etree  # noqa: N813
+
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from typing import Dict
+    from typing import List
+    from typing import Optional
+    from typing import Text
+    from typing import Union
 
 _catalog = join(dirname(__file__), "catalog")
 
 def _wrap_error(e):
+    # type: (expat.error) -> etree.ParseError
     err = etree.ParseError(e)
-    err.code = e.code
-    err.position = e.lineno, e.offset
+    err.code = e.code  # type: ignore
+    err.position = e.lineno, e.offset  # type: ignore
     raise err
 
-_names = {}
+_names = {}  # type: Dict[str, str]
 def _fixname(key):
+    # type: (str) -> str
     try:
         name = _names[key]
     except KeyError:
@@ -25,6 +37,13 @@
     return name
 
 
+if sys.version_info[0:2] >= (3, 2):
+    _undefined_entity_code = expat.errors.codes[expat.errors.XML_ERROR_UNDEFINED_ENTITY]  # type: int
+else:
+    _codes = {expat.ErrorString(i): i for i in range(0x100)}  # type: Dict[str, int]
+    _undefined_entity_code = _codes[expat.errors.XML_ERROR_UNDEFINED_ENTITY]
+
+
 class XMLParser(object):
     """
     An XML parser with support for XHTML DTDs and all Python-supported encodings
@@ -35,11 +54,12 @@
     Python does, rather than just those supported by expat.
     """
     def __init__(self, encoding=None):
+        # type: (Optional[str]) -> None
         self._parser = expat.ParserCreate(encoding, "}")
         self._target = etree.TreeBuilder()
         # parser settings
-        self._parser.buffer_text = 1
-        self._parser.ordered_attributes = 1
+        self._parser.buffer_text = True
+        self._parser.ordered_attributes = True
         self._parser.SetParamEntityParsing(expat.XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE)
         # parser callbacks
         self._parser.XmlDeclHandler = self._xml_decl
@@ -47,30 +67,35 @@
         self._parser.EndElementHandler = self._end
         self._parser.CharacterDataHandler = self._data
         self._parser.ExternalEntityRefHandler = self._external
-        self._parser.SkippedEntityHandler = self._skipped
+        self._parser.SkippedEntityHandler = self._skipped  # type: ignore
         # used for our horrible re-encoding hack
-        self._fed_data = []
-        self._read_encoding = None
+        self._fed_data = []  # type: Optional[List[bytes]]
+        self._read_encoding = None  # type: Optional[str]
 
     def _xml_decl(self, version, encoding, standalone):
+        # type: (str, Optional[str], int) -> None
         self._read_encoding = encoding
 
     def _start(self, tag, attrib_in):
+        # type: (str, List[str]) -> etree.Element
         self._fed_data = None
         tag = _fixname(tag)
-        attrib = OrderedDict()
+        attrib = OrderedDict()  # type: Dict[Union[str, Text], Union[str, Text]]
         if attrib_in:
             for i in range(0, len(attrib_in), 2):
                 attrib[_fixname(attrib_in[i])] = attrib_in[i+1]
         return self._target.start(tag, attrib)
 
     def _data(self, text):
-        return self._target.data(text)
+        # type: (str) -> None
+        self._target.data(text)
 
     def _end(self, tag):
+        # type: (str) -> etree.Element
         return self._target.end(_fixname(tag))
 
     def _external(self, context, base, system_id, public_id):
+        # type: (str, Optional[str], Optional[str], Optional[str]) -> bool
         if public_id in {
                 "-//W3C//DTD XHTML 1.0 Transitional//EN",
                 "-//W3C//DTD XHTML 1.1//EN",
@@ -92,15 +117,17 @@
         return True
 
     def _skipped(self, name, is_parameter_entity):
+        # type: (str, bool) -> None
         err = expat.error("undefined entity %s: line %d, column %d" %
                           (name, self._parser.ErrorLineNumber,
                            self._parser.ErrorColumnNumber))
-        err.code = expat.errors.XML_ERROR_UNDEFINED_ENTITY
+        err.code = _undefined_entity_code
         err.lineno = self._parser.ErrorLineNumber
         err.offset = self._parser.ErrorColumnNumber
         raise err
 
     def feed(self, data):
+        # type: (str) -> None
         if self._fed_data is not None:
             self._fed_data.append(data)
         try:
@@ -110,6 +137,7 @@
         except ValueError as e:
             if e.args[0] == 'multi-byte encodings are not supported':
                 assert self._read_encoding is not None
+                assert self._fed_data is not None
                 xml = b"".join(self._fed_data).decode(self._read_encoding).encode("utf-8")
                 new_parser = XMLParser("utf-8")
                 self._parser = new_parser._parser
@@ -118,6 +146,7 @@
                 self.feed(xml)
 
     def close(self):
+        # type: () -> etree.Element
         try:
             self._parser.Parse("", True)
         except expat.error as v:
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/download.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/download.py
index 2e505f3..8f3e9d5 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/download.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/download.py
@@ -1,6 +1,7 @@
 from __future__ import absolute_import
 
 import argparse
+import bz2
 import gzip
 import json
 import io
@@ -9,10 +10,23 @@
 
 from six.moves.urllib.request import urlopen
 
-from .vcs import Git
+try:
+    import zstandard
+except ImportError:
+    zstandard = None
+
+from .utils import git
 
 from . import log
 
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from typing import Any
+    from typing import Callable
+    from typing import List
+    from typing import Optional
+
 here = os.path.dirname(__file__)
 
 wpt_root = os.path.abspath(os.path.join(here, os.pardir, os.pardir))
@@ -20,10 +34,12 @@
 
 
 def abs_path(path):
+    # type: (str) -> str
     return os.path.abspath(os.path.expanduser(path))
 
 
 def should_download(manifest_path, rebuild_time=timedelta(days=5)):
+    # type: (str, timedelta) -> bool
     if not os.path.exists(manifest_path):
         return True
     mtime = datetime.fromtimestamp(os.path.getmtime(manifest_path))
@@ -34,16 +50,36 @@
 
 
 def merge_pr_tags(repo_root, max_count=50):
-    git = Git.get_func(repo_root)
-    tags = []
-    for line in git("log", "--format=%D", "--max-count=%s" % max_count).split("\n"):
+    # type: (str, int) -> List[str]
+    gitfunc = git(repo_root)
+    tags = []  # type: List[str]
+    if gitfunc is None:
+        return tags
+    for line in gitfunc("log", "--format=%D", "--max-count=%s" % max_count).split("\n"):
         for ref in line.split(", "):
             if ref.startswith("tag: merge_pr_"):
                 tags.append(ref[5:])
     return tags
 
 
+def score_name(name):
+    # type: (str) -> Optional[int]
+    """Score how much we like each filename, lower wins, None rejects"""
+
+    # Accept both ways of naming the manfest asset, even though
+    # there's no longer a reason to include the commit sha.
+    if name.startswith("MANIFEST-") or name.startswith("MANIFEST."):
+        if zstandard and name.endswith("json.zst"):
+            return 1
+        if name.endswith(".json.bz2"):
+            return 2
+        if name.endswith(".json.gz"):
+            return 3
+    return None
+
+
 def github_url(tags):
+    # type: (List[str]) -> Optional[List[str]]
     for tag in tags:
         url = "https://api.github.com/repos/web-platform-tests/wpt/releases/tags/%s" % tag
         try:
@@ -62,50 +98,79 @@
             logger.warning("Response was not valid JSON")
             return None
 
+        candidates = []
         for item in release["assets"]:
-            # Accept both ways of naming the manfest asset, even though
-            # there's no longer a reason to include the commit sha.
-            if item["name"].startswith("MANIFEST-") and item["name"].endswith(".json.gz"):
-                return item["browser_download_url"]
-            elif item["name"] == "MANIFEST.json.gz":
-                return item["browser_download_url"]
+            score = score_name(item["name"])
+            if score is not None:
+                candidates.append((score, item["browser_download_url"]))
+
+        return [item[1] for item in sorted(candidates)]
 
     return None
 
 
-def download_manifest(manifest_path, tags_func, url_func, force=False):
+def download_manifest(
+        manifest_path,  # type: str
+        tags_func,  # type: Callable[[], List[str]]
+        url_func,  # type: Callable[[List[str]], Optional[List[str]]]
+        force=False  # type: bool
+):
+    # type: (...) -> bool
     if not force and not should_download(manifest_path):
         return False
 
     tags = tags_func()
 
-    url = url_func(tags)
-    if not url:
+    urls = url_func(tags)
+    if not urls:
         logger.warning("No generated manifest found")
         return False
 
-    logger.info("Downloading manifest from %s" % url)
-    try:
-        resp = urlopen(url)
-    except Exception:
-        logger.warning("Downloading pregenerated manifest failed")
-        return False
+    for url in urls:
+        logger.info("Downloading manifest from %s" % url)
+        try:
+            resp = urlopen(url)
+        except Exception:
+            logger.warning("Downloading pregenerated manifest failed")
+            continue
 
-    if resp.code != 200:
-        logger.warning("Downloading pregenerated manifest failed; got HTTP status %d" %
-                       resp.code)
-        return False
+        if resp.code != 200:
+            logger.warning("Downloading pregenerated manifest failed; got HTTP status %d" %
+                           resp.code)
+            continue
 
-    gzf = gzip.GzipFile(fileobj=io.BytesIO(resp.read()))
-
-    try:
-        decompressed = gzf.read()
-    except IOError:
-        logger.warning("Failed to decompress downloaded file")
+        if url.endswith(".zst"):
+            if not zstandard:
+                continue
+            try:
+                dctx = zstandard.ZstdDecompressor()
+                decompressed = dctx.decompress(resp.read())
+            except IOError:
+                logger.warning("Failed to decompress downloaded file")
+                continue
+        elif url.endswith(".bz2"):
+            try:
+                decompressed = bz2.decompress(resp.read())
+            except IOError:
+                logger.warning("Failed to decompress downloaded file")
+                continue
+        elif url.endswith(".gz"):
+            fileobj = io.BytesIO(resp.read())
+            try:
+                with gzip.GzipFile(fileobj=fileobj) as gzf:
+                    decompressed = gzf.read()  # type: ignore
+            except IOError:
+                logger.warning("Failed to decompress downloaded file")
+                continue
+        else:
+            logger.warning("Unknown file extension: %s" % url)
+            continue
+        break
+    else:
         return False
 
     try:
-        with open(manifest_path, "w") as f:
+        with open(manifest_path, "wb") as f:
             f.write(decompressed)
     except Exception:
         logger.warning("Failed to write manifest")
@@ -115,6 +180,7 @@
 
 
 def create_parser():
+    # type: () -> argparse.ArgumentParser
     parser = argparse.ArgumentParser()
     parser.add_argument(
         "-p", "--path", type=abs_path, help="Path to manifest file.")
@@ -127,11 +193,13 @@
 
 
 def download_from_github(path, tests_root, force=False):
+    # type: (str, str, bool) -> bool
     return download_manifest(path, lambda: merge_pr_tags(tests_root), github_url,
                              force=force)
 
 
 def run(**kwargs):
+    # type: (**Any) -> int
     if kwargs["path"] is None:
         path = os.path.join(kwargs["tests_root"], "MANIFEST.json")
     else:
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/item.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/item.py
index b6d33e8..ea0c9b1 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/item.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/item.py
@@ -1,9 +1,28 @@
 from copy import copy
-from six import iteritems
+from inspect import isabstract
+from six import iteritems, with_metaclass
 from six.moves.urllib.parse import urljoin, urlparse
 from abc import ABCMeta, abstractproperty
 
-item_types = {}
+from .utils import to_os_path
+
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from typing import Optional
+    from typing import Text
+    from typing import Dict
+    from typing import Tuple
+    from typing import List
+    from typing import Union
+    from typing import Type
+    from typing import Any
+    from typing import Sequence
+    from typing import Hashable
+    from .manifest import Manifest
+    Fuzzy = Dict[Optional[Tuple[Text, Text, Text]], List[int]]
+
+item_types = {}  # type: Dict[str, Type[ManifestItem]]
 
 
 class ManifestItemMeta(ABCMeta):
@@ -11,57 +30,84 @@
     item_types dictionary according to the value of their item_type
     attribute, and otherwise behaves like an ABCMeta."""
 
-    def __new__(cls, name, bases, attrs, **kwargs):
-        rv = ABCMeta.__new__(cls, name, bases, attrs, **kwargs)
-        if rv.item_type:
+    def __new__(cls, name, bases, attrs):
+        # type: (Type[ManifestItemMeta], str, Tuple[ManifestItemMeta, ...], Dict[str, Any]) -> type
+        rv = ABCMeta.__new__(cls, name, bases, attrs)
+        if not isabstract(rv):
+            assert issubclass(rv, ManifestItem)
+            assert isinstance(rv.item_type, str)
             item_types[rv.item_type] = rv
 
         return rv
 
 
-class ManifestItem(object):
-    __metaclass__ = ManifestItemMeta
-
+class ManifestItem(with_metaclass(ManifestItemMeta)):
     __slots__ = ("_tests_root", "path")
 
-    item_type = None
-
-    def __init__(self, tests_root=None, path=None):
+    def __init__(self, tests_root, path):
+        # type: (Text, Text) -> None
         self._tests_root = tests_root
         self.path = path
 
     @abstractproperty
     def id(self):
+        # type: () -> Hashable
         """The test's id (usually its url)"""
         pass
 
+    @abstractproperty
+    def item_type(self):
+        # type: () -> str
+        """The item's type"""
+        pass
+
     def key(self):
+        # type: () -> Hashable
         """A unique identifier for the test"""
         return (self.item_type, self.id)
 
     def __eq__(self, other):
+        # type: (Any) -> bool
         if not hasattr(other, "key"):
             return False
-        return self.key() == other.key()
+        return bool(self.key() == other.key())
 
     def __hash__(self):
+        # type: () -> int
         return hash(self.key())
 
     def __repr__(self):
+        # type: () -> str
         return "<%s.%s id=%s, path=%s>" % (self.__module__, self.__class__.__name__, self.id, self.path)
 
     def to_json(self):
-        return [{}]
+        # type: () -> Tuple[Any, ...]
+        return ()
 
     @classmethod
-    def from_json(cls, manifest, path, obj):
-        return cls(manifest.tests_root, path)
+    def from_json(cls,
+                  manifest,  # type: Manifest
+                  path,  # type: Text
+                  obj  # type: Any
+                  ):
+        # type: (...) -> ManifestItem
+        path = to_os_path(path)
+        tests_root = manifest.tests_root
+        assert tests_root is not None
+        return cls(tests_root, path)
 
 
 class URLManifestItem(ManifestItem):
     __slots__ = ("url_base", "_url", "_extras")
 
-    def __init__(self, tests_root, path, url_base, url, **extras):
+    def __init__(self,
+                 tests_root,  # type: Text
+                 path,  # type: Text
+                 url_base,  # type: Text
+                 url,  # type: Text
+                 **extras  # type: Any
+                 ):
+        # type: (...) -> None
         super(URLManifestItem, self).__init__(tests_root, path)
         assert url_base[0] == "/"
         self.url_base = url_base
@@ -71,10 +117,12 @@
 
     @property
     def id(self):
+        # type: () -> Text
         return self.url
 
     @property
     def url(self):
+        # type: () -> Text
         # we can outperform urljoin, because we know we just have path relative URLs
         if self.url_base == "/":
             return "/" + self._url
@@ -82,17 +130,27 @@
 
     @property
     def https(self):
+        # type: () -> bool
         flags = set(urlparse(self.url).path.rsplit("/", 1)[1].split(".")[1:-1])
         return ("https" in flags or "serviceworker" in flags)
 
     def to_json(self):
-        rv = [self._url, {}]
+        # type: () -> Tuple[Text, Dict[Any, Any]]
+        rv = (self._url, {})  # type: Tuple[Text, Dict[Any, Any]]
         return rv
 
     @classmethod
-    def from_json(cls, manifest, path, obj):
+    def from_json(cls,
+                  manifest,  # type: Manifest
+                  path,  # type: Text
+                  obj  # type: Tuple[Text, Dict[Any, Any]]
+                  ):
+        # type: (...) -> URLManifestItem
+        path = to_os_path(path)
         url, extras = obj
-        return cls(manifest.tests_root,
+        tests_root = manifest.tests_root
+        assert tests_root is not None
+        return cls(tests_root,
                    path,
                    manifest.url_base,
                    url,
@@ -100,25 +158,32 @@
 
 
 class TestharnessTest(URLManifestItem):
+    __slots__ = ()
+
     item_type = "testharness"
 
     @property
     def timeout(self):
+        # type: () -> Optional[Text]
         return self._extras.get("timeout")
 
     @property
     def testdriver(self):
+        # type: () -> Optional[Text]
         return self._extras.get("testdriver")
 
     @property
     def jsshell(self):
+        # type: () -> Optional[Text]
         return self._extras.get("jsshell")
 
     @property
     def script_metadata(self):
+        # type: () -> Optional[Text]
         return self._extras.get("script_metadata")
 
     def to_json(self):
+        # type: () -> Tuple[Text, Dict[Text, Any]]
         rv = super(TestharnessTest, self).to_json()
         if self.timeout is not None:
             rv[-1]["timeout"] = self.timeout
@@ -134,37 +199,56 @@
 class RefTestBase(URLManifestItem):
     __slots__ = ("references",)
 
-    item_type = "reftest_base"
-
-    def __init__(self, tests_root, path, url_base, url, references=None, **extras):
+    def __init__(self,
+                 tests_root,  # type: Text
+                 path,  # type: Text
+                 url_base,  # type: Text
+                 url,  # type: Text
+                 references=None,  # type: Optional[List[Tuple[Text, Text]]]
+                 **extras  # type: Any
+                 ):
         super(RefTestBase, self).__init__(tests_root, path, url_base, url, **extras)
         if references is None:
-            self.references = []
+            self.references = []  # type: List[Tuple[Text, Text]]
         else:
             self.references = references
 
     @property
     def timeout(self):
+        # type: () -> Optional[Text]
         return self._extras.get("timeout")
 
     @property
     def viewport_size(self):
+        # type: () -> Optional[Text]
         return self._extras.get("viewport_size")
 
     @property
     def dpi(self):
+        # type: () -> Optional[Text]
         return self._extras.get("dpi")
 
     @property
     def fuzzy(self):
-        rv = self._extras.get("fuzzy", [])
-        if isinstance(rv, list):
-            return {tuple(item[0]): item[1]
-                    for item in self._extras.get("fuzzy", [])}
+        # type: () -> Fuzzy
+        fuzzy = self._extras.get("fuzzy", {})  # type: Union[Fuzzy, List[Tuple[Optional[Sequence[Text]], List[int]]]]
+        if not isinstance(fuzzy, list):
+            return fuzzy
+
+        rv = {}  # type: Fuzzy
+        for k, v in fuzzy:  # type: Tuple[Optional[Sequence[Text]], List[int]]
+            if k is None:
+                key = None  # type: Optional[Tuple[Text, Text, Text]]
+            else:
+                # mypy types this as Tuple[Text, ...]
+                assert len(k) == 3
+                key = tuple(k)  # type: ignore
+            rv[key] = v
         return rv
 
-    def to_json(self):
-        rv = [self._url, self.references, {}]
+    def to_json(self):  # type: ignore
+        # type: () -> Tuple[Text, List[Tuple[Text, Text]], Dict[Text, Any]]
+        rv = (self._url, self.references, {})  # type: Tuple[Text, List[Tuple[Text, Text]], Dict[Text, Any]]
         extras = rv[-1]
         if self.timeout is not None:
             extras["timeout"] = self.timeout
@@ -177,9 +261,17 @@
         return rv
 
     @classmethod
-    def from_json(cls, manifest, path, obj):
+    def from_json(cls,  # type: ignore
+                  manifest,  # type: Manifest
+                  path,  # type: Text
+                  obj  # type: Tuple[Text, List[Tuple[Text, Text]], Dict[Any, Any]]
+                  ):
+        # type: (...) -> RefTestBase
+        tests_root = manifest.tests_root
+        assert tests_root is not None
+        path = to_os_path(path)
         url, references, extras = obj
-        return cls(manifest.tests_root,
+        return cls(tests_root,
                    path,
                    manifest.url_base,
                    url,
@@ -187,52 +279,74 @@
                    **extras)
 
     def to_RefTest(self):
+        # type: () -> RefTest
         if type(self) == RefTest:
+            assert isinstance(self, RefTest)
             return self
         rv = copy(self)
         rv.__class__ = RefTest
+        assert isinstance(rv, RefTest)
         return rv
 
     def to_RefTestNode(self):
+        # type: () -> RefTestNode
         if type(self) == RefTestNode:
+            assert isinstance(self, RefTestNode)
             return self
         rv = copy(self)
         rv.__class__ = RefTestNode
+        assert isinstance(rv, RefTestNode)
         return rv
 
 
 class RefTestNode(RefTestBase):
+    __slots__ = ()
+
     item_type = "reftest_node"
 
 
 class RefTest(RefTestBase):
+    __slots__ = ()
+
     item_type = "reftest"
 
 
 class ManualTest(URLManifestItem):
+    __slots__ = ()
+
     item_type = "manual"
 
 
 class ConformanceCheckerTest(URLManifestItem):
+    __slots__ = ()
+
     item_type = "conformancechecker"
 
 
 class VisualTest(URLManifestItem):
+    __slots__ = ()
+
     item_type = "visual"
 
 
 class Stub(URLManifestItem):
+    __slots__ = ()
+
     item_type = "stub"
 
 
 class WebDriverSpecTest(URLManifestItem):
+    __slots__ = ()
+
     item_type = "wdspec"
 
     @property
     def timeout(self):
+        # type: () -> Optional[Text]
         return self._extras.get("timeout")
 
     def to_json(self):
+        # type: () -> Tuple[Text, Dict[Text, Any]]
         rv = super(WebDriverSpecTest, self).to_json()
         if self.timeout is not None:
             rv[-1]["timeout"] = self.timeout
@@ -240,8 +354,11 @@
 
 
 class SupportFile(ManifestItem):
+    __slots__ = ()
+
     item_type = "support"
 
     @property
     def id(self):
+        # type: () -> Text
         return self.path
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/log.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/log.py
index 55ff6e8..9e2ad7408 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/log.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/log.py
@@ -4,6 +4,7 @@
 logger = logging.getLogger("manifest")
 
 def setup():
+    # type: () -> None
     logger.setLevel(logging.DEBUG)
     handler = logging.StreamHandler(sys.stdout)
     formatter = logging.Formatter(logging.BASIC_FORMAT, None)
@@ -11,4 +12,5 @@
     logger.addHandler(handler)
 
 def get_logger():
+    # type: () -> logging.Logger
     return logger
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/manifest.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/manifest.py
index bfe57c782..b5ebeed6 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/manifest.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/manifest.py
@@ -1,19 +1,40 @@
 import itertools
 import json
 import os
-from collections import defaultdict
+from collections import MutableMapping, defaultdict
 from six import iteritems, iterkeys, itervalues, string_types
 
 from . import vcs
-from .item import (ManualTest, WebDriverSpecTest, Stub, RefTestNode, RefTest,
-                   TestharnessTest, SupportFile, ConformanceCheckerTest, VisualTest)
+from .item import (ConformanceCheckerTest, ManifestItem, ManualTest, RefTest, RefTestNode, Stub,
+                   SupportFile, TestharnessTest, VisualTest, WebDriverSpecTest)
 from .log import get_logger
+from .sourcefile import SourceFile
 from .utils import from_os_path, to_os_path
 
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from logging import Logger
+    from typing import Any
+    from typing import Container
+    from typing import Dict
+    from typing import IO
+    from typing import Iterable
+    from typing import Iterator
+    from typing import List
+    from typing import Optional
+    from typing import Set
+    from typing import Text
+    from typing import Tuple
+    from typing import Type
+    from typing import Union
+    from typing import cast
+
 try:
-    import ujson as fast_json
+    import ujson
+    fast_json = ujson
 except ImportError:
-    fast_json = json
+    fast_json = json  # type: ignore
 
 CURRENT_VERSION = 6
 
@@ -26,13 +47,6 @@
     pass
 
 
-def iterfilter(filters, iter):
-    for f in filters:
-        iter = f(iter)
-    for item in iter:
-        yield item
-
-
 item_classes = {"testharness": TestharnessTest,
                 "reftest": RefTest,
                 "reftest_node": RefTestNode,
@@ -41,11 +55,17 @@
                 "wdspec": WebDriverSpecTest,
                 "conformancechecker": ConformanceCheckerTest,
                 "visual": VisualTest,
-                "support": SupportFile}
+                "support": SupportFile}  # type: Dict[str, Type[ManifestItem]]
 
 
-class TypeData(object):
-    def __init__(self, manifest, type_cls, meta_filters):
+if MYPY:
+    TypeDataType = MutableMapping[Text, Set[ManifestItem]]
+else:
+    TypeDataType = MutableMapping
+
+class TypeData(TypeDataType):
+    def __init__(self, manifest, type_cls):
+        # type: (Manifest, Type[ManifestItem]) -> None
         """Dict-like object containing the TestItems for each test type.
 
         Loading an actual Item class for each test is unnecessarily
@@ -58,26 +78,29 @@
         over the class."""
         self.manifest = manifest
         self.type_cls = type_cls
-        self.json_data = {}
-        self.tests_root = None
-        self.data = {}
-        self.meta_filters = meta_filters or []
+        self.json_data = {}  # type: Optional[Dict[Text, List[Any]]]
+        self.tests_root = None  # type: Optional[str]
+        self.data = {}  # type: Dict[Text, Set[ManifestItem]]
 
     def __getitem__(self, key):
-        if key not in self.data:
+        # type: (Text) -> Set[ManifestItem]
+        if key not in self.data and self.json_data is not None:
             self.load(key)
         return self.data[key]
 
     def __nonzero__(self):
+        # type: () -> bool
         return bool(self.data) or bool(self.json_data)
 
     def __len__(self):
+        # type: () -> int
         rv = len(self.data)
         if self.json_data is not None:
             rv += len(self.json_data)
         return rv
 
     def __delitem__(self, key):
+        # type: (Text) -> None
         if key in self.data:
             del self.data[key]
         elif self.json_data is not None:
@@ -86,6 +109,7 @@
             raise KeyError
 
     def __setitem__(self, key, value):
+        # type: (Text, Set[ManifestItem]) -> None
         if self.json_data is not None:
             path = from_os_path(key)
             if path in self.json_data:
@@ -93,48 +117,40 @@
         self.data[key] = value
 
     def __contains__(self, key):
+        # type: (Any) -> bool
         self.load_all()
         return key in self.data
 
     def __iter__(self):
+        # type: () -> Iterator[Text]
         self.load_all()
         return self.data.__iter__()
 
-    def pop(self, key, default=None):
-        try:
-            value = self[key]
-        except ValueError:
-            value = default
-        else:
-            del self.data[key]
-        return value
-
-    def get(self, key, default=None):
-        try:
-            return self[key]
-        except ValueError:
-            return default
-
     def itervalues(self):
+        # type: () -> Iterator[Set[ManifestItem]]
         self.load_all()
         return itervalues(self.data)
 
     def iteritems(self):
+        # type: () -> Iterator[Tuple[Text, Set[ManifestItem]]]
         self.load_all()
         return iteritems(self.data)
 
     def values(self):
-        return self.itervalues()
+        # type: () -> List[Set[ManifestItem]]
+        return list(self.itervalues())
 
     def items(self):
-        return self.iteritems()
+        # type: () -> List[Tuple[Text, Set[ManifestItem]]]
+        return list(self.iteritems())
 
     def load(self, key):
+        # type: (Text) -> None
         """Load a specific Item given a path"""
         if self.json_data is not None:
             data = set()
             path = from_os_path(key)
-            for test in iterfilter(self.meta_filters, self.json_data.get(path, [])):
+            for test in self.json_data.get(path, []):
                 manifest_item = self.type_cls.from_json(self.manifest, path, test)
                 data.add(manifest_item)
             try:
@@ -146,6 +162,7 @@
             raise ValueError
 
     def load_all(self):
+        # type: () -> None
         """Load all test items in this class"""
         if self.json_data is not None:
             for path, value in iteritems(self.json_data):
@@ -153,19 +170,21 @@
                 if key in self.data:
                     continue
                 data = set()
-                for test in iterfilter(self.meta_filters, self.json_data.get(path, [])):
+                for test in self.json_data.get(path, []):
                     manifest_item = self.type_cls.from_json(self.manifest, path, test)
                     data.add(manifest_item)
                 self.data[key] = data
             self.json_data = None
 
     def set_json(self, tests_root, data):
+        # type: (str, Dict[Text, Any]) -> None
         if not isinstance(data, dict):
             raise ValueError("Got a %s expected a dict" % (type(data)))
         self.tests_root = tests_root
         self.json_data = data
 
     def to_json(self):
+        # type: () -> Dict[Text, Any]
         data = {
             from_os_path(path):
             [t for t in sorted(test.to_json() for test in tests)]
@@ -181,64 +200,78 @@
         return data
 
     def paths(self):
+        # type: () -> Set[Text]
         """Get a list of all paths containing items of this type,
         without actually constructing all the items"""
         rv = set(iterkeys(self.data))
         if self.json_data:
-            rv |= set(to_os_path(item) for item in iterkeys(self.json_data))
+            rv |= {to_os_path(item) for item in iterkeys(self.json_data)}
         return rv
 
 
-class ManifestData(dict):
-    def __init__(self, manifest, meta_filters=None):
+if MYPY:
+    ManifestDataType = Dict[Any, TypeData]
+else:
+    ManifestDataType = dict
+
+class ManifestData(ManifestDataType):
+    def __init__(self, manifest):
+        # type: (Manifest) -> None
         """Dictionary subclass containing a TypeData instance for each test type,
         keyed by type name"""
-        self.initialized = False
+        self.initialized = False  # type: bool
         for key, value in iteritems(item_classes):
-            self[key] = TypeData(manifest, value, meta_filters=meta_filters)
+            self[key] = TypeData(manifest, value)
         self.initialized = True
-        self.json_obj = None
+        self.json_obj = None  # type: None
 
     def __setitem__(self, key, value):
+        # type: (str, TypeData) -> None
         if self.initialized:
             raise AttributeError
         dict.__setitem__(self, key, value)
 
     def paths(self):
+        # type: () -> Set[Text]
         """Get a list of all paths containing test items
         without actually constructing all the items"""
-        rv = set()
+        rv = set()  # type: Set[Text]
         for item_data in itervalues(self):
             rv |= set(item_data.paths())
         return rv
 
 
 class Manifest(object):
-    def __init__(self, tests_root=None, url_base="/", meta_filters=None):
+    def __init__(self, tests_root=None, url_base="/"):
+        # type: (Optional[str], Text) -> None
         assert url_base is not None
-        self._path_hash = {}
-        self._data = ManifestData(self, meta_filters)
-        self._reftest_nodes_by_url = None
-        self.tests_root = tests_root
-        self.url_base = url_base
+        self._path_hash = {}  # type: Dict[Text, Tuple[Text, Text]]
+        self._data = ManifestData(self)  # type: ManifestData
+        self._reftest_nodes_by_url = None  # type: Optional[Dict[Text, Union[RefTest, RefTestNode]]]
+        self.tests_root = tests_root  # type: Optional[str]
+        self.url_base = url_base  # type: Text
 
     def __iter__(self):
+        # type: () -> Iterable[Tuple[str, Text, Set[ManifestItem]]]
         return self.itertypes()
 
     def itertypes(self, *types):
-        if not types:
-            types = sorted(self._data.keys())
-        for item_type in types:
+        # type: (*str) -> Iterable[Tuple[str, Text, Set[ManifestItem]]]
+        for item_type in (types or sorted(self._data.keys())):
             for path in sorted(self._data[item_type]):
                 tests = self._data[item_type][path]
                 yield item_type, path, tests
 
     def iterpath(self, path):
+        # type: (Text) -> Iterable[ManifestItem]
         for type_tests in self._data.values():
-            for test in type_tests.get(path, set()):
+            i = type_tests.get(path, set())
+            assert i is not None
+            for test in i:
                 yield test
 
     def iterdir(self, dir_name):
+        # type: (Text) -> Iterable[ManifestItem]
         if not dir_name.endswith(os.path.sep):
             dir_name = dir_name + os.path.sep
         for type_tests in self._data.values():
@@ -249,19 +282,23 @@
 
     @property
     def reftest_nodes_by_url(self):
+        # type: () -> Dict[Text, Union[RefTest, RefTestNode]]
         if self._reftest_nodes_by_url is None:
             by_url = {}
-            for path, nodes in itertools.chain(iteritems(self._data.get("reftest", {})),
-                                               iteritems(self._data.get("reftest_node", {}))):
+            for path, nodes in itertools.chain(iteritems(self._data["reftest"]),
+                                               iteritems(self._data["reftest_node"])):
                 for node in nodes:
+                    assert isinstance(node, (RefTest, RefTestNode))
                     by_url[node.url] = node
             self._reftest_nodes_by_url = by_url
         return self._reftest_nodes_by_url
 
     def get_reference(self, url):
+        # type: (Text) -> Optional[ManifestItem]
         return self.reftest_nodes_by_url.get(url)
 
     def update(self, tree):
+        # type: (Iterable[Tuple[Union[SourceFile, bytes], bool]]) -> bool
         """Update the manifest given an iterable of items that make up the updated manifest.
 
         The iterable must either generate tuples of the form (SourceFile, True) for paths
@@ -269,38 +306,40 @@
         unusual API is designed as an optimistaion meaning that SourceFile items need not be
         constructed in the case we are not updating a path, but the absence of an item from
         the iterator may be used to remove defunct entries from the manifest."""
-        reftest_nodes = []
-        seen_files = set()
+        all_reftest_nodes = []  # type: List[Tuple[ManifestItem, Text]]
+        seen_files = set()  # type: Set[Text]
 
         changed = False
         reftest_changes = False
 
         # Create local variable references to these dicts so we avoid the
         # attribute access in the hot loop below
-        path_hash = self._path_hash
+        path_hash = self._path_hash  # type: Dict[Text, Tuple[Text, Text]]
         data = self._data
 
-        prev_files = data.paths()
+        prev_files = data.paths()  # type: Set[Text]
 
         reftest_types = ("reftest", "reftest_node")
 
         for source_file, update in tree:
             if not update:
-                rel_path = source_file
+                assert isinstance(source_file, (bytes, str))
+                rel_path = source_file  # type: Text
                 seen_files.add(rel_path)
                 assert rel_path in path_hash
-                old_hash, old_type = path_hash[rel_path]
+                old_hash, old_type = path_hash[rel_path]  # type: Tuple[Text, Text]
                 if old_type in reftest_types:
-                    manifest_items = data[old_type][rel_path]
-                    reftest_nodes.extend((item, old_hash) for item in manifest_items)
+                    manifest_items = data[old_type][rel_path]  # type: Iterable[ManifestItem]
+                    all_reftest_nodes.extend((item, old_hash) for item in manifest_items)
             else:
+                assert not isinstance(source_file, bytes)
                 rel_path = source_file.rel_path
                 seen_files.add(rel_path)
 
-                file_hash = source_file.hash
+                file_hash = source_file.hash  # type: Text
 
-                is_new = rel_path not in path_hash
-                hash_changed = False
+                is_new = rel_path not in path_hash  # type: bool
+                hash_changed = False  # type: bool
 
                 if not is_new:
                     old_hash, old_type = path_hash[rel_path]
@@ -319,7 +358,7 @@
                     new_type, manifest_items = source_file.manifest_items()
 
                 if new_type in reftest_types:
-                    reftest_nodes.extend((item, file_hash) for item in manifest_items)
+                    all_reftest_nodes.extend((item, file_hash) for item in manifest_items)
                     if is_new or hash_changed:
                         reftest_changes = True
                 elif is_new or hash_changed:
@@ -348,43 +387,66 @@
                             del test_data[rel_path]
 
         if reftest_changes:
-            reftests, reftest_nodes, changed_hashes = self._compute_reftests(reftest_nodes)
-            data["reftest"].data = reftests
-            data["reftest_node"].data = reftest_nodes
+            reftests, reftest_nodes, changed_hashes = self._compute_reftests(all_reftest_nodes)
+            reftest_data = data["reftest"]
+            reftest_data.clear()
+            for path, items in iteritems(reftests):
+                if MYPY:
+                    reftest_data[path] = cast(Set[ManifestItem], items)
+                else:
+                    reftest_data[path] = items
+
+            reftest_node_data = data["reftest_node"]
+            reftest_node_data.clear()
+            for node_path, node_items in iteritems(reftest_nodes):
+                if MYPY:
+                    reftest_node_data[node_path] = cast(Set[ManifestItem], node_items)
+                else:
+                    reftest_node_data[node_path] = node_items
+
             path_hash.update(changed_hashes)
 
         return changed
 
-    def _compute_reftests(self, reftest_nodes):
+    def _compute_reftests(self,
+                          reftest_nodes  # type: List[Tuple[ManifestItem, Text]]
+                          ):
+        # type: (...) -> Tuple[Dict[Text, Set[RefTest]], Dict[Text, Set[RefTestNode]], Dict[Text, Tuple[Text, Text]]]
         self._reftest_nodes_by_url = {}
         has_inbound = set()
         for item, _ in reftest_nodes:
+            assert isinstance(item, (RefTestNode, RefTest))
             for ref_url, ref_type in item.references:
                 has_inbound.add(ref_url)
 
-        reftests = defaultdict(set)
-        references = defaultdict(set)
-        changed_hashes = {}
+        reftests = defaultdict(set)  # type: Dict[Text, Set[RefTest]]
+        references = defaultdict(set)  # type: Dict[Text, Set[RefTestNode]]
+        changed_hashes = {}  # type: Dict[Text, Tuple[Text, Text]]
 
         for item, file_hash in reftest_nodes:
+            assert isinstance(item, (RefTestNode, RefTest))
             if item.url in has_inbound:
                 # This is a reference
                 if isinstance(item, RefTest):
                     item = item.to_RefTestNode()
                     changed_hashes[item.path] = (file_hash,
                                                  item.item_type)
+                assert isinstance(item, RefTestNode)
                 references[item.path].add(item)
             else:
                 if isinstance(item, RefTestNode):
                     item = item.to_RefTest()
                     changed_hashes[item.path] = (file_hash,
                                                  item.item_type)
+                assert isinstance(item, RefTest)
                 reftests[item.path].add(item)
+            assert isinstance(item, (RefTestNode, RefTest))
             self._reftest_nodes_by_url[item.url] = item
 
         return reftests, references, changed_hashes
 
     def to_json(self):
+        # type: () -> Dict[Text, Any]
         out_items = {
             test_type: type_paths.to_json()
             for test_type, type_paths in iteritems(self._data) if type_paths
@@ -392,16 +454,17 @@
         rv = {"url_base": self.url_base,
               "paths": {from_os_path(k): v for k, v in iteritems(self._path_hash)},
               "items": out_items,
-              "version": CURRENT_VERSION}
+              "version": CURRENT_VERSION}  # type: Dict[Text, Any]
         return rv
 
     @classmethod
-    def from_json(cls, tests_root, obj, types=None, meta_filters=None):
+    def from_json(cls, tests_root, obj, types=None):
+        # type: (str, Dict[Text, Any], Optional[Container[Text]]) -> Manifest
         version = obj.get("version")
         if version != CURRENT_VERSION:
             raise ManifestVersionMismatch
 
-        self = cls(tests_root, url_base=obj.get("url_base", "/"), meta_filters=meta_filters)
+        self = cls(tests_root, url_base=obj.get("url_base", "/"))
         if not hasattr(obj, "items") and hasattr(obj, "paths"):
             raise ManifestError
 
@@ -419,18 +482,24 @@
         return self
 
 
-def load(tests_root, manifest, types=None, meta_filters=None):
+def load(tests_root, manifest, types=None):
+    # type: (str, Union[IO[bytes], str], Optional[Container[Text]]) -> Optional[Manifest]
     logger = get_logger()
 
     logger.warning("Prefer load_and_update instead")
-    return _load(logger, tests_root, manifest, types, meta_filters)
+    return _load(logger, tests_root, manifest, types)
 
 
-__load_cache = {}
+__load_cache = {}  # type: Dict[str, Manifest]
 
 
-def _load(logger, tests_root, manifest, types=None, meta_filters=None, allow_cached=True):
-    # "manifest" is a path or file-like object.
+def _load(logger,  # type: Logger
+          tests_root,  # type: str
+          manifest,  # type: Union[IO[bytes], str]
+          types=None,  # type: Optional[Container[Text]]
+          allow_cached=True  # type: bool
+          ):
+    # type: (...) -> Optional[Manifest]
     manifest_path = (manifest if isinstance(manifest, string_types)
                      else manifest.name)
     if allow_cached and manifest_path in __load_cache:
@@ -442,11 +511,10 @@
         else:
             logger.debug("Creating new manifest at %s" % manifest)
         try:
-            with open(manifest) as f:
+            with open(manifest, "rb") as f:
                 rv = Manifest.from_json(tests_root,
                                         fast_json.load(f),
-                                        types=types,
-                                        meta_filters=meta_filters)
+                                        types=types)
         except IOError:
             return None
         except ValueError:
@@ -455,26 +523,26 @@
     else:
         rv = Manifest.from_json(tests_root,
                                 fast_json.load(manifest),
-                                types=types,
-                                meta_filters=meta_filters)
+                                types=types)
 
     if allow_cached:
         __load_cache[manifest_path] = rv
     return rv
 
 
-def load_and_update(tests_root,
-                    manifest_path,
-                    url_base,
-                    update=True,
-                    rebuild=False,
-                    metadata_path=None,
-                    cache_root=None,
-                    working_copy=False,
-                    types=None,
-                    meta_filters=None,
-                    write_manifest=True,
-                    allow_cached=True):
+def load_and_update(tests_root,  # type: bytes
+                    manifest_path,  # type: bytes
+                    url_base,  # type: Text
+                    update=True,  # type: bool
+                    rebuild=False,  # type: bool
+                    metadata_path=None,  # type: Optional[bytes]
+                    cache_root=None,  # type: Optional[bytes]
+                    working_copy=True,  # type: bool
+                    types=None,  # type: Optional[Container[Text]]
+                    write_manifest=True,  # type: bool
+                    allow_cached=True  # type: bool
+                    ):
+    # type: (...) -> Manifest
     logger = get_logger()
 
     manifest = None
@@ -484,19 +552,20 @@
                              tests_root,
                              manifest_path,
                              types=types,
-                             meta_filters=meta_filters,
                              allow_cached=allow_cached)
         except ManifestVersionMismatch:
             logger.info("Manifest version changed, rebuilding")
 
         if manifest is not None and manifest.url_base != url_base:
             logger.info("Manifest url base did not match, rebuilding")
+            manifest = None
 
     if manifest is None:
-        manifest = Manifest(tests_root, url_base, meta_filters=meta_filters)
+        manifest = Manifest(tests_root, url_base)
+        rebuild = True
         update = True
 
-    if update:
+    if rebuild or update:
         tree = vcs.get_tree(tests_root, manifest, manifest_path, cache_root,
                             working_copy, rebuild)
         changed = manifest.update(tree)
@@ -508,6 +577,7 @@
 
 
 def write(manifest, manifest_path):
+    # type: (Manifest, bytes) -> None
     dir_name = os.path.dirname(manifest_path)
     if not os.path.exists(dir_name):
         os.makedirs(dir_name)
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/sourcefile.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/sourcefile.py
index 97fe89bb..a34153d 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/sourcefile.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/sourcefile.py
@@ -2,29 +2,51 @@
 import re
 import os
 from collections import deque
-from six import binary_type
+from six import binary_type, PY3
 from six.moves.urllib.parse import urljoin
 from fnmatch import fnmatch
+
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from typing import Any
+    from typing import AnyStr
+    from typing import BinaryIO
+    from typing import Callable
+    from typing import Deque
+    from typing import Dict
+    from typing import Iterable
+    from typing import List
+    from typing import Optional
+    from typing import Pattern
+    from typing import Set
+    from typing import Text
+    from typing import Tuple
+    from typing import Union
+    from typing import cast
+
 try:
     from xml.etree import cElementTree as ElementTree
 except ImportError:
-    from xml.etree import ElementTree
+    from xml.etree import ElementTree as ElementTree  # type: ignore
 
 import html5lib
 
 from . import XMLParser
-from .item import Stub, ManualTest, WebDriverSpecTest, RefTestNode, TestharnessTest, SupportFile, ConformanceCheckerTest, VisualTest
+from .item import (ManifestItem, Stub, ManualTest, WebDriverSpecTest, RefTestNode, TestharnessTest,
+                   SupportFile, ConformanceCheckerTest, VisualTest)
 from .utils import ContextManagerBytesIO, cached_property
 
 wd_pattern = "*.py"
-js_meta_re = re.compile(b"//\s*META:\s*(\w*)=(.*)$")
-python_meta_re = re.compile(b"#\s*META:\s*(\w*)=(.*)$")
+js_meta_re = re.compile(br"//\s*META:\s*(\w*)=(.*)$")
+python_meta_re = re.compile(br"#\s*META:\s*(\w*)=(.*)$")
 
 reference_file_re = re.compile(r'(^|[\-_])(not)?ref[0-9]*([\-_]|$)')
 
-space_chars = u"".join(html5lib.constants.spaceCharacters)
+space_chars = u"".join(html5lib.constants.spaceCharacters)  # type: Text
 
 def replace_end(s, old, new):
+    # type: (Text, Text, Text) -> Text
     """
     Given a string `s` that ends with `old`, replace that occurrence of `old`
     with `new`.
@@ -34,6 +56,7 @@
 
 
 def read_script_metadata(f, regexp):
+    # type: (BinaryIO, Pattern[bytes]) -> Iterable[Tuple[bytes, bytes]]
     """
     Yields any metadata (pairs of bytestrings) from the file-like object `f`,
     as specified according to a supplied regexp.
@@ -58,10 +81,11 @@
     b"dedicatedworker": {"suffix": ".any.worker.html"},
     b"worker": {"longhand": {b"dedicatedworker", b"sharedworker", b"serviceworker"}},
     b"jsshell": {"suffix": ".any.js"},
-}
+}  # type: Dict[bytes, Dict[str, Any]]
 
 
 def get_any_variants(item):
+    # type: (bytes) -> Set[bytes]
     """
     Returns a set of variants (bytestrings) defined by the given keyword.
     """
@@ -76,6 +100,7 @@
 
 
 def get_default_any_variants():
+    # type: () -> Set[bytes]
     """
     Returns a set of variants (bytestrings) that will be used by default.
     """
@@ -83,6 +108,7 @@
 
 
 def parse_variants(value):
+    # type: (bytes) -> Set[bytes]
     """
     Returns a set of variants (bytestrings) defined by a comma-separated value.
     """
@@ -101,6 +127,7 @@
 
 
 def global_suffixes(value):
+    # type: (bytes) -> Set[Tuple[bytes, bool]]
     """
     Yields tuples of the relevant filename suffix (a string) and whether the
     variant is intended to run in a JS shell, for the variants defined by the
@@ -120,6 +147,7 @@
 
 
 def global_variant_url(url, suffix):
+    # type: (Text, Text) -> Text
     """
     Returns a url created from the given url and suffix (all strings).
     """
@@ -131,32 +159,41 @@
     return replace_end(url, ".js", suffix)
 
 
+def _parse_html(f):
+    # type: (BinaryIO) -> ElementTree.ElementTree
+    doc = html5lib.parse(f, treebuilder="etree", useChardet=False)
+    if MYPY:
+        return cast(ElementTree.ElementTree, doc)
+    return doc
+
 def _parse_xml(f):
+    # type: (BinaryIO) -> ElementTree.ElementTree
     try:
         # raises ValueError with an unsupported encoding,
         # ParseError when there's an undefined entity
         return ElementTree.parse(f)
     except (ValueError, ElementTree.ParseError):
         f.seek(0)
-        return ElementTree.parse(f, XMLParser.XMLParser())
+        return ElementTree.parse(f, XMLParser.XMLParser())  # type: ignore
 
 
 class SourceFile(object):
-    parsers = {"html":lambda x:html5lib.parse(x, treebuilder="etree", useChardet=False),
+    parsers = {"html":_parse_html,
                "xhtml":_parse_xml,
-               "svg":_parse_xml}
+               "svg":_parse_xml}  # type: Dict[Text, Callable[[BinaryIO], ElementTree.ElementTree]]
 
-    root_dir_non_test = set(["common"])
+    root_dir_non_test = {"common"}
 
-    dir_non_test = set(["resources",
-                        "support",
-                        "tools"])
+    dir_non_test = {"resources",
+                    "support",
+                    "tools"}
 
     dir_path_non_test = {("css21", "archive"),
                          ("css", "CSS2", "archive"),
-                         ("css", "common")}
+                         ("css", "common")}  # type: Set[Tuple[bytes, ...]]
 
     def __init__(self, tests_root, rel_path, url_base, hash=None, contents=None):
+        # type: (AnyStr, AnyStr, Text, Optional[bytes], Optional[bytes]) -> None
         """Object representing a file in a source tree.
 
         :param tests_root: Path to the root of the source tree
@@ -167,31 +204,37 @@
 
         assert not os.path.isabs(rel_path), rel_path
 
-        self.tests_root = tests_root
         if os.name == "nt":
             # do slash normalization on Windows
             if isinstance(rel_path, binary_type):
-                self.rel_path = rel_path.replace(b"/", b"\\")
+                rel_path = rel_path.replace(b"/", b"\\")
             else:
-                self.rel_path = rel_path.replace(u"/", u"\\")
-        else:
-            self.rel_path = rel_path
+                rel_path = rel_path.replace(u"/", u"\\")
+
+        dir_path, filename = os.path.split(rel_path)
+        name, ext = os.path.splitext(filename)
+
+        type_flag = None
+        if "-" in name:
+            type_flag = name.rsplit("-", 1)[1].split(".")[0]
+
+        meta_flags = name.split(".")[1:]
+
+        self.tests_root = tests_root  # type: Union[bytes, Text]
+        self.rel_path = rel_path  # type: Union[bytes, Text]
+        self.dir_path = dir_path  # type: Union[bytes, Text]
+        self.filename = filename  # type: Union[bytes, Text]
+        self.name = name  # type: Union[bytes, Text]
+        self.ext = ext  # type: Union[bytes, Text]
+        self.type_flag = type_flag  # type: Optional[Union[bytes, Text]]
+        self.meta_flags = meta_flags  # type: Union[List[bytes], List[Text]]
         self.url_base = url_base
         self.contents = contents
-
-        self.dir_path, self.filename = os.path.split(self.rel_path)
-        self.name, self.ext = os.path.splitext(self.filename)
-
-        self.type_flag = None
-        if "-" in self.name:
-            self.type_flag = self.name.rsplit("-", 1)[1].split(".")[0]
-
-        self.meta_flags = self.name.split(".")[1:]
-
-        self.items_cache = None
+        self.items_cache = None  # type: Optional[Tuple[Text, List[ManifestItem]]]
         self._hash = hash
 
     def __getstate__(self):
+        # type: () -> Dict[str, Any]
         # Remove computed properties if we pickle this class
         rv = self.__dict__.copy()
 
@@ -204,12 +247,14 @@
         return rv
 
     def name_prefix(self, prefix):
+        # type: (bytes) -> bool
         """Check if the filename starts with a given prefix
 
         :param prefix: The prefix to check"""
         return self.name.startswith(prefix)
 
     def is_dir(self):
+        # type: () -> bool
         """Return whether this file represents a directory."""
         if self.contents is not None:
             return False
@@ -217,6 +262,7 @@
         return os.path.isdir(self.rel_path)
 
     def open(self):
+        # type: () -> BinaryIO
         """
         Return either
         * the contents specified in the constructor, if any;
@@ -224,34 +270,49 @@
         """
 
         if self.contents is not None:
-            file_obj = ContextManagerBytesIO(self.contents)
+            wrapped = ContextManagerBytesIO(self.contents)
+            if MYPY:
+                file_obj = cast(BinaryIO, wrapped)
+            else:
+                file_obj = wrapped
         else:
             file_obj = open(self.path, 'rb')
         return file_obj
 
     @cached_property
     def path(self):
+        # type: () -> Union[bytes, Text]
         return os.path.join(self.tests_root, self.rel_path)
 
     @cached_property
     def rel_url(self):
+        # type: () -> Text
         assert not os.path.isabs(self.rel_path), self.rel_path
         return self.rel_path.replace(os.sep, "/")
 
     @cached_property
     def url(self):
+        # type: () -> Text
         return urljoin(self.url_base, self.rel_url)
 
     @cached_property
     def hash(self):
+        # type: () -> bytes
         if not self._hash:
             with self.open() as f:
                 content = f.read()
-            data = "".join(("blob ", str(len(content)), "\0", content))
-            self._hash = hashlib.sha1(data).hexdigest()
+
+            data = b"".join((b"blob ", b"%d" % len(content), b"\0", content))
+            hash_str = hashlib.sha1(data).hexdigest()  # type: str
+            if PY3:
+                self._hash = hash_str.encode("ascii")
+            else:
+                self._hash = hash_str
+
         return self._hash
 
     def in_non_test_dir(self):
+        # type: () -> bool
         if self.dir_path == "":
             return True
 
@@ -264,11 +325,13 @@
         return False
 
     def in_conformance_checker_dir(self):
+        # type: () -> bool
         return (self.dir_path == "conformance-checkers" or
                 self.dir_path.startswith("conformance-checkers" + os.path.sep))
 
     @property
     def name_is_non_test(self):
+        # type: () -> bool
         """Check if the file name matches the conditions for the file to
         be a non-test file"""
         return (self.is_dir() or
@@ -281,51 +344,60 @@
 
     @property
     def name_is_conformance(self):
+        # type: () -> bool
         return (self.in_conformance_checker_dir() and
                 self.type_flag in ("is-valid", "no-valid"))
 
     @property
     def name_is_conformance_support(self):
+        # type: () -> bool
         return self.in_conformance_checker_dir()
 
     @property
     def name_is_stub(self):
+        # type: () -> bool
         """Check if the file name matches the conditions for the file to
         be a stub file"""
         return self.name_prefix("stub-")
 
     @property
     def name_is_manual(self):
+        # type: () -> bool
         """Check if the file name matches the conditions for the file to
         be a manual test file"""
         return self.type_flag == "manual"
 
     @property
     def name_is_visual(self):
+        # type: () -> bool
         """Check if the file name matches the conditions for the file to
         be a visual test file"""
         return self.type_flag == "visual"
 
     @property
     def name_is_multi_global(self):
+        # type: () -> bool
         """Check if the file name matches the conditions for the file to
         be a multi-global js test file"""
         return "any" in self.meta_flags and self.ext == ".js"
 
     @property
     def name_is_worker(self):
+        # type: () -> bool
         """Check if the file name matches the conditions for the file to
         be a worker js test file"""
         return "worker" in self.meta_flags and self.ext == ".js"
 
     @property
     def name_is_window(self):
+        # type: () -> bool
         """Check if the file name matches the conditions for the file to
         be a window js test file"""
         return "window" in self.meta_flags and self.ext == ".js"
 
     @property
     def name_is_webdriver(self):
+        # type: () -> bool
         """Check if the file name matches the conditions for the file to
         be a webdriver spec test file"""
         # wdspec tests are in subdirectories of /webdriver excluding __init__.py
@@ -339,12 +411,14 @@
 
     @property
     def name_is_reference(self):
+        # type: () -> bool
         """Check if the file name matches the conditions for the file to
         be a reference file (not a reftest)"""
         return "/reference/" in self.url or bool(reference_file_re.search(self.name))
 
     @property
     def markup_type(self):
+        # type: () -> Optional[Text]
         """Return the type of markup contained in a file, based on its extension,
         or None if it doesn't contain markup"""
         ext = self.ext
@@ -363,6 +437,7 @@
 
     @cached_property
     def root(self):
+        # type: () -> Optional[Union[ElementTree.Element, ElementTree.ElementTree]]
         """Return an ElementTree Element for the root node of the file if it contains
         markup, or None if it does not"""
         if not self.markup_type:
@@ -377,7 +452,7 @@
                 return None
 
         if hasattr(tree, "getroot"):
-            root = tree.getroot()
+            root = tree.getroot()  # type: Union[ElementTree.Element, ElementTree.ElementTree]
         else:
             root = tree
 
@@ -385,12 +460,15 @@
 
     @cached_property
     def timeout_nodes(self):
+        # type: () -> List[ElementTree.Element]
         """List of ElementTree Elements corresponding to nodes in a test that
         specify timeouts"""
+        assert self.root is not None
         return self.root.findall(".//{http://www.w3.org/1999/xhtml}meta[@name='timeout']")
 
     @cached_property
     def script_metadata(self):
+        # type: () -> Optional[List[Tuple[bytes, bytes]]]
         if self.name_is_worker or self.name_is_multi_global or self.name_is_window:
             regexp = js_meta_re
         elif self.name_is_webdriver:
@@ -403,6 +481,7 @@
 
     @cached_property
     def timeout(self):
+        # type: () -> Optional[Text]
         """The timeout of a test or reference file. "long" if the file has an extended timeout
         or None otherwise"""
         if self.script_metadata:
@@ -413,7 +492,7 @@
             return None
 
         if self.timeout_nodes:
-            timeout_str = self.timeout_nodes[0].attrib.get("content", None)
+            timeout_str = self.timeout_nodes[0].attrib.get("content", None)  # type: Optional[Text]
             if timeout_str and timeout_str.lower() == "long":
                 return "long"
 
@@ -421,12 +500,15 @@
 
     @cached_property
     def viewport_nodes(self):
+        # type: () -> List[ElementTree.Element]
         """List of ElementTree Elements corresponding to nodes in a test that
         specify viewport sizes"""
+        assert self.root is not None
         return self.root.findall(".//{http://www.w3.org/1999/xhtml}meta[@name='viewport-size']")
 
     @cached_property
     def viewport_size(self):
+        # type: () -> Optional[Text]
         """The viewport size of a test or reference file"""
         if self.root is None:
             return None
@@ -438,12 +520,15 @@
 
     @cached_property
     def dpi_nodes(self):
+        # type: () -> List[ElementTree.Element]
         """List of ElementTree Elements corresponding to nodes in a test that
         specify device pixel ratios"""
+        assert self.root is not None
         return self.root.findall(".//{http://www.w3.org/1999/xhtml}meta[@name='device-pixel-ratio']")
 
     @cached_property
     def dpi(self):
+        # type: () -> Optional[Text]
         """The device pixel ratio of a test or reference file"""
         if self.root is None:
             return None
@@ -455,55 +540,58 @@
 
     @cached_property
     def fuzzy_nodes(self):
+        # type: () -> List[ElementTree.Element]
         """List of ElementTree Elements corresponding to nodes in a test that
         specify reftest fuzziness"""
+        assert self.root is not None
         return self.root.findall(".//{http://www.w3.org/1999/xhtml}meta[@name='fuzzy']")
 
     @cached_property
     def fuzzy(self):
-        rv = {}
+        # type: () -> Dict[Optional[Tuple[Text, Text, Text]], List[List[int]]]
+        rv = {}  # type: Dict[Optional[Tuple[Text, Text, Text]], List[List[int]]]
         if self.root is None:
             return rv
 
         if not self.fuzzy_nodes:
             return rv
 
-        args = ["maxDifference", "totalPixels"]
+        args = [u"maxDifference", u"totalPixels"]
 
         for node in self.fuzzy_nodes:
-            item = node.attrib.get("content", "")
+            item = node.attrib.get(u"content", u"")  # type: Text
 
-            parts = item.rsplit(":", 1)
+            parts = item.rsplit(u":", 1)
             if len(parts) == 1:
-                key = None
+                key = None  # type: Optional[Tuple[Text, Text, Text]]
                 value = parts[0]
             else:
-                key = urljoin(self.url, parts[0])
+                key_part = urljoin(self.url, parts[0])
                 reftype = None
-                for ref in self.references:
-                    if ref[0] == key:
+                for ref in self.references:  # type: Tuple[Text, Text]
+                    if ref[0] == key_part:
                         reftype = ref[1]
                         break
-                if reftype not in ("==", "!="):
-                    raise ValueError("Fuzzy key %s doesn't correspond to a references" % key)
-                key = (self.url, key, reftype)
+                if reftype not in (u"==", u"!="):
+                    raise ValueError("Fuzzy key %s doesn't correspond to a references" % key_part)
+                key = (self.url, key_part, reftype)
                 value = parts[1]
-            ranges = value.split(";")
+            ranges = value.split(u";")
             if len(ranges) != 2:
                 raise ValueError("Malformed fuzzy value %s" % item)
-            arg_values = {None: deque()}
-            for range_str_value in ranges:
-                if "=" in range_str_value:
+            arg_values = {}  # type: Dict[Text, List[int]]
+            positional_args = deque()  # type: Deque[List[int]]
+            for range_str_value in ranges:  # type: Text
+                name = None  # type: Optional[Text]
+                if u"=" in range_str_value:
                     name, range_str_value = [part.strip()
-                                             for part in range_str_value.split("=", 1)]
+                                             for part in range_str_value.split(u"=", 1)]
                     if name not in args:
                         raise ValueError("%s is not a valid fuzzy property" % name)
                     if arg_values.get(name):
                         raise ValueError("Got multiple values for argument %s" % name)
-                else:
-                    name = None
-                if "-" in range_str_value:
-                    range_min, range_max = range_str_value.split("-")
+                if u"-" in range_str_value:
+                    range_min, range_max = range_str_value.split(u"-")
                 else:
                     range_min = range_str_value
                     range_max = range_str_value
@@ -513,27 +601,30 @@
                     raise ValueError("Fuzzy value %s must be a range of integers" %
                                      range_str_value)
                 if name is None:
-                    arg_values[None].append(range_value)
+                    positional_args.append(range_value)
                 else:
                     arg_values[name] = range_value
             rv[key] = []
             for arg_name in args:
                 if arg_values.get(arg_name):
-                    value = arg_values.pop(arg_name)
+                    arg_value = arg_values.pop(arg_name)
                 else:
-                    value = arg_values[None].popleft()
-                rv[key].append(value)
-            assert list(arg_values.keys()) == [None] and len(arg_values[None]) == 0
+                    arg_value = positional_args.popleft()
+                rv[key].append(arg_value)
+            assert len(arg_values) == 0 and len(positional_args) == 0
         return rv
 
     @cached_property
     def testharness_nodes(self):
+        # type: () -> List[ElementTree.Element]
         """List of ElementTree Elements corresponding to nodes representing a
         testharness.js script"""
+        assert self.root is not None
         return self.root.findall(".//{http://www.w3.org/1999/xhtml}script[@src='/resources/testharness.js']")
 
     @cached_property
     def content_is_testharness(self):
+        # type: () -> Optional[bool]
         """Boolean indicating whether the file content represents a
         testharness.js test"""
         if self.root is None:
@@ -542,21 +633,26 @@
 
     @cached_property
     def variant_nodes(self):
+        # type: () -> List[ElementTree.Element]
         """List of ElementTree Elements corresponding to nodes representing a
         test variant"""
+        assert self.root is not None
         return self.root.findall(".//{http://www.w3.org/1999/xhtml}meta[@name='variant']")
 
     @cached_property
     def test_variants(self):
-        rv = []
+        # type: () -> List[Text]
+        rv = []  # type: List[Text]
         if self.ext == ".js":
-            for (key, value) in self.script_metadata:
+            script_metadata = self.script_metadata
+            assert script_metadata is not None
+            for (key, value) in script_metadata:
                 if key == b"variant":
                     rv.append(value.decode("utf-8"))
         else:
             for element in self.variant_nodes:
                 if "content" in element.attrib:
-                    variant = element.attrib["content"]
+                    variant = element.attrib["content"]  # type: Text
                     rv.append(variant)
 
         for variant in rv:
@@ -569,12 +665,15 @@
 
     @cached_property
     def testdriver_nodes(self):
+        # type: () -> List[ElementTree.Element]
         """List of ElementTree Elements corresponding to nodes representing a
         testdriver.js script"""
+        assert self.root is not None
         return self.root.findall(".//{http://www.w3.org/1999/xhtml}script[@src='/resources/testdriver.js']")
 
     @cached_property
     def has_testdriver(self):
+        # type: () -> Optional[bool]
         """Boolean indicating whether the file content represents a
         testharness.js test"""
         if self.root is None:
@@ -583,6 +682,7 @@
 
     @cached_property
     def reftest_nodes(self):
+        # type: () -> List[ElementTree.Element]
         """List of ElementTree Elements corresponding to nodes representing a
         to a reftest <link>"""
         if self.root is None:
@@ -594,9 +694,10 @@
 
     @cached_property
     def references(self):
+        # type: () -> List[Tuple[Text, Text]]
         """List of (ref_url, relation) tuples for any reftest references specified in
         the file"""
-        rv = []
+        rv = []  # type: List[Tuple[Text, Text]]
         rel_map = {"match": "==", "mismatch": "!="}
         for item in self.reftest_nodes:
             if "href" in item.attrib:
@@ -607,12 +708,14 @@
 
     @cached_property
     def content_is_ref_node(self):
+        # type: () -> bool
         """Boolean indicating whether the file is a non-leaf node in a reftest
         graph (i.e. if it contains any <link rel=[mis]match>"""
         return bool(self.references)
 
     @cached_property
     def css_flag_nodes(self):
+        # type: () -> List[ElementTree.Element]
         """List of ElementTree Elements corresponding to nodes representing a
         flag <meta>"""
         if self.root is None:
@@ -621,8 +724,9 @@
 
     @cached_property
     def css_flags(self):
+        # type: () -> Set[Text]
         """Set of flags specified in the file"""
-        rv = set()
+        rv = set()  # type: Set[Text]
         for item in self.css_flag_nodes:
             if "content" in item.attrib:
                 for flag in item.attrib["content"].split():
@@ -631,6 +735,7 @@
 
     @cached_property
     def content_is_css_manual(self):
+        # type: () -> Optional[bool]
         """Boolean indicating whether the file content represents a
         CSS WG-style manual test"""
         if self.root is None:
@@ -640,6 +745,7 @@
 
     @cached_property
     def spec_link_nodes(self):
+        # type: () -> List[ElementTree.Element]
         """List of ElementTree Elements corresponding to nodes representing a
         <link rel=help>, used to point to specs"""
         if self.root is None:
@@ -648,8 +754,9 @@
 
     @cached_property
     def spec_links(self):
+        # type: () -> Set[Text]
         """Set of spec links specified in the file"""
-        rv = set()
+        rv = set()  # type: Set[Text]
         for item in self.spec_link_nodes:
             if "href" in item.attrib:
                 rv.add(item.attrib["href"].strip(space_chars))
@@ -657,6 +764,7 @@
 
     @cached_property
     def content_is_css_visual(self):
+        # type: () -> Optional[bool]
         """Boolean indicating whether the file content represents a
         CSS WG-style visual test"""
         if self.root is None:
@@ -666,10 +774,12 @@
 
     @property
     def type(self):
+        # type: () -> Text
         rv, _ = self.manifest_items()
         return rv
 
     def manifest_items(self):
+        # type: () -> Tuple[Text, List[ManifestItem]]
         """List of manifest items corresponding to the file. There is typically one
         per test, but in the case of reftests a node may have corresponding manifest
         items without being a test itself."""
@@ -682,7 +792,7 @@
                 SupportFile(
                     self.tests_root,
                     self.rel_path
-                )]
+                )]  # type: Tuple[Text, List[ManifestItem]]
 
         elif self.name_is_stub:
             rv = Stub.item_type, [
@@ -729,7 +839,9 @@
 
         elif self.name_is_multi_global:
             globals = b""
-            for (key, value) in self.script_metadata:
+            script_metadata = self.script_metadata
+            assert script_metadata is not None
+            for (key, value) in script_metadata:
                 if key == b"global":
                     globals = value
                     break
@@ -746,7 +858,7 @@
                 )
                 for (suffix, jsshell) in sorted(global_suffixes(globals))
                 for variant in self.test_variants
-            ]
+            ]   # type: List[ManifestItem]
             rv = TestharnessTest.item_type, tests
 
         elif self.name_is_worker:
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/update.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/update.py
index 321cfebe..166a7c9 100755
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/update.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/update.py
@@ -2,7 +2,7 @@
 import argparse
 import os
 
-import manifest
+from . import manifest
 from . import vcs
 from .log import get_logger
 from .download import download_from_github
@@ -13,13 +13,22 @@
 
 logger = get_logger()
 
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from typing import Any
+    from typing import Optional
+    from .manifest import Manifest  # avoid cyclic import
 
-def update(tests_root,
-           manifest,
-           manifest_path=None,
-           working_copy=False,
-           cache_root=None,
-           rebuild=False):
+
+def update(tests_root,  # type: str
+           manifest,  # type: Manifest
+           manifest_path=None,  # type: Optional[str]
+           working_copy=True,  # type: bool
+           cache_root=None,  # type: Optional[str]
+           rebuild=False  # type: bool
+           ):
+    # type: (...) -> bool
     logger.warning("Deprecated; use manifest.load_and_update instead")
     logger.info("Updating manifest")
 
@@ -29,6 +38,7 @@
 
 
 def update_from_cli(**kwargs):
+    # type: (**Any) -> None
     tests_root = kwargs["tests_root"]
     path = kwargs["path"]
     assert tests_root is not None
@@ -41,15 +51,16 @@
                              kwargs["url_base"],
                              update=True,
                              rebuild=kwargs["rebuild"],
-                             cache_root=kwargs["cache_root"],
-                             working_copy=kwargs["work"])
+                             cache_root=kwargs["cache_root"])
 
 
 def abs_path(path):
+    # type: (str) -> str
     return os.path.abspath(os.path.expanduser(path))
 
 
 def create_parser():
+    # type: () -> argparse.ArgumentParser
     parser = argparse.ArgumentParser()
     parser.add_argument(
         "-p", "--path", type=abs_path, help="Path to manifest file.")
@@ -59,9 +70,6 @@
         "-r", "--rebuild", action="store_true", default=False,
         help="Force a full rebuild of the manifest.")
     parser.add_argument(
-        "--work", action="store_true", default=False,
-        help="Build from the working tree rather than the latest commit")
-    parser.add_argument(
         "--url-base", action="store", default="/",
         help="Base url to use as the mount point for tests in this manifest.")
     parser.add_argument(
@@ -73,24 +81,15 @@
     return parser
 
 
-def find_top_repo():
-    path = here
-    rv = None
-    while path != "/":
-        if vcs.is_git_repo(path):
-            rv = path
-        path = os.path.abspath(os.path.join(path, os.pardir))
-
-    return rv
-
-
 def run(*args, **kwargs):
+    # type: (*Any, **Any) -> None
     if kwargs["path"] is None:
         kwargs["path"] = os.path.join(kwargs["tests_root"], "MANIFEST.json")
     update_from_cli(**kwargs)
 
 
 def main():
+    # type: () -> None
     opts = create_parser().parse_args()
 
     run(**vars(opts))
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/utils.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/utils.py
index a097ad5..2eed79f 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/utils.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/utils.py
@@ -1,9 +1,29 @@
-import platform
 import os
+import platform
+import subprocess
 
 from six import BytesIO
 
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from typing import Text
+    from typing import Callable
+    from typing import AnyStr
+    from typing import Any
+    from typing import BinaryIO
+    from typing import Generic
+    from typing import TypeVar
+    from typing import Optional
+    T = TypeVar("T")
+else:
+    # eww, eww, ewwww
+    Generic = {}
+    T = object()
+    Generic[T] = object
+
 def rel_path_to_url(rel_path, url_base="/"):
+    # type: (bytes, Text) -> Text
     assert not os.path.isabs(rel_path), rel_path
     if url_base[0] != "/":
         url_base = "/" + url_base
@@ -13,6 +33,7 @@
 
 
 def from_os_path(path):
+    # type: (AnyStr) -> AnyStr
     assert os.path.sep == "/" or platform.system() == "Windows"
     if "/" == os.path.sep:
         rv = path
@@ -24,6 +45,7 @@
 
 
 def to_os_path(path):
+    # type: (AnyStr) -> AnyStr
     assert os.path.sep == "/" or platform.system() == "Windows"
     if "\\" in path:
         raise ValueError("normalised path contains \\")
@@ -32,25 +54,53 @@
     return path.replace("/", os.path.sep)
 
 
-class ContextManagerBytesIO(BytesIO):
+def git(path):
+    # type: (bytes) -> Optional[Callable[..., bytes]]
+    def gitfunc(cmd, *args):
+        # type: (bytes, *bytes) -> bytes
+        full_cmd = ["git", cmd] + list(args)
+        try:
+            return subprocess.check_output(full_cmd, cwd=path, stderr=subprocess.STDOUT)
+        except Exception as e:
+            if platform.uname()[0] == "Windows" and isinstance(e, WindowsError):
+                full_cmd[0] = "git.bat"
+                return subprocess.check_output(full_cmd, cwd=path, stderr=subprocess.STDOUT)
+            else:
+                raise
+
+    try:
+        # this needs to be a command that fails if we aren't in a git repo
+        gitfunc("rev-parse", "--show-toplevel")
+    except (subprocess.CalledProcessError, OSError):
+        return None
+    else:
+        return gitfunc
+
+
+class ContextManagerBytesIO(BytesIO):  # type: ignore
     def __enter__(self):
-        return self
+        # type: () -> BinaryIO
+        return self  # type: ignore
 
     def __exit__(self, *args, **kwargs):
+        # type: (*Any, **Any) -> bool
         self.close()
+        return True
 
 
-class cached_property(object):
+class cached_property(Generic[T]):
     def __init__(self, func):
+        # type: (Callable[[Any], T]) -> None
         self.func = func
         self.__doc__ = getattr(func, "__doc__")
         self.name = func.__name__
 
     def __get__(self, obj, cls=None):
+        # type: (Any, Optional[type]) -> T
         if obj is None:
-            return self
+            return self  # type: ignore
 
-        if self.name not in obj.__dict__:
-            obj.__dict__[self.name] = self.func(obj)
-            obj.__dict__.setdefault("__cached_properties__", set()).add(self.name)
-        return obj.__dict__[self.name]
+        # we can unconditionally assign as next time this won't be called
+        assert self.name not in obj.__dict__
+        rv = obj.__dict__[self.name] = self.func(obj)
+        return rv
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/vcs.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/vcs.py
index 676e53f6..c810be1 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/vcs.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/vcs.py
@@ -1,18 +1,39 @@
+import abc
 import json
 import os
-import platform
 import stat
-import subprocess
 from collections import deque
+from collections import MutableMapping
+
+from six import with_metaclass, PY2
 
 from .sourcefile import SourceFile
+from .utils import git
+
+try:
+    from ..gitignore import gitignore
+except ValueError:
+    # relative import beyond toplevel throws *ValueError*!
+    from gitignore import gitignore  # type: ignore
+
+
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from typing import Dict, Optional, List, Set, Text, Iterable, Any, Tuple, Union, Iterator
+    from .manifest import Manifest  # cyclic import under MYPY guard
+    if PY2:
+        stat_result = Any
+    else:
+        stat_result = os.stat_result
 
 
 def get_tree(tests_root, manifest, manifest_path, cache_root,
-             working_copy=False, rebuild=False):
+             working_copy=True, rebuild=False):
+    # type: (bytes, Manifest, Optional[bytes], Optional[bytes], bool, bool) -> FileSystem
     tree = None
     if cache_root is None:
-        cache_root = os.path.join(tests_root, ".wptcache")
+        cache_root = os.path.join(tests_root, b".wptcache")
     if not os.path.exists(cache_root):
         try:
             os.makedirs(cache_root)
@@ -20,11 +41,8 @@
             cache_root = None
 
     if not working_copy:
-        tree = Git.for_path(tests_root,
-                            manifest.url_base,
-                            manifest_path=manifest_path,
-                            cache_path=cache_root,
-                            rebuild=rebuild)
+        raise ValueError("working_copy=False unsupported")
+
     if tree is None:
         tree = FileSystem(tests_root,
                           manifest.url_base,
@@ -34,46 +52,20 @@
     return tree
 
 
-class Git(object):
-    def __init__(self, repo_root, url_base, cache_path, manifest_path=None,
-                 rebuild=False):
-        self.root = repo_root
-        self.git = Git.get_func(repo_root)
-        self.url_base = url_base
-        # rebuild is a noop for now since we don't cache anything
-
-    @staticmethod
-    def get_func(repo_path):
-        def git(cmd, *args):
-            full_cmd = ["git", cmd] + list(args)
-            try:
-                return subprocess.check_output(full_cmd, cwd=repo_path, stderr=subprocess.STDOUT)
-            except Exception as e:
-                if platform.uname()[0] == "Windows" and isinstance(e, WindowsError):
-                    full_cmd[0] = "git.bat"
-                    return subprocess.check_output(full_cmd, cwd=repo_path, stderr=subprocess.STDOUT)
-                else:
-                    raise
-        return git
-
-    @classmethod
-    def for_path(cls, path, url_base, cache_path, manifest_path=None, rebuild=False):
-        git = Git.get_func(path)
-        try:
-            # this needs to be a command that fails if we aren't in a git repo
-            git("rev-parse", "--show-toplevel")
-        except (subprocess.CalledProcessError, OSError):
-            return None
-        else:
-            return cls(path, url_base, cache_path,
-                       manifest_path=manifest_path, rebuild=rebuild)
+class GitHasher(object):
+    def __init__(self, path):
+        # type: (bytes) -> None
+        self.git = git(path)
 
     def _local_changes(self):
+        # type: () -> Set[bytes]
         """get a set of files which have changed between HEAD and working copy"""
-        changes = set()
+        assert self.git is not None
 
-        cmd = ["status", "-z", "--ignore-submodules=all"]
-        data = self.git(*cmd)
+        changes = set()  # type: Set[bytes]
+
+        cmd = [b"status", b"-z", b"--ignore-submodules=all"]
+        data = self.git(*cmd)  # type: bytes
 
         in_rename = False
         for line in data.split(b"\0")[:-1]:
@@ -88,33 +80,29 @@
 
         return changes
 
-    def _show_file(self, path):
-        path = os.path.relpath(os.path.abspath(path), self.root)
-        return self.git("show", "HEAD:%s" % path)
+    def hash_cache(self):
+        # type: () -> Dict[bytes, Optional[bytes]]
+        """
+        A dict of rel_path -> current git object id if the working tree matches HEAD else None
+        """
+        hash_cache = {}  # type: Dict[bytes, Optional[bytes]]
 
-    def __iter__(self):
-        cmd = ["ls-tree", "-r", "-z", "HEAD"]
+        if self.git is None:
+            return hash_cache
+
+        cmd = [b"ls-tree", b"-r", b"-z", b"HEAD"]
         local_changes = self._local_changes()
-        for result in self.git(*cmd).split("\0")[:-1]:
-            data, rel_path = result.rsplit("\t", 1)
-            hash = data.split(" ", 3)[2]
-            if rel_path in local_changes:
-                contents = self._show_file(rel_path)
-            else:
-                contents = None
-            yield SourceFile(self.root,
-                             rel_path,
-                             self.url_base,
-                             hash,
-                             contents=contents), True
+        for result in self.git(*cmd).split(b"\0")[:-1]:  # type: bytes
+            data, rel_path = result.rsplit(b"\t", 1)
+            hash_cache[rel_path] = None if rel_path in local_changes else data.split(b" ", 3)[2]
 
-    def dump_caches(self):
-        pass
+        return hash_cache
+
 
 
 class FileSystem(object):
     def __init__(self, root, url_base, cache_path, manifest_path=None, rebuild=False):
-        from gitignore import gitignore
+        # type: (bytes, Text, Optional[bytes], Optional[bytes], bool) -> None
         self.root = os.path.abspath(root)
         self.url_base = url_base
         self.ignore_cache = None
@@ -127,27 +115,34 @@
         self.path_filter = gitignore.PathFilter(self.root,
                                                 extras=[".git/"],
                                                 cache=self.ignore_cache)
+        git = GitHasher(root)
+        if git is not None:
+            self.hash_cache = git.hash_cache()
+        else:
+            self.hash_cache = {}
 
     def __iter__(self):
+        # type: () -> Iterator[Tuple[Union[bytes, SourceFile], bool]]
         mtime_cache = self.mtime_cache
         for dirpath, dirnames, filenames in self.path_filter(walk(self.root)):
             for filename, path_stat in filenames:
                 path = os.path.join(dirpath, filename)
                 if mtime_cache is None or mtime_cache.updated(path, path_stat):
-                    yield SourceFile(self.root, path, self.url_base), True
+                    hash = self.hash_cache.get(path, None)
+                    yield SourceFile(self.root, path, self.url_base, hash), True
                 else:
                     yield path, False
 
     def dump_caches(self):
+        # type: () -> None
         for cache in [self.mtime_cache, self.ignore_cache]:
             if cache is not None:
                 cache.dump()
 
 
-class CacheFile(object):
-    file_name = None
-
+class CacheFile(with_metaclass(abc.ABCMeta)):
     def __init__(self, cache_root, tests_root, rebuild=False):
+        # type: (bytes, bytes, bool) -> None
         self.tests_root = tests_root
         if not os.path.exists(cache_root):
             os.makedirs(cache_root)
@@ -155,14 +150,21 @@
         self.modified = False
         self.data = self.load(rebuild)
 
+    @abc.abstractproperty
+    def file_name(self):
+        # type: () -> bytes
+        pass
+
     def dump(self):
+        # type: () -> None
         if not self.modified:
             return
         with open(self.path, 'w') as f:
             json.dump(self.data, f, indent=1)
 
     def load(self, rebuild=False):
-        data = {}
+        # type: (bool) -> Dict[Any, Any]
+        data = {}  # type: Dict[Any, Any]
         try:
             if not rebuild:
                 with open(self.path, 'r') as f:
@@ -173,19 +175,22 @@
         return data
 
     def check_valid(self, data):
+        # type: (Dict[Any, Any]) -> Dict[Any, Any]
         """Check if the cached data is valid and return an updated copy of the
         cache containing only data that can be used."""
         return data
 
 
 class MtimeCache(CacheFile):
-    file_name = "mtime.json"
+    file_name = b"mtime.json"
 
     def __init__(self, cache_root, tests_root, manifest_path, rebuild=False):
+        # type: (bytes, bytes, bytes, bool) -> None
         self.manifest_path = manifest_path
-        super(MtimeCache, self).__init__(cache_root, tests_root, rebuild=False)
+        super(MtimeCache, self).__init__(cache_root, tests_root, rebuild)
 
     def updated(self, rel_path, stat):
+        # type: (bytes, stat_result) -> bool
         """Return a boolean indicating whether the file changed since the cache was last updated.
 
         This implicitly updates the cache with the new mtime data."""
@@ -197,6 +202,7 @@
         return False
 
     def check_valid(self, data):
+        # type: (Dict[Any, Any]) -> Dict[Any, Any]
         if data.get("/tests_root") != self.tests_root:
             self.modified = True
         else:
@@ -212,6 +218,7 @@
         return data
 
     def dump(self):
+        # type: () -> None
         if self.manifest_path is None:
             raise ValueError
         if not os.path.exists(self.manifest_path):
@@ -222,31 +229,50 @@
         super(MtimeCache, self).dump()
 
 
-class GitIgnoreCache(CacheFile):
-    file_name = "gitignore.json"
+class GitIgnoreCache(CacheFile, MutableMapping):  # type: ignore
+    file_name = b"gitignore.json"
 
     def check_valid(self, data):
-        ignore_path = os.path.join(self.tests_root, ".gitignore")
+        # type: (Dict[Any, Any]) -> Dict[Any, Any]
+        ignore_path = os.path.join(self.tests_root, b".gitignore")
         mtime = os.path.getmtime(ignore_path)
-        if data.get("/gitignore_file") != [ignore_path, mtime]:
+        if data.get(b"/gitignore_file") != [ignore_path, mtime]:
             self.modified = True
             data = {}
-            data["/gitignore_file"] = [ignore_path, mtime]
+            data[b"/gitignore_file"] = [ignore_path, mtime]
         return data
 
     def __contains__(self, key):
+        # type: (Any) -> bool
         return key in self.data
 
     def __getitem__(self, key):
-        return self.data[key]
+        # type: (bytes) -> bool
+        v = self.data[key]
+        assert isinstance(v, bool)
+        return v
 
     def __setitem__(self, key, value):
+        # type: (bytes, bool) -> None
         if self.data.get(key) != value:
             self.modified = True
             self.data[key] = value
 
+    def __delitem__(self, key):
+        # type: (bytes) -> None
+        del self.data[key]
+
+    def __iter__(self):
+        # type: () -> Iterator[bytes]
+        return iter(self.data)
+
+    def __len__(self):
+        # type: () -> int
+        return len(self.data)
+
 
 def walk(root):
+    # type: (bytes) -> Iterable[Tuple[bytes, List[Tuple[bytes, stat_result]], List[Tuple[bytes, stat_result]]]]
     """Re-implementation of os.walk. Returns an iterator over
     (dirpath, dirnames, filenames), with some semantic differences
     to os.walk.
@@ -269,7 +295,7 @@
     relpath = os.path.relpath
 
     root = os.path.abspath(root)
-    stack = deque([(root, "")])
+    stack = deque([(root, b"")])
 
     while stack:
         dir_path, rel_path = stack.popleft()
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/serve/serve.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/serve/serve.py
index 53f8145..1fc61747 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/serve/serve.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/serve/serve.py
@@ -32,6 +32,12 @@
 from mod_pywebsocket import standalone as pywebsocket
 
 
+EDIT_HOSTS_HELP = ("Please ensure all the necessary WPT subdomains "
+                   "are mapped to a loopback device in /etc/hosts. "
+                   "See https://github.com/web-platform-tests/wpt#running-the-tests "
+                   "for instructions.")
+
+
 def replace_end(s, old, new):
     """
     Given a string `s` that ends with `old`, replace that occurrence of `old`
@@ -302,6 +308,7 @@
 
 rewrites = [("GET", "/resources/WebIDLParser.js", "/resources/webidl2/lib/webidl2.js")]
 
+
 class RoutesBuilder(object):
     def __init__(self):
         self.forbidden_override = [("GET", "/tools/runner/*", handlers.file_handler),
@@ -310,8 +317,7 @@
 
         self.forbidden = [("*", "/_certs/*", handlers.ErrorHandler(404)),
                           ("*", "/tools/*", handlers.ErrorHandler(404)),
-                          ("*", "{spec}/tools/*", handlers.ErrorHandler(404)),
-                          ("*", "/serve.py", handlers.ErrorHandler(404))]
+                          ("*", "{spec}/tools/*", handlers.ErrorHandler(404))]
 
         self.extra = []
 
@@ -445,18 +451,19 @@
     wrapper.start(start_http_server, host, port, paths, build_routes(aliases),
                   bind_address, config)
 
+    url = "http://{}:{}/".format(host, port)
     connected = False
     for i in range(10):
         try:
-            urllib.request.urlopen("http://%s:%d/" % (host, port))
+            urllib.request.urlopen(url)
             connected = True
             break
         except urllib.error.URLError:
             time.sleep(1)
 
     if not connected:
-        logger.critical("Failed to connect to test server on http://%s:%s. "
-                        "You may need to edit /etc/hosts or similar, see README.md." % (host, port))
+        logger.critical("Failed to connect to test server "
+                        "on {}. {}".format(url, EDIT_HOSTS_HELP))
         sys.exit(1)
 
     for domain in config.domains_set:
@@ -466,8 +473,7 @@
         try:
             urllib.request.urlopen("http://%s:%d/" % (domain, port))
         except Exception:
-            logger.critical("Failed probing domain %s. "
-                            "You may need to edit /etc/hosts or similar, see README.md." % domain)
+            logger.critical("Failed probing domain {}. {}".format(domain, EDIT_HOSTS_HELP))
             sys.exit(1)
 
     wrapper.wait()
@@ -496,7 +502,7 @@
 def start_servers(host, ports, paths, routes, bind_address, config, **kwargs):
     servers = defaultdict(list)
     for scheme, ports in ports.items():
-        assert len(ports) == {"http":2}.get(scheme, 1)
+        assert len(ports) == {"http": 2}.get(scheme, 1)
 
         # If trying to start HTTP/2.0 server, check compatibility
         if scheme == 'http2' and not http2_compatible():
@@ -507,11 +513,11 @@
         for port in ports:
             if port is None:
                 continue
-            init_func = {"http":start_http_server,
-                         "https":start_https_server,
-                         "http2":start_http2_server,
-                         "ws":start_ws_server,
-                         "wss":start_wss_server}[scheme]
+            init_func = {"http": start_http_server,
+                         "https": start_https_server,
+                         "http2": start_http2_server,
+                         "ws": start_ws_server,
+                         "wss": start_wss_server}[scheme]
 
             server_proc = ServerProc(scheme=scheme)
             server_proc.start(init_func, host, port, paths, routes, bind_address,
@@ -565,6 +571,8 @@
                                  encrypt_after_connect=config.ssl_config["encrypt_after_connect"],
                                  latency=kwargs.get("latency"),
                                  http2=True)
+
+
 class WebSocketDaemon(object):
     def __init__(self, host, port, doc_root, handlers_root, log_level, bind_address,
                  ssl_config):
@@ -596,6 +604,17 @@
         opts, args = pywebsocket._parse_args_and_config(cmd_args)
         opts.cgi_directories = []
         opts.is_executable_method = None
+
+        # Logging needs to be configured both before and after reloading,
+        # because some modules store loggers as global variables.
+        pywebsocket._configure_logging(opts)
+        # Ensure that when we start this in a new process we have the global
+        # lock in the logging module unlocked.
+        reload_module(logging)
+        release_mozlog_lock()
+        pywebsocket._configure_logging(opts)
+        # DO NOT LOG BEFORE THIS LINE.
+
         self.server = pywebsocket.WebSocketServer(opts)
         ports = [item[0].getsockname()[1] for item in self.server._sockets]
         assert all(item == ports[0] for item in ports)
@@ -642,29 +661,21 @@
 
 
 def start_ws_server(host, port, paths, routes, bind_address, config, **kwargs):
-    # Ensure that when we start this in a new process we have the global lock
-    # in the logging module unlocked
-    reload_module(logging)
-    release_mozlog_lock()
     return WebSocketDaemon(host,
                            str(port),
                            repo_root,
                            config.paths["ws_doc_root"],
-                           "debug",
+                           config.log_level.lower(),
                            bind_address,
-                           ssl_config = None)
+                           ssl_config=None)
 
 
 def start_wss_server(host, port, paths, routes, bind_address, config, **kwargs):
-    # Ensure that when we start this in a new process we have the global lock
-    # in the logging module unlocked
-    reload_module(logging)
-    release_mozlog_lock()
     return WebSocketDaemon(host,
                            str(port),
                            repo_root,
                            config.paths["ws_doc_root"],
-                           "debug",
+                           config.log_level.lower(),
                            bind_address,
                            config.ssl_config)
 
@@ -721,8 +732,10 @@
 
     return rv
 
+
 def _make_subdomains_product(s, depth=2):
-    return set(u".".join(x) for x in chain(*(product(s, repeat=i) for i in range(1, depth+1))))
+    return {u".".join(x) for x in chain(*(product(s, repeat=i) for i in range(1, depth+1)))}
+
 
 _subdomains = {u"www",
                u"www1",
@@ -740,7 +753,8 @@
 class ConfigBuilder(config.ConfigBuilder):
     """serve config
 
-    this subclasses wptserve.config.ConfigBuilder to add serve config options"""
+    This subclasses wptserve.config.ConfigBuilder to add serve config options.
+    """
 
     _default = {
         "browser_host": "web-platform.test",
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/browser.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/browser.py
index 52157bca..1b883b9 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/browser.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/browser.py
@@ -179,7 +179,11 @@
             binary = find_executable("firefox", os.path.join(path, "firefox"))
         elif self.platform == "win":
             import mozinstall
-            binary = mozinstall.get_binary(path, "firefox")
+            try:
+                binary = mozinstall.get_binary(path, "firefox")
+            except mozinstall.InvalidBinary:
+                # ignore the case where we fail to get a binary
+                pass
         elif self.platform == "macos":
             binary = find_executable("firefox", os.path.join(path, self.application_name.get(channel, "Firefox Nightly.app"),
                                                              "Contents", "MacOS"))
@@ -304,7 +308,7 @@
         # This is used rather than an API call to avoid rate limits
         tags = call("git", "ls-remote", "--tags", "--refs",
                     "https://github.com/mozilla/geckodriver.git")
-        release_re = re.compile(".*refs/tags/v(\d+)\.(\d+)\.(\d+)")
+        release_re = re.compile(r".*refs/tags/v(\d+)\.(\d+)\.(\d+)")
         latest_release = 0
         for item in tags.split("\n"):
             m = release_re.match(item)
@@ -461,34 +465,48 @@
     def find_webdriver(self, channel=None):
         return find_executable("chromedriver")
 
-    def _latest_chromedriver_url(self, browser_binary=None):
-        latest = None
-        chrome_version = self.version(browser_binary)
-        if chrome_version is not None:
-            parts = chrome_version.split(".")
-            if len(parts) == 4:
-                latest_url = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_%s.%s.%s" % (
-                    parts[0], parts[1], parts[2])
-                try:
-                    latest = get(latest_url).text.strip()
-                except requests.RequestException:
-                    latest_url = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_%s" % parts[0]
-                    try:
-                        latest = get(latest_url).text.strip()
-                    except requests.RequestException:
-                        pass
-        if latest is None:
-            # Fall back to the tip-of-tree *Chromium* build.
-            latest_url = "https://storage.googleapis.com/chromium-browser-snapshots/%s/LAST_CHANGE" % (
-                self.chromium_platform_string())
+    def _official_chromedriver_url(self, chrome_version):
+        # http://chromedriver.chromium.org/downloads/version-selection
+        parts = chrome_version.split(".")
+        assert len(parts) == 4
+        latest_url = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_%s.%s.%s" % tuple(parts[:-1])
+        try:
             latest = get(latest_url).text.strip()
+        except requests.RequestException:
+            latest_url = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_%s" % parts[0]
+            try:
+                latest = get(latest_url).text.strip()
+            except requests.RequestException:
+                return None
+        return "https://chromedriver.storage.googleapis.com/%s/chromedriver_%s.zip" % (
+            latest, self.platform_string())
+
+    def _chromium_chromedriver_url(self, chrome_version):
+        try:
+            # Try to find the Chromium build with the same revision.
+            omaha = get("https://omahaproxy.appspot.com/deps.json?version=" + chrome_version).json()
+            revision = omaha['chromium_base_position']
             url = "https://storage.googleapis.com/chromium-browser-snapshots/%s/%s/chromedriver_%s.zip" % (
-                self.chromium_platform_string(), latest, self.platform_string())
-        else:
-            url = "https://chromedriver.storage.googleapis.com/%s/chromedriver_%s.zip" % (
-                latest, self.platform_string())
+                self.chromium_platform_string(), revision, self.platform_string())
+            # Check the status without downloading the content (this is a streaming request).
+            get(url)
+        except requests.RequestException:
+            # Fall back to the tip-of-tree Chromium build.
+            revision_url = "https://storage.googleapis.com/chromium-browser-snapshots/%s/LAST_CHANGE" % (
+                self.chromium_platform_string())
+            revision = get(revision_url).text.strip()
+            url = "https://storage.googleapis.com/chromium-browser-snapshots/%s/%s/chromedriver_%s.zip" % (
+                self.chromium_platform_string(), revision, self.platform_string())
         return url
 
+    def _latest_chromedriver_url(self, browser_binary=None):
+        chrome_version = self.version(browser_binary)
+        assert chrome_version, "Cannot detect the version of Chrome"
+        # Remove channel suffixes (e.g. " dev").
+        chrome_version = chrome_version.split(' ')[0]
+        return (self._official_chromedriver_url(chrome_version) or
+                self._chromium_chromedriver_url(chrome_version))
+
     def install_webdriver(self, dest=None, channel=None, browser_binary=None):
         if dest is None:
             dest = os.pwd
@@ -618,6 +636,89 @@
         if m:
             return m.group(0)
 
+class EdgeChromium(Browser):
+    """MicrosoftEdge-specific interface."""
+    platform = {
+        "Linux": "linux",
+        "Windows": "win",
+        "Darwin": "macos"
+    }.get(uname[0])
+    product = "edgechromium"
+    requirements = "requirements_edge_chromium.txt"
+
+    def install(self, dest=None, channel=None):
+        raise NotImplementedError
+
+    def find_binary(self, venv_path=None, channel=None):
+        binary = None
+        if self.platform == "win":
+            binaryname = "msedge"
+            binary = find_executable(binaryname)
+            if not binary:
+                # Use paths from different Edge channels starting with Release\Beta\Dev\Canary
+                winpaths = [os.path.expanduser("~\\AppData\\Local\\Microsoft\\Edge\\Application"),
+                            os.path.expandvars("$SYSTEMDRIVE\\Program Files\\Microsoft\\Edge Beta\\Application"),
+                            os.path.expandvars("$SYSTEMDRIVE\\Program Files\\Microsoft\\Edge Dev\\Application"),
+                            os.path.expandvars("$SYSTEMDRIVE\\Program Files (x86)\\Microsoft\\Edge Beta\\Application"),
+                            os.path.expandvars("$SYSTEMDRIVE\\Program Files (x86)\\Microsoft\\Edge Dev\\Application"),
+                            os.path.expanduser("~\\AppData\Local\\Microsoft\\Edge SxS\\Application"),]
+                return find_executable(binaryname, os.pathsep.join(winpaths))
+        if self.platform == "macos":
+            binaryname = "Microsoft Edge Canary"
+            binary = find_executable(binaryname)
+            if not binary:
+                macpaths = ["/Applications/Microsoft Edge.app/Contents/MacOS",
+                    os.path.expanduser("~/Applications/Microsoft Edge.app/Contents/MacOS"),
+                    "/Applications/Microsoft Edge Canary.app/Contents/MacOS",
+                    os.path.expanduser("~/Applications/Microsoft Edge Canary.app/Contents/MacOS")]
+                return find_executable("Microsoft Edge Canary", os.pathsep.join(macpaths))
+        return binary
+
+    def find_webdriver(self, channel=None):
+        return find_executable("msedgedriver")
+
+    def install_webdriver(self, dest=None, channel=None, browser_binary=None):
+        if self.platform == "win":
+            raise ValueError("Only Windows platform is currently supported")
+
+        if dest is None:
+            dest = os.pwd
+
+        platform = "x64" if uname[4] == "x86_64" else "x86"
+        url = "https://az813057.vo.msecnd.net/webdriver/msedgedriver_%s/msedgedriver.exe" % platform
+
+        self.logger.info("Downloading MSEdgeDriver from %s" % url)
+        resp = get(url)
+        installer_path = os.path.join(dest, "msedgedriver.exe")
+        with open(installer_path, "wb") as f:
+            f.write(resp.content)
+
+        return find_executable("msedgedriver", dest)
+
+    def version(self, binary=None, webdriver_binary=None):
+        if binary is None:
+            binary = self.find_binary()
+        if self.platform != "win":
+            try:
+                version_string = call(binary, "--version").strip()
+            except subprocess.CalledProcessError:
+                self.logger.warning("Failed to call %s" % binary)
+                return None
+            m = re.match(r"Microsoft Edge (.*) ", version_string)
+            if not m:
+                self.logger.warning("Failed to extract version from: %s" % version_string)
+                return None
+            return m.group(1)
+        else:
+            if binary is not None:
+                command = "(Get-Item '%s').VersionInfo.FileVersion" % binary
+                try:
+                    return call("powershell.exe", command).strip()
+                except (subprocess.CalledProcessError, OSError):
+                    self.logger.warning("Failed to call %s in PowerShell" % command)
+                    return None
+            self.logger.warning("Failed to find Edge binary.")
+            return None
 
 class Edge(Browser):
     """Edge-specific interface."""
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/commands.json b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/commands.json
index 161a9def..da8804d 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/commands.json
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/commands.json
@@ -1,6 +1,7 @@
 {
     "run": {"path": "run.py", "script": "run", "parser": "create_parser", "help": "Run tests in a browser",
-            "virtualenv": true, "install": ["requests"], "requirements": ["../wptrunner/requirements.txt"]},
+            "virtualenv": true, "install": ["requests", "zstandard"],
+            "requirements": ["../wptrunner/requirements.txt"]},
     "create": {"path": "create.py", "script": "run", "parser": "get_parser", "help": "Create a new wpt test"},
     "update-expectations": {"path": "update.py", "script": "update_expectations",
                             "parser": "create_parser_update", "help": "Update expectations files from raw logs.",
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/run.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/run.py
index ffdd8ef..0b306aee 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/run.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/run.py
@@ -121,10 +121,15 @@
 
         missing_hosts = set(expected_hosts)
         if is_windows:
-            hosts_path = "%s\System32\drivers\etc\hosts" % os.environ.get("SystemRoot", "C:\Windows")
+            hosts_path = r"%s\System32\drivers\etc\hosts" % os.environ.get("SystemRoot", r"C:\Windows")
         else:
             hosts_path = "/etc/hosts"
 
+        if os.path.abspath(os.curdir) == wpt_root:
+            wpt_path = "wpt"
+        else:
+            wpt_path = os.path.join(wpt_root, "wpt")
+
         with open(hosts_path, "r") as f:
             for line in f:
                 line = line.split("#", 1)[0].strip()
@@ -136,13 +141,14 @@
                 if is_windows:
                     message = """Missing hosts file configuration. Run
 
-python wpt make-hosts-file | Out-File %s -Encoding ascii -Append
+python %s make-hosts-file | Out-File %s -Encoding ascii -Append
 
-in PowerShell with Administrator privileges.""" % hosts_path
+in PowerShell with Administrator privileges.""" % (wpt_path, hosts_path)
                 else:
                     message = """Missing hosts file configuration. Run
 
-./wpt make-hosts-file | sudo tee -a %s""" % hosts_path
+%s make-hosts-file | sudo tee -a %s""" % ("./wpt" if wpt_path == "wpt" else wpt_path,
+                                          hosts_path)
                 raise WptrunError(message)
 
 
@@ -277,6 +283,8 @@
         if kwargs["browser_channel"] == "dev":
             logger.info("Automatically turning on experimental features for Chrome Dev")
             kwargs["binary_args"].append("--enable-experimental-web-platform-features")
+            # HACK(Hexcles): work around https://github.com/web-platform-tests/wpt/issues/16448
+            kwargs["webdriver_args"].append("--disable-build-check")
 
 
 class ChromeAndroid(BrowserSetup):
@@ -325,6 +333,29 @@
                 raise WptrunError("Unable to locate or install operadriver binary")
 
 
+class EdgeChromium(BrowserSetup):
+    name = "MicrosoftEdge"
+    browser_cls = browser.EdgeChromium
+
+    def setup_kwargs(self, kwargs):
+        if kwargs["webdriver_binary"] is None:
+            webdriver_binary = self.browser.find_webdriver()
+
+            if webdriver_binary is None:
+                install = self.prompt_install("msedgedriver")
+
+                if install:
+                    logger.info("Downloading msedgedriver")
+                    webdriver_binary = self.browser.install_webdriver(dest=self.venv.bin_path)
+            else:
+                logger.info("Using webdriver binary %s" % webdriver_binary)
+
+            if webdriver_binary:
+                kwargs["webdriver_binary"] = webdriver_binary
+            else:
+                raise WptrunError("Unable to locate or install msedgedriver binary")
+
+
 class Edge(BrowserSetup):
     name = "edge"
     browser_cls = browser.Edge
@@ -463,6 +494,7 @@
     "firefox": Firefox,
     "chrome": Chrome,
     "chrome_android": ChromeAndroid,
+    "edgechromium": EdgeChromium,
     "edge": Edge,
     "edge_webdriver": EdgeWebDriver,
     "ie": InternetExplorer,
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/testfiles.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/testfiles.py
index 006e4a22d..b1c81877 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/testfiles.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/testfiles.py
@@ -5,9 +5,38 @@
 import subprocess
 import sys
 
+import six
 from collections import OrderedDict
 from six import iteritems
 
+try:
+    from ..manifest import manifest
+except ValueError:
+    # if we're not within the tools package, the above is an import from above
+    # the top-level which raises ValueError, so reimport it with an absolute
+    # reference
+    #
+    # note we need both because depending on caller we may/may not have the
+    # paths set up correctly to handle both and MYPY has no knowledge of our
+    # sys.path magic
+    from manifest import manifest  # type: ignore
+
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from typing import Any
+    from typing import Callable
+    from typing import Dict
+    from typing import Iterable
+    from typing import List
+    from typing import Optional
+    from typing import Pattern
+    from typing import Sequence
+    from typing import Set
+    from typing import Text
+    from typing import Tuple
+    from typing import Union
+
 here = os.path.dirname(__file__)
 wpt_root = os.path.abspath(os.path.join(here, os.pardir, os.pardir))
 
@@ -15,9 +44,11 @@
 
 
 def get_git_cmd(repo_path):
+    # type: (bytes) -> Callable[..., Text]
     """Create a function for invoking git commands as a subprocess."""
     def git(cmd, *args):
-        full_cmd = ["git", cmd] + list(item.decode("utf8") if isinstance(item, bytes) else item for item in args)
+        # type: (Text, *Union[bytes, Text]) -> Text
+        full_cmd = [u"git", cmd] + list(item.decode("utf8") if isinstance(item, bytes) else item for item in args)  # type: List[Text]
         try:
             logger.debug(" ".join(full_cmd))
             return subprocess.check_output(full_cmd, cwd=repo_path, stderr=subprocess.STDOUT).decode("utf8").strip()
@@ -29,10 +60,12 @@
 
 
 def display_branch_point():
+    # type: () -> None
     print(branch_point())
 
 
 def branch_point():
+    # type: () -> Optional[Text]
     git = get_git_cmd(wpt_root)
     if (os.environ.get("GITHUB_PULL_REQUEST", "false") == "false" and
         os.environ.get("GITHUB_BRANCH") == "master"):
@@ -42,7 +75,7 @@
         # This is a PR, so the base branch is in GITHUB_BRANCH
         base_branch = os.environ.get("GITHUB_BRANCH")
         assert base_branch, "GITHUB_BRANCH environment variable is defined"
-        branch_point = git("merge-base", "HEAD", base_branch)
+        branch_point = git("merge-base", "HEAD", base_branch)  # type: Optional[Text]
     else:
         # Otherwise we aren't on a PR, so we try to find commits that are only in the
         # current branch c.f.
@@ -57,7 +90,7 @@
 
         # get all commits on HEAD but not reachable from anything in not_heads
         commits = git("rev-list", "--topo-order", "--parents", "HEAD", *not_heads)
-        commit_parents = OrderedDict()
+        commit_parents = OrderedDict()  # type: Dict[Text, List[Text]]
         if commits:
             for line in commits.split("\n"):
                 line_commits = line.split(" ")
@@ -101,6 +134,7 @@
 
 
 def compile_ignore_rule(rule):
+    # type: (str) -> Pattern[str]
     rule = rule.replace(os.path.sep, "/")
     parts = rule.split("/")
     re_parts = []
@@ -115,10 +149,11 @@
 
 
 def repo_files_changed(revish, include_uncommitted=False, include_new=False):
+    # type: (str, bool, bool) -> Set[Text]
     git = get_git_cmd(wpt_root)
-    files = git("diff", "--name-only", "-z", revish).split("\0")
-    assert not files[-1]
-    files = set(files[:-1])
+    files_list = git("diff", "--name-only", "-z", revish).split("\0")
+    assert not files_list[-1]
+    files = set(files_list[:-1])
 
     if include_uncommitted:
         entries = git("status", "-z").split("\0")
@@ -140,16 +175,17 @@
 
 
 def exclude_ignored(files, ignore_rules):
+    # type: (Iterable[Text], Optional[Sequence[str]]) -> Tuple[List[Text], List[Text]]
     if ignore_rules is None:
         ignore_rules = []
-    ignore_rules = [compile_ignore_rule(item) for item in ignore_rules]
+    compiled_ignore_rules = [compile_ignore_rule(item) for item in ignore_rules]
 
     changed = []
     ignored = []
     for item in sorted(files):
         fullpath = os.path.join(wpt_root, item)
         rule_path = item.replace(os.path.sep, "/")
-        for rule in ignore_rules:
+        for rule in compiled_ignore_rules:
             if rule.match(rule_path):
                 ignored.append(fullpath)
                 break
@@ -159,7 +195,12 @@
     return changed, ignored
 
 
-def files_changed(revish, ignore_rules=None, include_uncommitted=False, include_new=False):
+def files_changed(revish,  # type: str
+                  ignore_rules=None,  # type: Optional[Sequence[str]]
+                  include_uncommitted=False,  # type: bool
+                  include_new=False  # type: bool
+                  ):
+    # type: (...) -> Tuple[List[Text], List[Text]]
     """Find files changed in certain revisions.
 
     The function passes `revish` directly to `git diff`, so `revish` can have a
@@ -176,27 +217,29 @@
 
 
 def _in_repo_root(full_path):
+    # type: (Union[bytes, Text]) -> bool
     rel_path = os.path.relpath(full_path, wpt_root)
     path_components = rel_path.split(os.sep)
     return len(path_components) < 2
 
 
 def load_manifest(manifest_path=None, manifest_update=True):
-    # Delay import after localpaths sets up sys.path, because otherwise the
-    # import path will be "..manifest" and Python will treat it as a different
-    # manifest module.
-    from manifest import manifest
+    # type: (Optional[str], bool) -> manifest.Manifest
     if manifest_path is None:
         manifest_path = os.path.join(wpt_root, "MANIFEST.json")
     return manifest.load_and_update(wpt_root, manifest_path, "/",
                                     update=manifest_update)
 
 
-def affected_testfiles(files_changed, skip_dirs=None,
-                       manifest_path=None, manifest_update=True):
+def affected_testfiles(files_changed,  # type: Iterable[Text]
+                       skip_dirs=None,  # type: Optional[Set[str]]
+                       manifest_path=None,  # type: Optional[str]
+                       manifest_update=True  # type: bool
+                       ):
+    # type: (...) -> Tuple[Set[Text], Set[str]]
     """Determine and return list of test files that reference changed files."""
     if skip_dirs is None:
-        skip_dirs = set(["conformance-checkers", "docs", "tools"])
+        skip_dirs = {"conformance-checkers", "docs", "tools"}
     affected_testfiles = set()
     # Exclude files that are in the repo root, because
     # they are not part of any test.
@@ -219,10 +262,10 @@
     interfaces_changed = interfaces_files.intersection(nontests_changed)
     nontests_changed = nontests_changed.intersection(support_files)
 
-    tests_changed = set(item for item in files_changed if item in test_files)
+    tests_changed = {item for item in files_changed if item in test_files}
 
     nontest_changed_paths = set()
-    rewrites = {"/resources/webidl2/lib/webidl2.js": "/resources/WebIDLParser.js"}
+    rewrites = {"/resources/webidl2/lib/webidl2.js": "/resources/WebIDLParser.js"}  # type: Dict[Text, Text]
     for full_path in nontests_changed:
         rel_path = os.path.relpath(full_path, wpt_root)
         path_components = rel_path.split(os.sep)
@@ -239,6 +282,7 @@
     interfaces_changed_names = map(interface_name, interfaces_changed)
 
     def affected_by_wdspec(test):
+        # type: (str) -> bool
         affected = False
         if test in wdspec_test_files:
             for support_full_path, _ in nontest_changed_paths:
@@ -254,6 +298,7 @@
         return affected
 
     def affected_by_interfaces(file_contents):
+        # type: (Union[bytes, Text]) -> bool
         if len(interfaces_changed_names) > 0:
             if 'idlharness.js' in file_contents:
                 for interface in interfaces_changed_names:
@@ -278,13 +323,13 @@
                 continue
 
             with open(test_full_path, "rb") as fh:
-                file_contents = fh.read()
-                if file_contents.startswith("\xfe\xff"):
-                    file_contents = file_contents.decode("utf-16be", "replace")
-                elif file_contents.startswith("\xff\xfe"):
-                    file_contents = file_contents.decode("utf-16le", "replace")
+                raw_file_contents = fh.read()  # type: bytes
+                if raw_file_contents.startswith("\xfe\xff"):
+                    file_contents = raw_file_contents.decode("utf-16be", "replace")  # type: Text
+                elif raw_file_contents.startswith("\xff\xfe"):
+                    file_contents = raw_file_contents.decode("utf-16le", "replace")
                 else:
-                    file_contents = file_contents.decode("utf8", "replace")
+                    file_contents = raw_file_contents.decode("utf8", "replace")
                 for full_path, repo_path in nontest_changed_paths:
                     rel_path = os.path.relpath(full_path, root).replace(os.path.sep, "/")
                     if rel_path in file_contents or repo_path in file_contents or affected_by_interfaces(file_contents):
@@ -295,13 +340,14 @@
 
 
 def get_parser():
+    # type: () -> argparse.ArgumentParser
     parser = argparse.ArgumentParser()
     parser.add_argument("revish", default=None, help="Commits to consider. Defaults to the "
                         "commits on the current branch", nargs="?")
     # TODO: Consolidate with `./wpt run --affected`:
     # https://github.com/web-platform-tests/wpt/issues/14560
-    parser.add_argument("--ignore-rules", nargs="*", type=set,
-                        default=set(["resources/testharness*"]),
+    parser.add_argument("--ignore-rules", nargs="*", type=set,  # type: ignore
+                        default={"resources/testharness*"},
                         help="Rules for paths to exclude from lists of changes. Rules are paths "
                         "relative to the test root, with * before a separator or the end matching "
                         "anything other than a path separator and ** in that position matching "
@@ -319,6 +365,7 @@
 
 
 def get_parser_affected():
+    # type: () -> argparse.ArgumentParser
     parser = get_parser()
     parser.add_argument("--metadata",
                         dest="metadata_root",
@@ -329,13 +376,18 @@
 
 
 def get_revish(**kwargs):
-    revish = kwargs["revish"]
-    if kwargs["revish"] is None:
+    # type: (**Any) -> bytes
+    revish = kwargs.get("revish")
+    if revish is None:
         revish = "%s..HEAD" % branch_point()
+    if isinstance(revish, six.text_type):
+        revish = revish.encode("utf8")
+    assert isinstance(revish, six.binary_type)
     return revish
 
 
 def run_changed_files(**kwargs):
+    # type: (**Any) -> None
     revish = get_revish(**kwargs)
     changed, _ = files_changed(revish, kwargs["ignore_rules"],
                                include_uncommitted=kwargs["modified"],
@@ -344,10 +396,11 @@
     separator = "\0" if kwargs["null"] else "\n"
 
     for item in sorted(changed):
-        sys.stdout.write(os.path.relpath(item, wpt_root) + separator)
+        sys.stdout.write(os.path.relpath(item.encode("utf8"), wpt_root) + separator)
 
 
 def run_tests_affected(**kwargs):
+    # type: (**Any) -> None
     revish = get_revish(**kwargs)
     changed, _ = files_changed(revish, kwargs["ignore_rules"],
                                include_uncommitted=kwargs["modified"],
@@ -355,7 +408,7 @@
     manifest_path = os.path.join(kwargs["metadata_root"], "MANIFEST.json")
     tests_changed, dependents = affected_testfiles(
         changed,
-        set(["conformance-checkers", "docs", "tools"]),
+        {"conformance-checkers", "docs", "tools"},
         manifest_path=manifest_path
     )
 
@@ -373,6 +426,6 @@
         if kwargs["show_type"]:
             item_types = {i.item_type for i in wpt_manifest.iterpath(results["path"])}
             if len(item_types) != 1:
-                item_types = [" ".join(item_types)]
+                item_types = {" ".join(item_types)}
             results["item_type"] = item_types.pop()
         sys.stdout.write(message.format(**results))
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/utils.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/utils.py
index f98687f..2d427713 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/utils.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/utils.py
@@ -6,7 +6,7 @@
 from io import BytesIO
 
 try:
-    from typing import Any
+    from typing import Any, Callable
 except ImportError:
     pass
 
@@ -17,10 +17,11 @@
     def set_if_none(self,
                     name,            # type: str
                     value,           # type: Any
-                    err_fn=None,     # type: (Kwargs, str) -> Any
+                    err_fn=None,     # type: Callable[[Kwargs, str], Any]
                     desc=None,       # type: str
-                    extra_cond=None  # type: (Kwargs) -> bool
+                    extra_cond=None  # type: Callable[[Kwargs], Any]
                     ):
+        # type: (...) -> Any
         if desc is None:
             desc = name
 
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/virtualenv.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/virtualenv.py
index b99d15d..e0f51d5 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/virtualenv.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/virtualenv.py
@@ -4,7 +4,17 @@
 import logging
 from distutils.spawn import find_executable
 
-import pkg_resources
+# The `pkg_resources` module is provided by `setuptools`, which is itself a
+# dependency of `virtualenv`. Tolerate its absence so that this module may be
+# evaluated when that module is not available. Because users may not recognize
+# the `pkg_resources` module by name, raise a more descriptive error if it is
+# referenced during execution.
+try:
+    import pkg_resources as _pkg_resources
+    get_pkg_resources = lambda: _pkg_resources
+except ImportError:
+    def get_pkg_resources():
+        raise ValueError("The Python module `virtualenv` is not installed.")
 
 from tools.wpt.utils import call
 
@@ -69,7 +79,7 @@
             raise ValueError("trying to read working_set when venv doesn't exist")
 
         if self._working_set is None:
-            self._working_set = pkg_resources.WorkingSet((self.lib_path,))
+            self._working_set = get_pkg_resources().WorkingSet((self.lib_path,))
 
         return self._working_set
 
@@ -85,7 +95,7 @@
     def install(self, *requirements):
         try:
             self.working_set.require(*requirements)
-        except pkg_resources.ResolutionError:
+        except Exception:
             pass
         else:
             return
@@ -98,7 +108,7 @@
         with open(requirements_path) as f:
             try:
                 self.working_set.require(f.read())
-            except pkg_resources.ResolutionError:
+            except Exception:
                 pass
             else:
                 return
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/wpt.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/wpt.py
index 909d435..4130e1e 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/wpt.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/wpt.py
@@ -80,6 +80,34 @@
     return script, parser
 
 
+def create_complete_parser():
+    """Eagerly load all subparsers. This involves more work than is required
+    for typical command-line usage. It is maintained for the purposes of
+    documentation generation as implemented in WPT's top-level `/docs`
+    directory."""
+
+    commands = load_commands()
+    parser = argparse.ArgumentParser()
+    subparsers = parser.add_subparsers()
+
+    for command in commands:
+        props = commands[command]
+
+        if props["virtualenv"]:
+            setup_virtualenv(None, False, props)
+
+        subparser = import_command('wpt', command, props)[1]
+        if not subparser:
+            continue
+
+        subparsers.add_parser(command,
+                              help=props["help"],
+                              add_help=False,
+                              parents=[subparser])
+
+    return parser
+
+
 def setup_virtualenv(path, skip_venv_setup, props):
     if skip_venv_setup and path is None:
         raise ValueError("Must set --venv when --skip-venv-setup is used")
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/request.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/request.py
index 1b1061b..a80bc0c 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/request.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/request.py
@@ -556,7 +556,7 @@
             return dict.__getitem__(self, key)[0]
         elif default is not missing:
             return default
-        raise KeyError
+        raise KeyError(key)
 
     def last(self, key, default=missing):
         """Get the last value with a given key
@@ -570,7 +570,7 @@
             return dict.__getitem__(self, key)[-1]
         elif default is not missing:
             return default
-        raise KeyError
+        raise KeyError(key)
 
     def get_list(self, key):
         """Get all values with a given key as a list
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/response.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/response.py
index efc65ef..6a241039 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/response.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/response.py
@@ -113,10 +113,10 @@
                         time or interval from now when the cookie expires
 
         """
-        days = dict((i+1, name) for i, name in enumerate(["jan", "feb", "mar",
-                                                          "apr", "may", "jun",
-                                                          "jul", "aug", "sep",
-                                                          "oct", "nov", "dec"]))
+        days = {i+1: name for i, name in enumerate(["jan", "feb", "mar",
+                                                    "apr", "may", "jun",
+                                                    "jul", "aug", "sep",
+                                                    "oct", "nov", "dec"])}
         if value is None:
             value = ''
             max_age = 0
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/router.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/router.py
index 5118c03..5b860b5 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/router.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/router.py
@@ -102,7 +102,7 @@
             self.register(*route)
 
     def register(self, methods, path, handler):
-        """Register a handler for a set of paths.
+        r"""Register a handler for a set of paths.
 
         :param methods: Set of methods this should match. "*" is a
                         special value indicating that all methods should
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/server.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/server.py
index d57a36b..e58b4ac 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/server.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/server.py
@@ -29,7 +29,8 @@
 from .utils import HTTPException
 from .constants import h2_headers
 
-"""HTTP server designed for testing purposes.
+"""
+HTTP server designed for testing purposes.
 
 The server is designed to provide flexibility in the way that
 requests are handled, and to provide control both of exactly
@@ -63,6 +64,12 @@
 """
 
 
+EDIT_HOSTS_HELP = ("Please ensure all the necessary WPT subdomains "
+                  "are mapped to a loopback device in /etc/hosts. "
+                  "See https://github.com/web-platform-tests/wpt#running-the-tests "
+                  "for instructions.")
+
+
 class RequestRewriter(object):
     def __init__(self, rules):
         """Object for rewriting the request path.
@@ -669,8 +676,7 @@
 
             _host, self.port = self.httpd.socket.getsockname()
         except Exception:
-            self.logger.error("Failed to start HTTP server. "
-                              "You may need to edit /etc/hosts or similar, see README.md.")
+            self.logger.error("Failed to start HTTP server. {}".format(EDIT_HOSTS_HELP))
             raise
 
     def start(self, block=False):
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/sslutils/openssl.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/sslutils/openssl.py
index db188dad..3c5c1dc23 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/sslutils/openssl.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/sslutils/openssl.py
@@ -6,7 +6,7 @@
 import tempfile
 from datetime import datetime, timedelta
 
-from six import iteritems
+from six import iteritems, PY2
 
 # Amount of time beyond the present to consider certificates "expired." This
 # allows certificates to be proactively re-generated in the "buffer" period
@@ -14,6 +14,17 @@
 CERT_EXPIRY_BUFFER = dict(hours=6)
 
 
+def _ensure_str(s, encoding):
+    """makes sure s is an instance of str, converting with encoding if needed"""
+    if isinstance(s, str):
+        return s
+
+    if PY2:
+        return s.encode(encoding)
+    else:
+        return s.decode(encoding)
+
+
 class OpenSSL(object):
     def __init__(self, logger, binary, base_path, conf_path, hosts, duration,
                  base_conf_path=None):
@@ -65,18 +76,14 @@
             self.cmd += ["-config", self.conf_path]
         self.cmd += list(args)
 
-        # Copy the environment, converting to plain strings. Windows
-        # StartProcess is picky about all the keys/values being plain strings,
-        # but at least in MSYS shells, the os.environ dictionary can be mixed.
+        # Copy the environment, converting to plain strings. Win32 StartProcess
+        # is picky about all the keys/values being str (on both Py2/3).
         env = {}
         for k, v in iteritems(os.environ):
-            try:
-                env[k.encode("utf8")] = v.encode("utf8")
-            except UnicodeDecodeError:
-                pass
+            env[_ensure_str(k, "utf8")] = _ensure_str(v, "utf8")
 
         if self.base_conf_path is not None:
-            env["OPENSSL_CONF"] = self.base_conf_path.encode("utf8")
+            env["OPENSSL_CONF"] = _ensure_str(self.base_conf_path, "utf-8")
 
         self.proc = subprocess.Popen(self.cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                                      env=env)
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/utils.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/utils.py
index d0f87fe..64b08a2 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/utils.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/utils.py
@@ -100,6 +100,7 @@
     ]
 
 def get_port(host=''):
+    host = host or '127.0.0.1'
     port = 0
     while True:
         free_socket = _open_socket(host, 0)
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py b/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
index 8bb0ad4..543f647 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
@@ -510,7 +510,6 @@
                     'python',
                     '/mock-checkout/third_party/blink/tools/blinkpy/third_party/wpt/wpt/wpt',
                     'manifest',
-                    '--work',
                     '--no-download',
                     '--tests-root',
                     MOCK_WEB_TESTS + 'external/wpt',
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py b/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py
index 3906dd48..0df95e7 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py
@@ -222,7 +222,7 @@
         """Generates MANIFEST.json on the specified directory."""
         finder = PathFinder(host.filesystem)
         wpt_exec_path = finder.path_from_blink_tools('blinkpy', 'third_party', 'wpt', 'wpt', 'wpt')
-        cmd = ['python', wpt_exec_path, 'manifest', '--work', '--no-download', '--tests-root', dest_path]
+        cmd = ['python', wpt_exec_path, 'manifest', '--no-download', '--tests-root', dest_path]
 
         # ScriptError will be raised if the command fails.
         host.executive.run_command(
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_manifest_unittest.py b/third_party/blink/tools/blinkpy/w3c/wpt_manifest_unittest.py
index 537b298..20de9c77 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_manifest_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_manifest_unittest.py
@@ -30,7 +30,6 @@
                     'python',
                     '/mock-checkout/third_party/blink/tools/blinkpy/third_party/wpt/wpt/wpt',
                     'manifest',
-                    '--work',
                     '--no-download',
                     '--tests-root',
                     WEB_TEST_DIR + '/external/wpt',
@@ -57,7 +56,6 @@
                     'python',
                     '/mock-checkout/third_party/blink/tools/blinkpy/third_party/wpt/wpt/wpt',
                     'manifest',
-                    '--work',
                     '--no-download',
                     '--tests-root',
                     WEB_TEST_DIR + '/external/wpt',
@@ -84,7 +82,6 @@
                     'python',
                     '/mock-checkout/third_party/blink/tools/blinkpy/third_party/wpt/wpt/wpt',
                     'manifest',
-                    '--work',
                     '--no-download',
                     '--tests-root',
                     WEB_TEST_DIR + '/wpt_internal',
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
index 19b79cb9..11c069d 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
@@ -128,7 +128,6 @@
 Bug(none) fast/sub-pixel/transformed-iframe-copy-on-scroll.html [ Failure ]
 Bug(none) fragmentation/outline-crossing-columns.html [ Crash ]
 Bug(none) fullscreen/compositor-touch-hit-rects-fullscreen-video-controls.html [ Failure ]
-Bug(none) http/tests/subresource_filter/ad-highlight-frame-resized.html [ Failure ]
 Bug(none) paint/pagination/pagination-change-clip-crash.html [ Failure ]
 Bug(none) printing/fixed-positioned-headers-and-footers-absolute-covering-some-pages.html [ Failure ]
 Bug(none) printing/fixed-positioned-headers-and-footers-larger-than-page.html [ Failure ]
@@ -148,7 +147,6 @@
 crbug.com/959949 external/wpt/element-timing/invisible-images.html [ Failure Pass ]
 
 # Single pixel difference around one of the radiused borders.
-Bug(none) external/wpt/css/filter-effects/backdrop-filter-clip-rect.html [ Failure ]
 Bug(none) external/wpt/css/filter-effects/backdrop-filter-reference-filter.html [ Failure ]
 
 # Less invalidations or different invalidations without pixel failures.
@@ -360,8 +358,6 @@
 Bug(none) paint/clipath/change-mask-clip-path-multicol-crash.html [ Crash ]
 Bug(none) paint/pagination/composited-paginated-outlined-box.html [ Failure ]
 
-crbug.com/832928 compositing/geometry/child-layer-position-with-clip-path-overflow.html [ Failure ]
-
 crbug.com/882075 compositing/overflow/composited-sticky-element-with-non-integer-relative-position-to-container.html [ Failure ]
 
 Bug(none) compositing/overflow/ancestor-with-clip-path.html [ Failure ]
@@ -393,6 +389,7 @@
 
 # Backdrop filter
 crbug.com/923429 css3/filters/backdrop-filter-basic-blur.html [ Timeout Failure ]
+crbug.com/923429 css3/filters/backdrop-filter-boundary.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-browser-zoom.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-border-radius.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-clip-rect-zoom.html [ Failure ]
@@ -404,16 +401,36 @@
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-basic-background-color.html [ Failure ]
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-basic-opacity-2.html [ Failure ]
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-basic.html [ Failure ]
+crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-clip-rect.html [ Failure ]
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-fixed-clip.html [ Failure ]
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-isolation.html [ Failure ]
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-isolation-fixed.html [ Failure ]
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-isolation-isolate.html [ Failure ]
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-update.html [ Failure ]
+crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-clipped.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-basic-blur.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-border-radius.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-boundary.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-plus-filter.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-rendering-no-background.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-rendering.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-transform.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-basic-background-color.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-basic-opacity-2.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-basic.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-clipped.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-fixed-clip.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-isolation-fixed.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-isolation-isolate.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-isolation.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-update.html [ Failure ]
 
 crbug.com/940033 virtual/fractional_scrolling_threaded/fast/scrolling/wheel-scrolling-over-custom-scrollbar.html [ Failure ]
 crbug.com/940033 virtual/threaded/fast/scrolling/wheel-scrolling-over-custom-scrollbar.html [ Failure ]
 
-
 # Missing composited layer for fixed-position
 crbug.com/931491 compositing/layer-creation/fixed-position-change-out-of-view-in-view.html [ Failure ]
 crbug.com/931491 compositing/layer-creation/fixed-position-in-fixed-overflow.html [ Failure ]
@@ -432,8 +449,6 @@
 crbug.com/931491 compositing/squashing/squash-above-fixed-3.html [ Failure ]
 crbug.com/931491 compositing/squashing/squash-paint-invalidation-fixed-position.html [ Failure ]
 
-Bug(none) css3/filters/blur-filter-page-scroll-self.html [ Failure ]
-
 # Crash during PictureLayer::GetPicture() when DisplayItemList is finished twice.
 Bug(none) http/tests/devtools/layers/layer-canvas-log.js [ Crash ]
 Bug(none) http/tests/devtools/layers/layer-replay-scale.js [ Crash ]
@@ -460,11 +475,7 @@
 Bug(none) external/wpt/css/css-masking/clip-path-svg-content/mask-nested-clip-path-panning-001.svg [ Failure ]
 Bug(none) external/wpt/css/css-masking/mask-svg-content/mask-type-002.svg [ Failure ]
 Bug(none) external/wpt/css/css-masking/mask-svg-content/mask-type-003.svg [ Failure ]
-Bug(none) images/color-profile-mask-image-svg.html [ Failure ]
 Bug(none) svg/W3C-SVG-1.1/masking-intro-01-f.svg [ Failure ]
-Bug(none) svg/W3C-SVG-1.1/masking-mask-01-b.svg [ Failure ]
-Bug(none) svg/W3C-SVG-1.1/masking-path-04-b.svg [ Failure ]
-Bug(none) svg/batik/text/textEffect2.svg [ Failure ]
 Bug(none) svg/clip-path/clip-in-mask.svg [ Failure ]
 Bug(none) svg/clip-path/deep-nested-clip-in-mask-different-unitTypes.svg [ Failure ]
 Bug(none) svg/clip-path/deep-nested-clip-in-mask-panning.svg [ Failure ]
@@ -482,7 +493,6 @@
 Bug(none) svg/filters/filter-clip.svg [ Failure ]
 Bug(none) svg/masking/mask-type-luminance.svg [ Failure ]
 Bug(none) svg/masking/mask-type-not-set.svg [ Failure ]
-Bug(none) svg/zoom/page/zoom-mask-with-percentages.svg [ Failure ]
 
 crbug.com/962191 animations/stability/base-render-style-crash.html [ Crash ]
 crbug.com/962191 animations/stability/option-element-crash.html [ Crash ]
@@ -527,7 +537,6 @@
 crbug.com/962191 external/wpt/web-animations/timing-model/animations/setting-the-current-time-of-an-animation.html [ Crash ]
 crbug.com/962191 external/wpt/web-animations/timing-model/animations/setting-the-playback-rate-of-an-animation.html [ Crash ]
 crbug.com/962191 external/wpt/web-animations/timing-model/animations/setting-the-start-time-of-an-animation.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animations/setting-the-target-effect-of-an-animation.html [ Crash ]
 crbug.com/962191 external/wpt/web-animations/timing-model/animations/setting-the-timeline-of-an-animation.html [ Crash ]
 crbug.com/962191 external/wpt/web-animations/timing-model/animations/the-current-time-of-an-animation.html [ Crash ]
 crbug.com/962191 external/wpt/web-animations/timing-model/animations/updating-the-finished-state.html [ Crash ]
@@ -605,9 +614,3 @@
 crbug.com/966981 virtual/threaded/fast/scrolling/events/scrollend-event-fired-to-scrolled-element.html [ Skip ]
 crbug.com/966981 virtual/threaded/fast/scrolling/events/scrollend-event-fired-to-window.html [ Skip ]
 crbug.com/966981 virtual/threaded/synthetic_gestures/animated-wheel-tiny-delta.html [ Skip ]
-
-Bug(none) fast/borders/border-radius-mask-canvas-border.html [ Failure ]
-Bug(none) fast/borders/border-radius-mask-canvas.html [ Failure ]
-Bug(none) fast/borders/border-radius-mask-video-ratio.html [ Failure ]
-Bug(none) fast/borders/border-radius-mask-video-shadow.html [ Failure ]
-Bug(none) fast/borders/border-radius-mask-video.html [ Failure ]
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
index a5732be9..511c892 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -71,6 +71,7 @@
 crbug.com/591099 css3/filters/filter-repaint-composited-fallback.html [ Pass ]
 crbug.com/591099 editing/selection/paint-hyphen.html [ Pass ]
 crbug.com/591099 external/wpt/acid/acid2/reftest.html [ Failure Pass ]
+crbug.com/591099 external/wpt/content-security-policy/inheritance/iframe-all-local-schemes.sub.html [ Pass ]
 crbug.com/591099 external/wpt/css/CSS2/floats/float-nowrap-3.html [ Pass ]
 crbug.com/591099 external/wpt/css/CSS2/floats/floats-line-wrap-shifted-001.html [ Pass ]
 crbug.com/408859 external/wpt/css/CSS2/floats/overhanging-float-paint-order.html [ Pass ]
@@ -82,6 +83,7 @@
 crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-nested-002.xht [ Pass ]
 crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-remove-006.xht [ Pass ]
 crbug.com/591099 external/wpt/css/CSS2/text/white-space-mixed-003.xht [ Pass ]
+crbug.com/591099 external/wpt/css/css-contain/contain-layout-baseline-005.html [ Pass ]
 crbug.com/714962 external/wpt/css/css-fonts/font-features-across-space-1.html [ Pass ]
 crbug.com/714962 external/wpt/css/css-fonts/font-features-across-space-3.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-grid/grid-items/grid-item-percentage-sizes-003.html [ Pass ]
@@ -103,6 +105,7 @@
 crbug.com/591099 external/wpt/css/css-position/static-position/vrl-rtl-rtl.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-rhythm/ [ Skip ]
 crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/polygon/shape-outside-polygon-017.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-shapes/spec-examples/shape-outside-018.html [ Failure ]
 crbug.com/845902 external/wpt/css/css-sizing/whitespace-and-break.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/boundary-shaping/boundary-shaping-001.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/boundary-shaping/boundary-shaping-003.html [ Pass ]
@@ -163,7 +166,7 @@
 crbug.com/591099 external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-002.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/word-break/word-break-break-all-004.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/word-break/word-break-break-all-inline-006.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-transitions/no-transition-from-ua-to-blocking-stylesheet.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-transitions/no-transition-from-ua-to-blocking-stylesheet.html [ Failure Pass ]
 crbug.com/591099 external/wpt/css/css-ui/text-overflow-010.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-ui/webkit-appearance-menulist-button-002.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-writing-modes/abs-pos-non-replaced-icb-vlr-003.xht [ Pass ]
@@ -288,7 +291,7 @@
 crbug.com/591099 external/wpt/geolocation-API/PositionOptions.https.html [ Failure Pass ]
 crbug.com/591099 external/wpt/html/user-activation/activation-transfer-without-click.tentative.html [ Pass ]
 crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-pan-x-css_touch.html [ Pass ]
-crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-pan-y-css_touch.html [ Pass ]
+crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-pan-y-css_touch.html [ Failure Pass ]
 crbug.com/845902 external/wpt/quirks/line-height-trailing-collapsable-whitespace.html [ Pass ]
 crbug.com/591099 external/wpt/referrer-policy/no-referrer/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Pass ]
 crbug.com/591099 external/wpt/referrer-policy/no-referrer/http-rp/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Pass ]
@@ -334,6 +337,7 @@
 crbug.com/591099 http/tests/devtools/tracing/console-timeline.js [ Pass ]
 crbug.com/591099 http/tests/media/video-load-metadata-decode-error.html [ Pass ]
 crbug.com/591099 http/tests/security/inactive-document-with-empty-security-origin.html [ Pass ]
+crbug.com/591099 http/tests/security/mixedContent/insecure-css-resources.html [ Failure Pass ]
 crbug.com/591099 images/feature-policy-oversized-images-resize.html [ Pass ]
 crbug.com/591099 media/video-object-fit-change.html [ Failure Pass ]
 crbug.com/591099 paint/invalidation/flexbox/scrollbars-changed.html [ Failure ]
@@ -360,7 +364,7 @@
 crbug.com/591099 virtual/exotic-color-space/ [ Skip ]
 crbug.com/591099 virtual/fractional_scrolling_threaded/fast/scrolling/unscrollable-layer-subpixel-size-with-negative-overflow.html [ Failure Pass ]
 crbug.com/591099 virtual/gpu-rasterization/images/color-profile-image-filter-all.html [ Pass ]
-crbug.com/591099 virtual/gpu/fast/canvas/OffscreenCanvas-copyImage.html [ Failure Pass ]
+crbug.com/591099 virtual/gpu/fast/canvas/OffscreenCanvas-copyImage.html [ Pass ]
 crbug.com/591099 virtual/gpu/fast/canvas/OffscreenCanvas-filter.html [ Pass ]
 crbug.com/591099 virtual/gpu/fast/canvas/canvas-arc-circumference.html [ Failure ]
 crbug.com/591099 virtual/gpu/fast/canvas/canvas-blend-image.html [ Pass ]
@@ -379,6 +383,9 @@
 crbug.com/591099 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/update-registration-with-type.https.html [ Pass ]
 crbug.com/591099 virtual/paint-timing/external/wpt/paint-timing/sibling-painting-first-image.html [ Failure ]
 crbug.com/591099 virtual/prefer_compositing_to_lcd_text/ [ Skip ]
+crbug.com/591099 virtual/scalefactor200/css3/filters/backdrop-filter-svg.html [ Pass ]
+crbug.com/591099 virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change.html [ Failure ]
+crbug.com/591099 virtual/scalefactor200/external/wpt/css/filter-effects/filtered-inline-applies-to-float.html [ Pass ]
 crbug.com/591099 virtual/scroll_customization/ [ Skip ]
 crbug.com/591099 virtual/stable/ [ Skip ]
 crbug.com/591099 virtual/streams-native/external/wpt/fetch/api/basic/error-after-response.html [ Pass ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index a40fc5e..c441b59 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1648,7 +1648,6 @@
 
 
 ### sheriff 2018-05-28
-crbug.com/840238 http/tests/devtools/elements/shadow/shadow-distribution.js [ Failure ]
 
 crbug.com/767269 [ Win ] inspector-protocol/layout-fonts/cjk-ideograph-fallback-by-lang.js [ Pass Failure ]
 
@@ -1719,8 +1718,8 @@
 
 crbug.com/771233 [ Win10 ] http/tests/devtools/audits/ [ Skip ]
 
-crbug.com/923269 virtual/gpu/fast/canvas/OffscreenCanvas-copyImage.html [ Failure ]
-crbug.com/923269 fast/canvas/OffscreenCanvas-copyImage.html [ Failure ]
+crbug.com/923269 virtual/gpu/fast/canvas/OffscreenCanvas-copyImage.html [ Pass Failure ]
+crbug.com/923269 fast/canvas/OffscreenCanvas-copyImage.html [ Pass Failure ]
 
 crbug.com/410974 fast/scroll-behavior/scroll-customization/scrollstate-basic.html [ Pass Failure ]
 crbug.com/410974 fast/scroll-behavior/scroll-customization/scrollstate-consume-deltas.html [ Pass Failure ]
@@ -3132,6 +3131,7 @@
 crbug.com/626703 [ Win ] external/wpt/html/semantics/embedded-content/media-elements/ready-states/autoplay-hidden.optional.html [ Failure Timeout ]
 crbug.com/626703 external/wpt/fetch/content-type/response.window.html [ Timeout ]
 crbug.com/626703 virtual/blink-cors/external/wpt/fetch/content-type/response.window.html [ Timeout ]
+crbug.com/626703 virtual/omt-worker-fetch/external/wpt/fetch/content-type/response.window.html [ Timeout ]
 crbug.com/626703 virtual/streams-native/external/wpt/fetch/content-type/response.window.html [ Timeout ]
 crbug.com/626703 virtual/streaming-preload/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-manual-module.html [ Timeout ]
 crbug.com/626703 virtual/streaming-preload/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-manual-classic.html [ Timeout ]
@@ -3454,6 +3454,7 @@
 crbug.com/626703 external/wpt/css/css-transforms/transform-box/view-box-mutation.html [ Failure ]
 crbug.com/626703 external/wpt/fetch/security/redirect-to-url-with-credentials.https.html [ Timeout ]
 crbug.com/626703 virtual/blink-cors/external/wpt/fetch/security/redirect-to-url-with-credentials.https.html [ Timeout ]
+crbug.com/626703 virtual/omt-worker-fetch/external/wpt/fetch/security/redirect-to-url-with-credentials.https.html [ Timeout ]
 crbug.com/626703 virtual/streams-native/external/wpt/fetch/security/redirect-to-url-with-credentials.https.html [ Timeout ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-024.html [ Failure ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-048.html [ Failure ]
@@ -3542,6 +3543,7 @@
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-009.html [ Failure ]
 crbug.com/626703 external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ]
 crbug.com/626703 virtual/blink-cors/external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ]
+crbug.com/626703 virtual/omt-worker-fetch/external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ]
 crbug.com/626703 virtual/streams-native/external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-fonts/font-feature-settings-descriptor-01.html [ Failure ]
 crbug.com/626703 [ Win10 ] external/wpt/fetch/api/redirect/redirect-count.any.worker.html [ Timeout ]
@@ -4148,6 +4150,76 @@
 # This is failing because of the lack of CSP support.
 crbug.com/940316 virtual/omt-worker-fetch/external/wpt/content-security-policy/inside-worker/shared-script.html [ Failure Pass ]
 
+# PlzDedicatedWorker
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/module-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/module-worker/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/worker-request/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/worker-request/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/module-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/module-worker/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/worker-request/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/worker-request/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/http-rp/same-origin/http-http/module-worker/keep-origin-redirect/generic.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/http-rp/same-origin/http-http/module-worker/no-redirect/generic.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/http-rp/same-origin/http-http/worker-request/keep-origin-redirect/generic.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/http-rp/same-origin/http-http/worker-request/no-redirect/generic.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-http/module-worker/keep-origin-redirect/generic.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-http/module-worker/no-redirect/generic.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-http/worker-request/keep-origin-redirect/generic.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-http/worker-request/no-redirect/generic.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/module-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/module-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/worker-request/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/worker-request/no-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/module-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/module-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/worker-request/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/worker-request/no-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/module-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/module-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/worker-request/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/worker-request/no-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-http/module-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-http/module-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-http/worker-request/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-http/worker-request/no-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-http/module-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-http/module-worker/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-http/worker-request/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-http/worker-request/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-http/module-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-http/module-worker/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-http/worker-request/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-http/worker-request/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-http/module-worker/keep-origin-redirect/same-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-http/module-worker/no-redirect/same-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-http/worker-request/keep-origin-redirect/same-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-http/worker-request/no-redirect/same-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-http/module-worker/keep-origin-redirect/same-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-http/module-worker/no-redirect/same-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-http/worker-request/keep-origin-redirect/same-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-http/worker-request/no-redirect/same-insecure.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/module-worker/keep-origin-redirect/generic.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/module-worker/no-redirect/generic.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/worker-request/keep-origin-redirect/generic.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/worker-request/no-redirect/generic.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/module-worker/keep-origin-redirect/generic.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/module-worker/no-redirect/generic.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/worker-request/keep-origin-redirect/generic.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/worker-request/no-redirect/generic.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/module-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/module-worker/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/worker-request/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/worker-request/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/module-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/module-worker/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/worker-request/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/worker-request/no-redirect/insecure-protocol.http.html [ Failure ]
+
+crbug.com/971581 virtual/omt-worker-fetch/external/wpt/fetch/api/request/destination/fetch-destination-no-load-event.https.html [ Timeout ]
+crbug.com/971581 virtual/omt-worker-fetch/external/wpt/fetch/api/request/destination/fetch-destination-worker.https.html [ Failure ]
+crbug.com/971581 virtual/omt-worker-fetch/external/wpt/fetch/sec-metadata/worker.tentative.https.sub.html [ Failure ]
+
 # This fails because AllowedByNoSniff::MimeTypeAsScript() blocks the nested worker's
 # worker script, because the script url has a .html file extension.
 crbug.com/655458 external/wpt/workers/semantics/multiple-workers/003.html [ Timeout ]
@@ -4981,7 +5053,7 @@
 crbug.com/853360 [ Mac ] fast/forms/calendar-picker/month-picker-appearance-step.html [ Failure ]
 crbug.com/853360 [ Mac ] fast/forms/calendar-picker/week-picker-appearance.html [ Failure ]
 crbug.com/853360 [ Mac ] http/tests/misc/slow-loading-image-in-pattern.html [ Pass Timeout Failure ]
-crbug.com/853360 [ Mac ] paint/invalidation/media-audio-no-spurious-repaints.html [ Failure ]
+crbug.com/853360 [ Mac ] paint/invalidation/media-audio-no-spurious-repaints.html [ Failure Timeout ]
 crbug.com/853360 [ Mac ] tables/mozilla/bugs/bug30692.html [ Failure ]
 crbug.com/853360 [ Mac ] virtual/scalefactor200/fast/hidpi/static/calendar-picker-appearance.html [ Failure ]
 crbug.com/853360 [ Mac ] virtual/scalefactor200/fast/hidpi/static/data-suggestion-picker-appearance.html [ Failure ]
@@ -5321,6 +5393,7 @@
 crbug.com/922951 http/tests/security/offscreencanvas-placeholder-read-blocked-no-crossorigin.html [ Skip ]
 crbug.com/922951 virtual/blink-cors/http/tests/security/offscreencanvas-placeholder-read-blocked-no-crossorigin.html [ Skip ]
 crbug.com/922951 virtual/blink-cors/http/tests/security/cookies/basic.html [ Skip ]
+crbug.com/922951 http/tests/security/cookies/basic.html [ Skip ]
 crbug.com/922951 http/tests/webaudio/autoplay-crossorigin.html [ Skip ]
 crbug.com/922951 images/feature-policy-oversized-images-resize.html [ Skip ]
 crbug.com/922951 media/controls/overflow-menu-hide-on-click-outside-stoppropagation.html [ Skip ]
@@ -5716,7 +5789,12 @@
 crbug.com/970142 virtual/blink-cors/http/tests/security/mixedContent/insecure-css-resources.html [ Failure ]
 
 # Sheriff 2019-06-05
-crbug.com/971031 [ Mac ] fast/dom/timer-throttling-hidden-page.html [ Pass Failure ]
 crbug.com/971252 [ Mac ] external/wpt/media-source/mediasource-play.html [ Pass Timeout ]
 crbug.com/971259 media/controls/volumechange-stopimmediatepropagation.html [ Pass Failure ]
 crbug.com/971262 http/tests/devtools/profiler/live-line-level-heap-profile.js [ Pass Timeout ]
+crbug.com/971319 [ Mac ] media/audio-garbage-collect.html [ Pass Timeout ]
+
+# Sheriff 2019-06-06
+crbug.com/971590 fast/dom/timer-throttling-out-of-view-cross-origin-page.html [ Pass Failure ]
+crbug.com/971590 [ Mac ] fast/dom/timer-throttling-hidden-page.html [ Pass Failure ]
+crbug.com/971612 fast/canvas/OffscreenCanvas-MessageChannel-transfer.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index a8c2f87..836d0a07 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -568,6 +568,11 @@
   },
   {
     "prefix": "omt-worker-fetch",
+    "base": "external/wpt/fetch/",
+    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,PlzDedicatedWorker,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
+  },
+  {
+    "prefix": "omt-worker-fetch",
     "base": "external/wpt/html/browsers/offline/appcache/workers/",
     "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,PlzDedicatedWorker,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
   },
@@ -594,7 +599,7 @@
   {
     "prefix": "omt-worker-fetch",
     "base": "external/wpt/referrer-policy",
-    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
+    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,PlzDedicatedWorker,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
   },
   {
     "prefix": "omt-worker-fetch",
@@ -624,7 +629,7 @@
   {
     "prefix": "omt-worker-fetch",
     "base": "fast/workers",
-    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
+    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,PlzDedicatedWorker,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
   },
   {
     "prefix": "omt-worker-fetch",
diff --git a/third_party/blink/web_tests/fast/writing-mode/japanese-lr-selection.html b/third_party/blink/web_tests/editing/selection/japanese-lr-selection.html
similarity index 100%
rename from third_party/blink/web_tests/fast/writing-mode/japanese-lr-selection.html
rename to third_party/blink/web_tests/editing/selection/japanese-lr-selection.html
diff --git a/third_party/blink/web_tests/fast/writing-mode/japanese-rl-selection.html b/third_party/blink/web_tests/editing/selection/japanese-rl-selection.html
similarity index 100%
rename from third_party/blink/web_tests/fast/writing-mode/japanese-rl-selection.html
rename to third_party/blink/web_tests/editing/selection/japanese-rl-selection.html
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_word_02_rtl_multi_line.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_word_02_rtl_multi_line.html
index 91147ca..48cb10b 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_word_02_rtl_multi_line.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_word_02_rtl_multi_line.html
@@ -68,72 +68,72 @@
   '<div contenteditable dir="rtl">abc def|<div><br></div><div><br></div><div><br></div>uvw xyz</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc def<div>|<br></div><div><br></div><div><br></div>uvw xyz</div>'
-      : '<div contenteditable dir="rtl">abc def<div>|<br></div><div><br></div><div><br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-7 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc def<div>|<br></div><div><br></div><div><br></div>uvw xyz</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc def<div>|<br></div><div><br></div><div><br></div>uvw xyz</div>'
-      : '<div contenteditable dir="rtl">abc def<div>|<br></div><div><br></div><div><br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-8 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc def<div><br>|</div><div><br></div><div><br></div>uvw xyz</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc def<div>|<br></div><div><br></div><div><br></div>uvw xyz</div>'
-      : '<div contenteditable dir="rtl">abc def<div>|<br></div><div><br></div><div><br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-9 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc def<div><br></div>|<div><br></div><div><br></div>uvw xyz</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc def<div><br></div><div>|<br></div><div><br></div>uvw xyz</div>'
-      : '<div contenteditable dir="rtl">abc def<div><br></div><div>|<br></div><div><br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-10 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc def<div><br></div><div>|<br></div><div><br></div>uvw xyz</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc def<div><br></div><div>|<br></div><div><br></div>uvw xyz</div>'
-      : '<div contenteditable dir="rtl">abc def<div><br></div><div>|<br></div><div><br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-11 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc def<div><br></div><div><br>|</div><div><br></div>uvw xyz</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc def<div><br></div><div>|<br></div><div><br></div>uvw xyz</div>'
-      : '<div contenteditable dir="rtl">abc def<div><br></div><div>|<br></div><div><br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-12 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div>|<div><br></div>uvw xyz</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div>|<br></div>uvw xyz</div>'
-      : '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div>|<br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-13 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div>|<br></div>uvw xyz</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div>|<br></div>uvw xyz</div>'
-      : '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div>|<br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-14 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br>|</div>uvw xyz</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div>|<br></div>uvw xyz</div>'
-      : '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div>|<br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="rtl">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-15 rtl left word');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_word_04_rtl_multi_line.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_word_04_rtl_multi_line.html
index a8feb1e2..5c0ad30 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_word_04_rtl_multi_line.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_word_04_rtl_multi_line.html
@@ -68,72 +68,72 @@
   '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0|<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-7 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-8 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br>|</div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-9 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div>|<div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-10 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-11 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br>|</div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-12 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div>|<div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-13 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-14 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br>|</div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-15 rtl left word');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_word_06_rtl_multi_line.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_word_06_rtl_multi_line.html
index 32accc5..350c456 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_word_06_rtl_multi_line.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_word_06_rtl_multi_line.html
@@ -228,80 +228,80 @@
   '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz| <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-27 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz |<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-28 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-29 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br>|</div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-30 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div>|<div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-31 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-32 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br>|</div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-33 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div>|<div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-34 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-35 rtl left word');
 
 selection_test(
   '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br>|</div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'left', 'word'),
   isMac
-      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="rtl">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-36 rtl left word');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_word_02_ltr_multi_line.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_word_02_ltr_multi_line.html
index 2a049f1..82e61e1 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_word_02_ltr_multi_line.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_word_02_ltr_multi_line.html
@@ -68,72 +68,72 @@
   '<div contenteditable dir="ltr">abc def|<div><br></div><div><br></div><div><br></div>uvw xyz</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc def<div>|<br></div><div><br></div><div><br></div>uvw xyz</div>'
-      : '<div contenteditable dir="ltr">abc def<div>|<br></div><div><br></div><div><br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-7 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc def<div>|<br></div><div><br></div><div><br></div>uvw xyz</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc def<div>|<br></div><div><br></div><div><br></div>uvw xyz</div>'
-      : '<div contenteditable dir="ltr">abc def<div>|<br></div><div><br></div><div><br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-8 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc def<div><br>|</div><div><br></div><div><br></div>uvw xyz</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc def<div>|<br></div><div><br></div><div><br></div>uvw xyz</div>'
-      : '<div contenteditable dir="ltr">abc def<div>|<br></div><div><br></div><div><br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-9 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc def<div><br></div>|<div><br></div><div><br></div>uvw xyz</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc def<div><br></div><div>|<br></div><div><br></div>uvw xyz</div>'
-      : '<div contenteditable dir="ltr">abc def<div><br></div><div>|<br></div><div><br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-10 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc def<div><br></div><div>|<br></div><div><br></div>uvw xyz</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc def<div><br></div><div>|<br></div><div><br></div>uvw xyz</div>'
-      : '<div contenteditable dir="ltr">abc def<div><br></div><div>|<br></div><div><br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-11 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc def<div><br></div><div><br>|</div><div><br></div>uvw xyz</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc def<div><br></div><div>|<br></div><div><br></div>uvw xyz</div>'
-      : '<div contenteditable dir="ltr">abc def<div><br></div><div>|<br></div><div><br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-12 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div>|<div><br></div>uvw xyz</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div>|<br></div>uvw xyz</div>'
-      : '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div>|<br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-13 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div>|<br></div>uvw xyz</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div>|<br></div>uvw xyz</div>'
-      : '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div>|<br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-14 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br>|</div>uvw xyz</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div>|<br></div>uvw xyz</div>'
-      : '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div>|<br></div>uvw xyz</div>',
+      ? '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw| xyz</div>'
+      : '<div contenteditable dir="ltr">abc def<div><br></div><div><br></div><div><br></div>uvw |xyz</div>',
   '2-15 ltr right word');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_word_04_ltr_multi_line.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_word_04_ltr_multi_line.html
index 67d98b740..869543f 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_word_04_ltr_multi_line.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_word_04_ltr_multi_line.html
@@ -68,72 +68,72 @@
   '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0|<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-7 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-8 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br>|</div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-9 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div>|<div><br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-10 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-11 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br>|</div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-12 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div>|<div><br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-13 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-14 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br>|</div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |\u05D0\u05D0\u05D0</div>',
   '4-15 ltr right word');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_word_06_ltr_multi_line.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_word_06_ltr_multi_line.html
index a4c7a12..907353cd 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_word_06_ltr_multi_line.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_word_06_ltr_multi_line.html
@@ -228,80 +228,80 @@
   '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz| <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-27 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz |<div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-28 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-29 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br>|</div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div>|<br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-30 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div>|<div><br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-31 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-32 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br>|</div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div>|<br></div><div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-33 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div>|<div><br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-34 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-35 ltr right word');
 
 selection_test(
   '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br>|</div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   selection => selection.modify('move', 'right', 'word'),
   isMac
-      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
-      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div>|<br></div>\u05D0\u05D0\u05D0 kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
+      ? '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0| kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>'
+      : '<div contenteditable dir="ltr">abc \u05D0\u05D0\u05D0 hij \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0 uvw xyz <div><br></div><div><br></div><div><br></div>\u05D0\u05D0\u05D0 |kj \u05D0\u05D0\u05D0 mn opq \u05D0\u05D0\u05D0 \u05D0\u05D0\u05D0</div>',
   '6-36 ltr right word');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/mouse/mouse-selection-horizontal.html b/third_party/blink/web_tests/editing/selection/mouse/mouse-selection-horizontal.html
new file mode 100644
index 0000000..4a98b08
--- /dev/null
+++ b/third_party/blink/web_tests/editing/selection/mouse/mouse-selection-horizontal.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<script src="../../../resources/ahem.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../resources/mouse-selection.js"></script>
+<style>
+html {
+  writing-mode: vertical-rl;
+  font: 20px/20px Ahem;
+  word-wrap: break-word;
+}
+body {
+  margin: 0;
+}
+div {
+  height: 200px;
+  margin: 200px;
+}
+</style>
+<!-- 40 Xs, 4 lines -->
+<div id="div">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</div>
+<script>
+// The expectations are based on windows editing behavior.
+if (window.internals)
+  internals.settings.setEditingBehavior('win');
+
+var node = div.firstChild;
+testMouseSelection(590, 310, 530, 310, node, 5, node, 35,
+                   'Middle of the first line -> Middle of the last line');
+testMouseSelection(590, 310, 550, 500, node, 5, node, 30,
+                   'Middle of the first line -> Outside below the third line');
+testMouseSelection(590, 310, 550, 100, node, 5, node, 20,
+                   'Middel of the first line -> Outside above the third line');
+testMouseSelection(590, 310, 300, 310, node, 5, node, 35,
+                   'Middle of the first line -> Outside left, vertical middle');
+testMouseSelection(590, 310, 300, 500, node, 5, node, 40,
+                   'Middle of the first line -> Outside left, below');
+testMouseSelection(530, 310, 700, 310, node, 5, node, 35,
+                   'Middle of the last line -> Outside right, vertical middle');
+testMouseSelection(530, 310, 570, 100, node, 10, node, 35,
+                   'Middle of the last line -> Outside above the second line');
+testMouseSelection(530, 310, 570, 500, node, 20, node, 35,
+                   'Middle of the last line -> Outside below the second line');
+</script>
diff --git a/third_party/blink/web_tests/editing/selection/mouse/mouse-selection-vertical-lr.html b/third_party/blink/web_tests/editing/selection/mouse/mouse-selection-vertical-lr.html
new file mode 100644
index 0000000..6117e77
--- /dev/null
+++ b/third_party/blink/web_tests/editing/selection/mouse/mouse-selection-vertical-lr.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<script src="../../../resources/ahem.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../resources/mouse-selection.js"></script>
+<style>
+html {
+  writing-mode: vertical-lr;
+  font: 20px/20px Ahem;
+  word-wrap: break-word;
+}
+body {
+  margin: 0;
+}
+div {
+  height: 200px;
+  margin: 200px;
+}
+</style>
+<!-- 40 Xs, 4 lines -->
+<div id="div">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</div>
+<script>
+// The expectations are based on windows editing behavior.
+if (window.internals)
+  internals.settings.setEditingBehavior('win');
+
+var node = div.firstChild;
+testMouseSelection(210, 310, 270, 310, node, 5, node, 35,
+                   'Middle of the first line -> Middle of the last line');
+testMouseSelection(210, 310, 250, 500, node, 5, node, 30,
+                   'Middle of the first line -> Outside below the third line');
+testMouseSelection(210, 310, 250, 100, node, 5, node, 20,
+                   'Middel of the first line -> Outside above the third line');
+testMouseSelection(210, 310, 600, 310, node, 5, node, 35,
+                   'Middle of the first line -> Outside right, vertical middle');
+testMouseSelection(210, 310, 600, 500, node, 5, node, 40,
+                   'Middle of the first line -> Outside right, below');
+testMouseSelection(270, 310, 100, 310, node, 5, node, 35,
+                   'Middle of the last line -> Outside left, vertical middle');
+testMouseSelection(270, 310, 230, 100, node, 10, node, 35,
+                   'Middle of the last line -> Outside above the second line');
+testMouseSelection(270, 310, 230, 500, node, 20, node, 35,
+                   'Middle of the last line -> Outside below the second line');
+</script>
diff --git a/third_party/blink/web_tests/editing/selection/mouse/mouse-selection-vertical-rl.html b/third_party/blink/web_tests/editing/selection/mouse/mouse-selection-vertical-rl.html
new file mode 100644
index 0000000..4a98b08
--- /dev/null
+++ b/third_party/blink/web_tests/editing/selection/mouse/mouse-selection-vertical-rl.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<script src="../../../resources/ahem.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../resources/mouse-selection.js"></script>
+<style>
+html {
+  writing-mode: vertical-rl;
+  font: 20px/20px Ahem;
+  word-wrap: break-word;
+}
+body {
+  margin: 0;
+}
+div {
+  height: 200px;
+  margin: 200px;
+}
+</style>
+<!-- 40 Xs, 4 lines -->
+<div id="div">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</div>
+<script>
+// The expectations are based on windows editing behavior.
+if (window.internals)
+  internals.settings.setEditingBehavior('win');
+
+var node = div.firstChild;
+testMouseSelection(590, 310, 530, 310, node, 5, node, 35,
+                   'Middle of the first line -> Middle of the last line');
+testMouseSelection(590, 310, 550, 500, node, 5, node, 30,
+                   'Middle of the first line -> Outside below the third line');
+testMouseSelection(590, 310, 550, 100, node, 5, node, 20,
+                   'Middel of the first line -> Outside above the third line');
+testMouseSelection(590, 310, 300, 310, node, 5, node, 35,
+                   'Middle of the first line -> Outside left, vertical middle');
+testMouseSelection(590, 310, 300, 500, node, 5, node, 40,
+                   'Middle of the first line -> Outside left, below');
+testMouseSelection(530, 310, 700, 310, node, 5, node, 35,
+                   'Middle of the last line -> Outside right, vertical middle');
+testMouseSelection(530, 310, 570, 100, node, 10, node, 35,
+                   'Middle of the last line -> Outside above the second line');
+testMouseSelection(530, 310, 570, 500, node, 20, node, 35,
+                   'Middle of the last line -> Outside below the second line');
+</script>
diff --git a/third_party/blink/web_tests/editing/selection/resources/mouse-selection.js b/third_party/blink/web_tests/editing/selection/resources/mouse-selection.js
new file mode 100644
index 0000000..fd8c863
--- /dev/null
+++ b/third_party/blink/web_tests/editing/selection/resources/mouse-selection.js
@@ -0,0 +1,43 @@
+function testMouseSelectionOneDirection(
+    start_x, start_y, end_x, end_y,
+    expected_start_container, expected_start_offset,
+    expected_end_container, expected_end_offset, name) {
+  promise_test(() => {
+    return new Promise((resolve, reject) => {
+      if (!window.chrome || !chrome.gpuBenchmarking)
+        return reject();
+      window.getSelection().removeAllRanges();
+      chrome.gpuBenchmarking.pointerActionSequence(
+        [{source: "mouse",
+          actions: [
+            {name: "pointerDown", x: start_x, y: start_y},
+            {name: "pointerMove", x: end_x, y: end_y},
+            {name: "pointerUp"},
+          ],
+        }], resolve);
+    }).then(() => {
+      var selection = window.getSelection();
+      assert_equals(selection.rangeCount, 1, 'rangeCount');
+      var range = selection.getRangeAt(0);
+      assert_equals(range.startContainer, expected_start_container, 'startContainer');
+      assert_equals(range.startOffset, expected_start_offset, 'startOffset');
+      assert_equals(range.endContainer, expected_end_container, 'endContainer');
+      assert_equals(range.endOffset, expected_end_offset, 'endOffset');
+    });
+  }, name);
+}
+
+function testMouseSelection(
+    start_x, start_y, end_x, end_y,
+    expected_start_container, expected_start_offset,
+    expected_end_container, expected_end_offset, name) {
+  testMouseSelectionOneDirection(
+      start_x, start_y, end_x, end_y,
+      expected_start_container, expected_start_offset,
+      expected_end_container, expected_end_offset, name);
+  testMouseSelectionOneDirection(
+      end_x, end_y, start_x, start_y,
+      expected_start_container, expected_start_offset,
+      expected_end_container, expected_end_offset,
+      name + ' Reversed');
+}
diff --git a/third_party/blink/web_tests/fast/writing-mode/vertical-lr-replaced-selection.html b/third_party/blink/web_tests/editing/selection/vertical-lr-replaced-selection.html
similarity index 100%
rename from third_party/blink/web_tests/fast/writing-mode/vertical-lr-replaced-selection.html
rename to third_party/blink/web_tests/editing/selection/vertical-lr-replaced-selection.html
diff --git a/third_party/blink/web_tests/fast/writing-mode/vertical-rl-replaced-selection.html b/third_party/blink/web_tests/editing/selection/vertical-rl-replaced-selection.html
similarity index 100%
rename from third_party/blink/web_tests/fast/writing-mode/vertical-rl-replaced-selection.html
rename to third_party/blink/web_tests/editing/selection/vertical-rl-replaced-selection.html
diff --git a/third_party/blink/web_tests/external/PRESUBMIT_test.py b/third_party/blink/web_tests/external/PRESUBMIT_test.py
index be047433..bb36134 100755
--- a/third_party/blink/web_tests/external/PRESUBMIT_test.py
+++ b/third_party/blink/web_tests/external/PRESUBMIT_test.py
@@ -52,7 +52,7 @@
   def testWPTLintErrors(self):
     # Private LayoutTests APIs are not allowed.
     with open(self._test_file, 'w') as f:
-      f.write('<script>testRunner</script>')
+      f.write('<script>testRunner.notifyDone()</script>')
     mock_input = MockInputApi()
     mock_output = MockOutputApi()
     mock_input.affected_files = [os.path.abspath(self._test_file)]
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 b110737..6ecbabd 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
@@ -250974,6 +250974,12 @@
      {}
     ]
    ],
+   "element-timing/observe-text.html": [
+    [
+     "element-timing/observe-text.html",
+     {}
+    ]
+   ],
    "element-timing/observe-video-poster.html": [
     [
      "element-timing/observe-video-poster.html",
@@ -375335,7 +375341,7 @@
    "visual"
   ],
   "css/css-backgrounds/border-radius-dynamic-from-no-radius-ref.html": [
-   "72ada88416255c18c4e6606bee4b6cf8dc353698",
+   "e0ab6ce4414b107b4ca6a3fce8d6f5976d9bd7db",
    "support"
   ],
   "css/css-backgrounds/border-radius-dynamic-from-no-radius.html": [
@@ -393019,7 +393025,7 @@
    "reftest"
   ],
   "css/css-grid/grid-layout-properties.html": [
-   "b1b0bd87d494e0045c9a56501011428ac1af3ab8",
+   "0debe91f9246f67753cdb89c62ef818cbba73063",
    "testharness"
   ],
   "css/css-grid/grid-model/display-grid.html": [
@@ -421027,7 +421033,7 @@
    "support"
   ],
   "css/css-ui/text-overflow-023.html": [
-   "a8122185dae1232201bd47953e70d6820d439633",
+   "b9c21ed8f22e5380773a6ebdf620913be7aa77e1",
    "testharness"
   ],
   "css/css-ui/text-overflow-024-ref.html": [
@@ -441683,7 +441689,7 @@
    "testharness"
   ],
   "element-timing/background-image-multiple-elements.html": [
-   "a4ad83dbae069f241224219487a31f3abb67ce75",
+   "24f72a67c34d0cc5323c6d4a0acb2e25085ec87a",
    "testharness"
   ],
   "element-timing/background-image-stretched.html": [
@@ -441782,6 +441788,10 @@
    "29fec392a993b793fe824e8a6f6c1a9867740f6c",
    "testharness"
   ],
+  "element-timing/observe-text.html": [
+   "a9a0e30adf353f342ad8bb6a2300ea90beb5d9fa",
+   "testharness"
+  ],
   "element-timing/observe-video-poster.html": [
    "9f82478ea27779d7bb52dd9bc4966d5e5c09f6a3",
    "testharness"
@@ -441803,7 +441813,7 @@
    "support"
   ],
   "element-timing/resources/element-timing-helpers.js": [
-   "3ab4859fb2f8b736c02399dd0fb85b4541f9fd2f",
+   "6c0aec80960ebab60979986434272a96ecc6c674",
    "support"
   ],
   "element-timing/resources/iframe-with-square-sends-entry.html": [
diff --git a/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.actualBoundingBox.html b/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.actualBoundingBox.html
index 3d565e9..484af7c2 100644
--- a/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.actualBoundingBox.html
+++ b/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.actualBoundingBox.html
@@ -19,36 +19,33 @@
 
 <span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
 <p class="output">Actual output:</p>
-<canvas id="c" class="output" width="100" height="50">
-<p class="fallback">FAIL (fallback content)</p>
-</canvas>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 
 <ul id="d"></ul>
 <script>
 var t = async_test("Testing actualBoundingBox");
-_addTest(function (canvas, ctx) {
-  deferTest();
-  var f = new FontFace("CanvasTest", "/fonts/CanvasTest.ttf");
-  document.fonts.add(f);
-  document.fonts.ready.then(() => {
-    step_timeout(t.step_func_done(function () {
-      ctx.font = '50px CanvasTest';
-      ctx.direction = 'ltr';
-      ctx.align = 'left'
-      ctx.baseline = 'alphabetic'
-      // Some platforms may return '-0'.
-      _assertSame(Math.abs(ctx.measureText('A').actualBoundingBoxLeft), 0, "Math.abs(ctx.measureText('A').actualBoundingBoxLeft)", "0");
-      // Different platforms may render text slightly different.
-      _assert(ctx.measureText('A').actualBoundingBoxRight >= 50, "ctx.measureText('A').actualBoundingBoxRight >= 50");
-      _assert(ctx.measureText('A').actualBoundingBoxAscent >= 35, "ctx.measureText('A').actualBoundingBoxAscent >= 35");
-      _assertSame(Math.abs(ctx.measureText('A').actualBoundingBoxDescent), 0, "Math.abs(ctx.measureText('A').actualBoundingBoxDescent)", "0");
+_addTest(function(canvas, ctx) {
 
-      _assertSame(Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft), 0, "Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft)", "0");
-      _assert(ctx.measureText('ABCD').actualBoundingBoxRight >= 200, "ctx.measureText('ABCD').actualBoundingBoxRight >= 200");
-      _assert(ctx.measureText('ABCD').actualBoundingBoxAscent >= 85, "ctx.measureText('ABCD').actualBoundingBoxAscent >= 85");
-      _assert(ctx.measureText('ABCD').actualBoundingBoxDescent >= 37, "ctx.measureText('ABCD').actualBoundingBoxDescent >= 37");
-    }), 0);
-  });
+deferTest();
+step_timeout(t.step_func_done(function () {
+    ctx.font = '50px CanvasTest';
+    ctx.direction = 'ltr';
+    ctx.align = 'left'
+    ctx.baseline = 'alphabetic'
+    // Some platforms may return '-0'.
+    _assertSame(Math.abs(ctx.measureText('A').actualBoundingBoxLeft), 0, "Math.abs(ctx.measureText('A').actualBoundingBoxLeft)", "0");
+    // Different platforms may render text slightly different.
+    _assert(ctx.measureText('A').actualBoundingBoxRight >= 50, "ctx.measureText('A').actualBoundingBoxRight >= 50");
+    _assert(ctx.measureText('A').actualBoundingBoxAscent >= 35, "ctx.measureText('A').actualBoundingBoxAscent >= 35");
+    _assertSame(Math.abs(ctx.measureText('A').actualBoundingBoxDescent), 0, "Math.abs(ctx.measureText('A').actualBoundingBoxDescent)", "0");
+
+    _assertSame(Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft), 0, "Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft)", "0");
+    _assert(ctx.measureText('ABCD').actualBoundingBoxRight >= 200, "ctx.measureText('ABCD').actualBoundingBoxRight >= 200");
+    _assert(ctx.measureText('ABCD').actualBoundingBoxAscent >= 85, "ctx.measureText('ABCD').actualBoundingBoxAscent >= 85");
+    _assert(ctx.measureText('ABCD').actualBoundingBoxDescent >= 37, "ctx.measureText('ABCD').actualBoundingBoxDescent >= 37");
+}), 500);
+
+
 });
 </script>
-</body>>
\ No newline at end of file
+
diff --git a/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.advances.html b/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.advances.html
index af33dfccb..ed8c04d 100644
--- a/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.advances.html
+++ b/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.advances.html
@@ -19,38 +19,35 @@
 
 <span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
 <p class="output">Actual output:</p>
-<canvas id="c" class="output" width="100" height="50">
-<p class="fallback">FAIL (fallback content)</p>
-</canvas>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 
 <ul id="d"></ul>
 <script>
 var t = async_test("Testing width advances");
-_addTest(function (canvas, ctx) {
-  deferTest();
-  var f = new FontFace("CanvasTest", "/fonts/CanvasTest.ttf");
-  document.fonts.add(f);
-  document.fonts.ready.then(() => {
-    step_timeout(t.step_func_done(function () {
-      ctx.font = '50px CanvasTest';
-      ctx.direction = 'ltr';
-      ctx.align = 'left'
-      // Some platforms may return '-0'.
-      _assertSame(Math.abs(ctx.measureText('Hello').advances[0]), 0, "Math.abs(ctx.measureText('Hello').advances[\"" + (0) + "\"])", "0");
-      // Different platforms may render text slightly different.
-      _assert(ctx.measureText('Hello').advances[1] >= 36, "ctx.measureText('Hello').advances[\"" + (1) + "\"] >= 36");
-      _assert(ctx.measureText('Hello').advances[2] >= 58, "ctx.measureText('Hello').advances[\"" + (2) + "\"] >= 58");
-      _assert(ctx.measureText('Hello').advances[3] >= 70, "ctx.measureText('Hello').advances[\"" + (3) + "\"] >= 70");
-      _assert(ctx.measureText('Hello').advances[4] >= 80, "ctx.measureText('Hello').advances[\"" + (4) + "\"] >= 80");
+_addTest(function(canvas, ctx) {
 
-      var tm = ctx.measureText('Hello');
-      _assertSame(ctx.measureText('Hello').advances[0], tm.advances[0], "ctx.measureText('Hello').advances[\"" + (0) + "\"]", "tm.advances[\"" + (0) + "\"]");
-      _assertSame(ctx.measureText('Hello').advances[1], tm.advances[1], "ctx.measureText('Hello').advances[\"" + (1) + "\"]", "tm.advances[\"" + (1) + "\"]");
-      _assertSame(ctx.measureText('Hello').advances[2], tm.advances[2], "ctx.measureText('Hello').advances[\"" + (2) + "\"]", "tm.advances[\"" + (2) + "\"]");
-      _assertSame(ctx.measureText('Hello').advances[3], tm.advances[3], "ctx.measureText('Hello').advances[\"" + (3) + "\"]", "tm.advances[\"" + (3) + "\"]");
-      _assertSame(ctx.measureText('Hello').advances[4], tm.advances[4], "ctx.measureText('Hello').advances[\"" + (4) + "\"]", "tm.advances[\"" + (4) + "\"]");
-    }), 0);
-  });
+deferTest();
+step_timeout(t.step_func_done(function () {
+    ctx.font = '50px CanvasTest';
+    ctx.direction = 'ltr';
+    ctx.align = 'left'
+    // Some platforms may return '-0'.
+    _assertSame(Math.abs(ctx.measureText('Hello').advances[0]), 0, "Math.abs(ctx.measureText('Hello').advances[\""+(0)+"\"])", "0");
+    // Different platforms may render text slightly different.
+    _assert(ctx.measureText('Hello').advances[1] >= 36, "ctx.measureText('Hello').advances[\""+(1)+"\"] >= 36");
+    _assert(ctx.measureText('Hello').advances[2] >= 58, "ctx.measureText('Hello').advances[\""+(2)+"\"] >= 58");
+    _assert(ctx.measureText('Hello').advances[3] >= 70, "ctx.measureText('Hello').advances[\""+(3)+"\"] >= 70");
+    _assert(ctx.measureText('Hello').advances[4] >= 80, "ctx.measureText('Hello').advances[\""+(4)+"\"] >= 80");
+
+    var tm = ctx.measureText('Hello');
+    _assertSame(ctx.measureText('Hello').advances[0], tm.advances[0], "ctx.measureText('Hello').advances[\""+(0)+"\"]", "tm.advances[\""+(0)+"\"]");
+    _assertSame(ctx.measureText('Hello').advances[1], tm.advances[1], "ctx.measureText('Hello').advances[\""+(1)+"\"]", "tm.advances[\""+(1)+"\"]");
+    _assertSame(ctx.measureText('Hello').advances[2], tm.advances[2], "ctx.measureText('Hello').advances[\""+(2)+"\"]", "tm.advances[\""+(2)+"\"]");
+    _assertSame(ctx.measureText('Hello').advances[3], tm.advances[3], "ctx.measureText('Hello').advances[\""+(3)+"\"]", "tm.advances[\""+(3)+"\"]");
+    _assertSame(ctx.measureText('Hello').advances[4], tm.advances[4], "ctx.measureText('Hello').advances[\""+(4)+"\"]", "tm.advances[\""+(4)+"\"]");
+}), 500);
+
+
 });
 </script>
-</body>>
\ No newline at end of file
+
diff --git a/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.baselines.html b/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.baselines.html
index d1ab585..2472889 100644
--- a/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.baselines.html
+++ b/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.baselines.html
@@ -19,31 +19,28 @@
 
 <span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
 <p class="output">Actual output:</p>
-<canvas id="c" class="output" width="100" height="50">
-<p class="fallback">FAIL (fallback content)</p>
-</canvas>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 
 <ul id="d"></ul>
 <script>
 var t = async_test("Testing baselines");
-_addTest(function (canvas, ctx) {
-  deferTest();
-  var f = new FontFace("CanvasTest", "/fonts/CanvasTest.ttf");
-  document.fonts.add(f);
-  document.fonts.ready.then(() => {
-    step_timeout(t.step_func_done(function () {
-      ctx.font = '50px CanvasTest';
-      ctx.direction = 'ltr';
-      ctx.align = 'left'
-      _assertSame(Math.abs(ctx.measureText('A').getBaselines().alphabetic), 0, "Math.abs(ctx.measureText('A').getBaselines().alphabetic)", "0");
-      _assertSame(ctx.measureText('A').getBaselines().ideographic, -39, "ctx.measureText('A').getBaselines().ideographic", "-39");
-      _assertSame(ctx.measureText('A').getBaselines().hanging, 68, "ctx.measureText('A').getBaselines().hanging", "68");
+_addTest(function(canvas, ctx) {
 
-      _assertSame(Math.abs(ctx.measureText('ABCD').getBaselines().alphabetic), 0, "Math.abs(ctx.measureText('ABCD').getBaselines().alphabetic)", "0");
-      _assertSame(ctx.measureText('ABCD').getBaselines().ideographic, -39, "ctx.measureText('ABCD').getBaselines().ideographic", "-39");
-      _assertSame(ctx.measureText('ABCD').getBaselines().hanging, 68, "ctx.measureText('ABCD').getBaselines().hanging", "68");
-    }), 0);
-  });
+deferTest();
+step_timeout(t.step_func_done(function () {
+    ctx.font = '50px CanvasTest';
+    ctx.direction = 'ltr';
+    ctx.align = 'left'
+    _assertSame(Math.abs(ctx.measureText('A').getBaselines().alphabetic), 0, "Math.abs(ctx.measureText('A').getBaselines().alphabetic)", "0");
+    _assertSame(ctx.measureText('A').getBaselines().ideographic, -39, "ctx.measureText('A').getBaselines().ideographic", "-39");
+    _assertSame(ctx.measureText('A').getBaselines().hanging, 68, "ctx.measureText('A').getBaselines().hanging", "68");
+
+    _assertSame(Math.abs(ctx.measureText('ABCD').getBaselines().alphabetic), 0, "Math.abs(ctx.measureText('ABCD').getBaselines().alphabetic)", "0");
+    _assertSame(ctx.measureText('ABCD').getBaselines().ideographic, -39, "ctx.measureText('ABCD').getBaselines().ideographic", "-39");
+    _assertSame(ctx.measureText('ABCD').getBaselines().hanging, 68, "ctx.measureText('ABCD').getBaselines().hanging", "68");
+}), 500);
+
+
 });
 </script>
-</body>>
\ No newline at end of file
+
diff --git a/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.emHeights.html b/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.emHeights.html
index a12f719d..36087e7 100644
--- a/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.emHeights.html
+++ b/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.emHeights.html
@@ -24,26 +24,23 @@
 <ul id="d"></ul>
 <script>
 var t = async_test("Testing emHeights");
-
 _addTest(function(canvas, ctx) {
-  deferTest();
-  var f = new FontFace("CanvasTest", "/fonts/CanvasTest.ttf");
-  document.fonts.add(f);
-  document.fonts.ready.then(() => {
-    step_timeout(t.step_func_done(function () {
-      ctx.font = '50px CanvasTest';
-      ctx.direction = 'ltr';
-      ctx.align = 'left'
-      ctx.baseline = 'alphabetic'
-      _assertSame(ctx.measureText('A').emHeightAscent, 37.5, "ctx.measureText('A').emHeightAscent", "37.5");
-      _assertSame(ctx.measureText('A').emHeightDescent, 12.5, "ctx.measureText('A').emHeightDescent", "12.5");
-      _assertSame(ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent, 50, "ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent", "50");
 
-      _assertSame(ctx.measureText('ABCD').emHeightAscent, 37.5, "ctx.measureText('ABCD').emHeightAscent", "37.5");
-      _assertSame(ctx.measureText('ABCD').emHeightDescent, 12.5, "ctx.measureText('ABCD').emHeightDescent", "12.5");
-      _assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 50, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "50");
-    }), 0);
-  });
+deferTest();
+step_timeout(t.step_func_done(function () {
+    ctx.font = '50px CanvasTest';
+    ctx.direction = 'ltr';
+    ctx.align = 'left'
+    _assertSame(ctx.measureText('A').emHeightAscent, 37.5, "ctx.measureText('A').emHeightAscent", "37.5");
+    _assertSame(ctx.measureText('A').emHeightDescent, 12.5, "ctx.measureText('A').emHeightDescent", "12.5");
+    _assertSame(ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent, 50, "ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent", "50");
+
+    _assertSame(ctx.measureText('ABCD').emHeightAscent, 37.5, "ctx.measureText('ABCD').emHeightAscent", "37.5");
+    _assertSame(ctx.measureText('ABCD').emHeightDescent, 12.5, "ctx.measureText('ABCD').emHeightDescent", "12.5");
+    _assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 50, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "50");
+}), 500);
+
+
 });
 </script>
-</body>>
+
diff --git a/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.fontBoundingBox.html b/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.fontBoundingBox.html
index f951040..1c9bfc49 100644
--- a/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.fontBoundingBox.html
+++ b/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.fontBoundingBox.html
@@ -19,30 +19,26 @@
 
 <span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
 <p class="output">Actual output:</p>
-<canvas id="c" class="output" width="100" height="50">
-<p class="fallback">FAIL (fallback content)</p>
-</canvas>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 
 <ul id="d"></ul>
 <script>
 var t = async_test("Testing fontBoundingBox");
-_addTest(function (canvas, ctx) {
-  deferTest();
-  var f = new FontFace("CanvasTest", "/fonts/CanvasTest.ttf");
-  document.fonts.add(f);
-  document.fonts.ready.then(() => {
-    step_timeout(t.step_func_done(function () {
-      ctx.font = '50px CanvasTest';
-      ctx.direction = 'ltr';
-      ctx.align = 'left'
-      ctx.baseline = 'alphabetic'
-      _assertSame(ctx.measureText('A').fontBoundingBoxAscent, 85, "ctx.measureText('A').fontBoundingBoxAscent", "85");
-      _assertSame(ctx.measureText('A').fontBoundingBoxDescent, 39, "ctx.measureText('A').fontBoundingBoxDescent", "39");
+_addTest(function(canvas, ctx) {
 
-      _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 85, "ctx.measureText('ABCD').fontBoundingBoxAscent", "85");
-      _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 39, "ctx.measureText('ABCD').fontBoundingBoxDescent", "39");
-    }), 0);
-  });
+deferTest();
+step_timeout(t.step_func_done(function () {
+    ctx.font = '50px CanvasTest';
+    ctx.direction = 'ltr';
+    ctx.align = 'left'
+    _assertSame(ctx.measureText('A').fontBoundingBoxAscent, 85, "ctx.measureText('A').fontBoundingBoxAscent", "85");
+    _assertSame(ctx.measureText('A').fontBoundingBoxDescent, 39, "ctx.measureText('A').fontBoundingBoxDescent", "39");
+
+    _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 85, "ctx.measureText('ABCD').fontBoundingBoxAscent", "85");
+    _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 39, "ctx.measureText('ABCD').fontBoundingBoxDescent", "39");
+}), 500);
+
+
 });
 </script>
-</body>>
\ No newline at end of file
+
diff --git a/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.width.basic.html b/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.width.basic.html
index 1808cfe..1cfe164 100644
--- a/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.width.basic.html
+++ b/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.width.basic.html
@@ -19,28 +19,25 @@
 
 <span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
 <p class="output">Actual output:</p>
-<canvas id="c" class="output" width="100" height="50">
-<p class="fallback">FAIL (fallback content)</p>
-</canvas>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 
 <ul id="d"></ul>
 <script>
 var t = async_test("");
-_addTest(function (canvas, ctx) {
-  deferTest();
-  var f = new FontFace("CanvasTest", "/fonts/CanvasTest.ttf");
-  document.fonts.add(f);
-  document.fonts.ready.then(() => {
-    step_timeout(t.step_func_done(function () {
-      ctx.font = '50px CanvasTest';
-      _assertSame(ctx.measureText('A').width, 50, "ctx.measureText('A').width", "50");
-      _assertSame(ctx.measureText('AA').width, 100, "ctx.measureText('AA').width", "100");
-      _assertSame(ctx.measureText('ABCD').width, 200, "ctx.measureText('ABCD').width", "200");
+_addTest(function(canvas, ctx) {
 
-      ctx.font = '100px CanvasTest';
-      _assertSame(ctx.measureText('A').width, 100, "ctx.measureText('A').width", "100");
-    }), 0);
-  });
-})
+deferTest();
+step_timeout(t.step_func_done(function () {
+    ctx.font = '50px CanvasTest';
+    _assertSame(ctx.measureText('A').width, 50, "ctx.measureText('A').width", "50");
+    _assertSame(ctx.measureText('AA').width, 100, "ctx.measureText('AA').width", "100");
+    _assertSame(ctx.measureText('ABCD').width, 200, "ctx.measureText('ABCD').width", "200");
+
+    ctx.font = '100px CanvasTest';
+    _assertSame(ctx.measureText('A').width, 100, "ctx.measureText('A').width", "100");
+}), 500);
+
+
+});
 </script>
-</body>>
\ No newline at end of file
+
diff --git a/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.width.empty.html b/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.width.empty.html
index b1dcad6..add8ce5b 100644
--- a/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.width.empty.html
+++ b/third_party/blink/web_tests/external/wpt/2dcontext/drawing-text-to-the-canvas/2d.text.measure.width.empty.html
@@ -19,23 +19,20 @@
 
 <span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
 <p class="output">Actual output:</p>
-<canvas id="c" class="output" width="100" height="50">
-<p class="fallback">FAIL (fallback content)</p>
-</canvas>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 
 <ul id="d"></ul>
 <script>
 var t = async_test("The empty string has zero width");
-_addTest(function (canvas, ctx) {
-  deferTest();
-  var f = new FontFace("CanvasTest", "/fonts/CanvasTest.ttf");
-  document.fonts.add(f);
-  document.fonts.ready.then(() => {
-    step_timeout(t.step_func_done(function () {
-      ctx.font = '50px CanvasTest';
-      _assertSame(ctx.measureText("").width, 0, "ctx.measureText(\"\").width", "0");
-    }), 0);
-  });
+_addTest(function(canvas, ctx) {
+
+deferTest();
+step_timeout(t.step_func_done(function () {
+    ctx.font = '50px CanvasTest';
+    _assertSame(ctx.measureText("").width, 0, "ctx.measureText(\"\").width", "0");
+}), 500);
+
+
 });
 </script>
-</body>>
\ No newline at end of file
+
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/stacking-context/composite-change-after-scroll-preserves-stacking-order-ref.html b/third_party/blink/web_tests/external/wpt/css/CSS2/stacking-context/composite-change-after-scroll-preserves-stacking-order-ref.html
new file mode 100644
index 0000000..9bf223c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/stacking-context/composite-change-after-scroll-preserves-stacking-order-ref.html
@@ -0,0 +1,19 @@
+<!doctype HTML>
+<link rel="author" title="Chris Harrelson" href="chrishtr@chromium.org">
+<style>
+    #one, #two {
+        width: 200px; height: 200px; background: lightblue; position: relative
+    }
+    #one {
+        background: lightblue
+    }
+    #two {
+        background: lightgray;
+        margin-top: -200px;
+    }
+</style>
+<div id=scroller style="overflow: scroll; width: 300px; height: 300px; will-change: transform">
+  <div id=one></div>
+  <div id=two></div>
+  <div id=spacer style="width: 50px; height: 5000px"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/stacking-context/composite-change-after-scroll-preserves-stacking-order.html b/third_party/blink/web_tests/external/wpt/css/CSS2/stacking-context/composite-change-after-scroll-preserves-stacking-order.html
new file mode 100644
index 0000000..1abf6f2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/stacking-context/composite-change-after-scroll-preserves-stacking-order.html
@@ -0,0 +1,39 @@
+<!doctype HTML>
+<link rel="author" title="Chris Harrelson" href="chrishtr@chromium.org">
+<link rel="match" href="composite-change-after-scroll-preserves-stacking-order-ref.html">
+<link rel="help" href="https://www.w3.org/TR/CSS2/zindex.html"/>
+<script src="/common/reftest-wait.js"></script>
+<script src="/web-animations/testcommon.js"></script>
+<style>
+    #one, #two {
+        width: 200px; height: 200px; background: lightblue; position: relative
+    }
+    #one {
+        background: lightblue
+    }
+    #two {
+        background: lightgray;
+        margin-top: -200px;
+    }
+</style>
+<html class=reftest-wait>
+  <div id=scroller style="overflow: scroll; width: 300px; height: 300px; will-change: transform">
+    <div id=one></div>
+    <div id=two></div>
+    <div id=spacer style="width: 50px; height: 5000px"></div>
+  </div>
+</html>
+<script>
+  onload = () => {
+    waitForAnimationFrames(2).then(() => {
+      scroller.scrollBy(0, 1000);
+      waitForAnimationFrames(2).then(() => {
+        one.style = 'will-change: transform';
+        waitForAnimationFrames(2).then(() => {
+          scroller.scrollBy(0, -1000);
+          takeScreenshot();
+        });
+      });
+    });
+  };
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/contain-size-select-002.html b/third_party/blink/web_tests/external/wpt/css/css-contain/contain-size-select-002.html
new file mode 100644
index 0000000..88d37323
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/contain-size-select-002.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Containment Test: Size containment on select</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-contain-1/#containment-size">
+<link rel="match" href="reference/contain-size-select-001-ref.html">
+<meta name=assert content="Check that setting 'contain: size' on a <select> elements causes it to be sized as having no contents.">
+<style>
+select {
+  color: white;
+  background: white;
+}
+</style>
+<p>Test passes if it has the same output than the reference.</p>
+<select id="target">
+  <option>AVeryLongOption</option>
+  <option>Another Option</option>
+</select>
+<script>
+  window.requestAnimationFrame( () => {
+    target.style.contain = "size";
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-layout-properties.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-layout-properties.html
index b1b0bd87..0debe91f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-layout-properties.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-layout-properties.html
@@ -11,6 +11,7 @@
   <meta name="assert" content="Test checks that css properties of grid layout exist.">
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
+  <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
   <style>
     #container {
         width: 800px;
@@ -34,6 +35,8 @@
   </div>
 
   <script>
+  setup({explicit_done: true});
+  document.fonts.ready.then(()=> {
     var myDiv = document.getElementById('myDiv')
 
     test(function(){
@@ -228,6 +231,8 @@
         'reset': ['auto', 'auto'],
       },
     })
+    done();
+  });
   </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-logical/parsing/max-block-size-computed.html b/third_party/blink/web_tests/external/wpt/css/css-logical/parsing/max-block-size-computed.html
new file mode 100644
index 0000000..d7d7f2f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-logical/parsing/max-block-size-computed.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Logical Properties and Values: getComputedValue().maxBlockSize</title>
+<link rel="help" href="https://drafts.csswg.org/css-logical/#propdef-max-block-size">
+<meta name="assert" content="Computed max-block-size is the specified keyword, or the length-percentage made absolute.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+  #target {
+    font-size: 40px;
+  }
+</style>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("max-block-size", "none");
+
+test_computed_value("max-block-size", "10px");
+test_computed_value("max-block-size", "20%");
+test_computed_value("max-block-size", "calc(10px + 0.5em)", "30px");
+test_computed_value("max-block-size", "calc(10px - 0.5em)", "0px");
+test_computed_value("max-block-size", "calc(20% + 10px)");
+
+test_computed_value("max-block-size", "min-content");
+test_computed_value("max-block-size", "max-content");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-logical/parsing/max-inline-size-computed.html b/third_party/blink/web_tests/external/wpt/css/css-logical/parsing/max-inline-size-computed.html
new file mode 100644
index 0000000..c5d1fe6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-logical/parsing/max-inline-size-computed.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Logical Properties and Values: getComputedValue().maxInlineSize</title>
+<link rel="help" href="https://drafts.csswg.org/css-logical/#propdef-max-inline-size">
+<meta name="assert" content="Computed max-inline-size is the specified keyword, or the length-percentage made absolute.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+  #target {
+    font-size: 40px;
+  }
+</style>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("max-inline-size", "none");
+
+test_computed_value("max-inline-size", "10px");
+test_computed_value("max-inline-size", "20%");
+test_computed_value("max-inline-size", "calc(10px + 0.5em)", "30px");
+test_computed_value("max-inline-size", "calc(10px - 0.5em)", "0px");
+test_computed_value("max-inline-size", "calc(20% + 10px)");
+
+test_computed_value("max-inline-size", "min-content");
+test_computed_value("max-inline-size", "max-content");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-logical/parsing/min-block-size-computed.html b/third_party/blink/web_tests/external/wpt/css/css-logical/parsing/min-block-size-computed.html
new file mode 100644
index 0000000..d08a0f6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-logical/parsing/min-block-size-computed.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Logical Properties and Values: getComputedValue().minBlockSize</title>
+<link rel="help" href="https://drafts.csswg.org/css-logical/#propdef-min-block-size">
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#min-size-auto">
+<meta name="assert" content="Computed min-block-size is the specified keyword, or the length-percentage made absolute.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+  #target {
+    font-size: 40px;
+  }
+  #container {
+    display: flex;
+  }
+  #box {
+    min-block-size: auto;
+  }
+</style>
+</head>
+<body>
+<div id="target"></div>
+<div id="container">
+  <div id="box"></div>
+</div>
+<script>
+test_computed_value("min-block-size", "auto", "0px");
+
+test_computed_value("min-block-size", "10px");
+test_computed_value("min-block-size", "20%");
+test_computed_value("min-block-size", "calc(10px + 0.5em)", "30px");
+test_computed_value("min-block-size", "calc(10px - 0.5em)", "0px");
+test_computed_value("min-block-size", "calc(20% + 10px)");
+
+test_computed_value("min-block-size", "min-content");
+test_computed_value("min-block-size", "max-content");
+
+test(() => {
+  const picture = document.getElementById('box');
+  assert_equals(getComputedStyle(picture).minBlockSize, 'auto');
+}, 'min-block-size auto computes to auto with flex layout.');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-logical/parsing/min-inline-size-computed.html b/third_party/blink/web_tests/external/wpt/css/css-logical/parsing/min-inline-size-computed.html
new file mode 100644
index 0000000..ac17044f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-logical/parsing/min-inline-size-computed.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Logical Properties and Values: getComputedValue().minInlineSize</title>
+<link rel="help" href="https://drafts.csswg.org/css-logical/#propdef-min-inline-size">
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#min-size-auto">
+<meta name="assert" content="Computed min-inline-size is the specified keyword, or the length-percentage made absolute.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+  #target {
+    font-size: 40px;
+  }
+  #container {
+    display: flex;
+  }
+  #box {
+    min-inline-size: auto;
+  }
+</style>
+</head>
+<body>
+<div id="target"></div>
+<div id="container">
+  <div id="box"></div>
+</div>
+<script>
+test_computed_value("min-inline-size", "auto", "0px");
+
+test_computed_value("min-inline-size", "10px");
+test_computed_value("min-inline-size", "20%");
+test_computed_value("min-inline-size", "calc(10px + 0.5em)", "30px");
+test_computed_value("min-inline-size", "calc(10px - 0.5em)", "0px");
+test_computed_value("min-inline-size", "calc(20% + 10px)");
+
+test_computed_value("min-inline-size", "min-content");
+test_computed_value("min-inline-size", "max-content");
+
+test(() => {
+  const picture = document.getElementById('box');
+  assert_equals(getComputedStyle(picture).minInlineSize, 'auto');
+}, 'min-inline-size auto computes to auto with flex layout.');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/parsing/scroll-margin-block-invalid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/parsing/scroll-margin-block-invalid-expected.txt
deleted file mode 100644
index b67940e8..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/parsing/scroll-margin-block-invalid-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-PASS e.style['scroll-margin-block-start'] = "auto" should not set the property value
-FAIL e.style['scroll-margin-block-start'] = "20%" should not set the property value assert_equals: expected "" but got "20%"
-PASS e.style['scroll-margin-block-start'] = "-30%" should not set the property value
-PASS e.style['scroll-margin-block-start'] = "1px 2px" should not set the property value
-PASS e.style['scroll-margin-block-end'] = "auto" should not set the property value
-FAIL e.style['scroll-margin-block-end'] = "20%" should not set the property value assert_equals: expected "" but got "20%"
-PASS e.style['scroll-margin-block-end'] = "-30%" should not set the property value
-PASS e.style['scroll-margin-block-end'] = "1px 2px" should not set the property value
-PASS e.style['scroll-margin-block'] = "auto" should not set the property value
-PASS e.style['scroll-margin-block'] = "20%" should not set the property value
-PASS e.style['scroll-margin-block'] = "-30%" should not set the property value
-PASS e.style['scroll-margin-block'] = "1px 2px 3px" should not set the property value
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/parsing/scroll-margin-inline-invalid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/parsing/scroll-margin-inline-invalid-expected.txt
deleted file mode 100644
index 04b046ef..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/parsing/scroll-margin-inline-invalid-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-PASS e.style['scroll-margin-inline-start'] = "auto" should not set the property value
-FAIL e.style['scroll-margin-inline-start'] = "20%" should not set the property value assert_equals: expected "" but got "20%"
-PASS e.style['scroll-margin-inline-start'] = "-30%" should not set the property value
-PASS e.style['scroll-margin-inline-start'] = "1px 2px" should not set the property value
-PASS e.style['scroll-margin-inline-end'] = "auto" should not set the property value
-FAIL e.style['scroll-margin-inline-end'] = "20%" should not set the property value assert_equals: expected "" but got "20%"
-PASS e.style['scroll-margin-inline-end'] = "-30%" should not set the property value
-PASS e.style['scroll-margin-inline-end'] = "1px 2px" should not set the property value
-PASS e.style['scroll-margin-inline'] = "auto" should not set the property value
-PASS e.style['scroll-margin-inline'] = "20%" should not set the property value
-PASS e.style['scroll-margin-inline'] = "-30%" should not set the property value
-PASS e.style['scroll-margin-inline'] = "1px 2px 3px" should not set the property value
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/parsing/scroll-margin-invalid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/parsing/scroll-margin-invalid-expected.txt
deleted file mode 100644
index 59aa0e3..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/parsing/scroll-margin-invalid-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-This is a testharness.js-based test.
-PASS e.style['scroll-margin-top'] = "auto" should not set the property value
-FAIL e.style['scroll-margin-top'] = "20%" should not set the property value assert_equals: expected "" but got "20%"
-PASS e.style['scroll-margin-top'] = "-30%" should not set the property value
-PASS e.style['scroll-margin-top'] = "1px 2px" should not set the property value
-PASS e.style['scroll-margin-right'] = "auto" should not set the property value
-FAIL e.style['scroll-margin-right'] = "20%" should not set the property value assert_equals: expected "" but got "20%"
-PASS e.style['scroll-margin-right'] = "-30%" should not set the property value
-PASS e.style['scroll-margin-right'] = "1px 2px" should not set the property value
-PASS e.style['scroll-margin-bottom'] = "auto" should not set the property value
-FAIL e.style['scroll-margin-bottom'] = "20%" should not set the property value assert_equals: expected "" but got "20%"
-PASS e.style['scroll-margin-bottom'] = "-30%" should not set the property value
-PASS e.style['scroll-margin-bottom'] = "1px 2px" should not set the property value
-PASS e.style['scroll-margin-left'] = "auto" should not set the property value
-FAIL e.style['scroll-margin-left'] = "20%" should not set the property value assert_equals: expected "" but got "20%"
-PASS e.style['scroll-margin-left'] = "-30%" should not set the property value
-PASS e.style['scroll-margin-left'] = "1px 2px" should not set the property value
-PASS e.style['scroll-margin'] = "auto" should not set the property value
-PASS e.style['scroll-margin'] = "20%" should not set the property value
-PASS e.style['scroll-margin'] = "-30%" should not set the property value
-PASS e.style['scroll-margin'] = "1px 2px 3px 4px 5px" should not set the property value
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/text-overflow-023.html b/third_party/blink/web_tests/external/wpt/css/css-ui/text-overflow-023.html
index a812218..b9c21ed 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/text-overflow-023.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/text-overflow-023.html
@@ -15,6 +15,7 @@
 -->
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
 <style>
 #parent {
   position: absolute;
@@ -27,9 +28,13 @@
 </style>
 <div id=parent>&nbsp;&nbsp;<span id=target>&nbsp;&nbsp;</span></div>
 <script>
-test(
-  function() {
-    var e = document.elementFromPoint(125,25);
-    assert_equals(e.id,"target", "the element targeted by a hit on the ellipsis is the elided inline.");
-  }, "Checks hit testing on the ellipsis");
+setup({explicit_done: true});
+document.fonts.ready.then(()=> {
+  test(
+    function() {
+      var e = document.elementFromPoint(125,25);
+      assert_equals(e.id,"target", "the element targeted by a hit on the ellipsis is the elided inline.");
+    }, "Checks hit testing on the ellipsis");
+  done();
+});
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/background-image-multiple-elements.html b/third_party/blink/web_tests/external/wpt/element-timing/background-image-multiple-elements.html
index a4ad83d..24f72a6 100644
--- a/third_party/blink/web_tests/external/wpt/element-timing/background-image-multiple-elements.html
+++ b/third_party/blink/web_tests/external/wpt/element-timing/background-image-multiple-elements.html
@@ -29,7 +29,8 @@
     let beforeRender = performance.now();
     let numObservedElements = 0;
     let observedDiv1 = false;
-    let observedDiv2 = false;
+    let observedDiv2Img = false;
+    let observedDiv2Txt = false;
     const index = window.location.href.lastIndexOf('/');
     const pathname = window.location.href.substring(0, index) +
         '/resources/square100.png';
@@ -46,19 +47,29 @@
             checkNaturalSize(entry, 100, 100);
           }
           else if (entry.id == 'div2') {
-            observedDiv2 = true;
-            checkElement(entry, pathname, 'et2', 'div2', beforeRender,
+            // Check image entry.
+            if (entry.name !== 'text-paint') {
+              observedDiv2Img = true;
+              checkElement(entry, pathname, 'et2', 'div2', beforeRender,
+                  document.getElementById('div2'));
+              // Div is below div1, on the left.
+              checkRect(entry, [0, 200, 100, 200]);
+              checkNaturalSize(entry, 100, 100);
+            }
+            // Check the text entry.
+            else {
+              observedDiv2Txt = true;
+              checkTextElement(entry, 'et2', 'div2', beforeRender,
                 document.getElementById('div2'));
-            // Div is below div1, on the left.
-            checkRect(entry, [0, 200, 100, 200]);
-            checkNaturalSize(entry, 100, 100);
+            }
           }
           else {
             assert_unreached("Should not observe other elements!");
           }
-          if (numObservedElements === 2) {
+          if (numObservedElements === 3) {
             assert_true(observedDiv1);
-            assert_true(observedDiv2);
+            assert_true(observedDiv2Img);
+            assert_true(observedDiv2Txt);
             t.done();
           }
         });
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-text.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-text.html
new file mode 100644
index 0000000..a9a0e30a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-text.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Element Timing: observe text</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/element-timing-helpers.js"></script>
+<script>
+  async_test((t) => {
+    if (!window.PerformanceElementTiming) {
+      assert_unreached("PerformanceElementTiming is not implemented");
+    }
+    let paragraph;
+    let beforeRender;
+    const observer = new PerformanceObserver(
+      t.step_func_done((entryList) => {
+        assert_equals(entryList.getEntries().length, 1);
+        checkTextElement(entryList.getEntries()[0], 'my_text', 'text_id', beforeRender, paragraph);
+      })
+    );
+    observer.observe({entryTypes: ['element']});
+    // We add the iframe during onload to be sure that the observer is registered
+    // in time for it to observe the element timing.
+    window.onload = () => {
+      paragraph = document.createElement('p');
+      paragraph.innerHTML = 'This is text I care about';
+      paragraph.setAttribute('elementtiming', 'my_text');
+      paragraph.setAttribute('id', 'text_id');
+      document.body.appendChild(paragraph);
+      beforeRender = performance.now();
+    };
+  }, 'Paragraph with elementtiming attribute is observed.');
+</script>
+
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js b/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js
index 3ab4859..6c0aec8 100644
--- a/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js
+++ b/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js
@@ -2,7 +2,6 @@
 function checkElementInternal(entry, expectedUrl, expectedIdentifier, expectedID, beforeRender,
     expectedElement) {
   assert_equals(entry.entryType, 'element');
-  assert_equals(entry.name, 'image-paint');
   assert_equals(entry.url, expectedUrl);
   assert_equals(entry.identifier, expectedIdentifier);
   assert_equals(entry.duration, 0);
@@ -19,6 +18,7 @@
     expectedElement) {
   checkElementInternal(entry, expectedUrl, expectedIdentifier, expectedID, beforeRender,
       expectedElement);
+  assert_equals(entry.name, 'image-paint');
   const rt_entries = performance.getEntriesByName(expectedUrl, 'resource');
   assert_equals(rt_entries.length, 1);
   assert_equals(rt_entries[0].responseEnd, entry.responseEnd);
@@ -28,6 +28,7 @@
     expectedID, beforeRender, expectedElement) {
   checkElementInternal(entry, expectedUrl, expectedIdentifier, expectedID, beforeRender,
       expectedElement);
+  assert_equals(entry.name, 'image-paint');
   // No associated resource from ResourceTiming, so the responseEnd should be 0.
   assert_equals(entry.responseEnd, 0);
 }
@@ -49,3 +50,11 @@
   assert_equals(entry.naturalWidth, width);
   assert_equals(entry.naturalHeight, height);
 }
+
+function checkTextElement(entry, expectedIdentifier, expectedID, beforeRender,
+    expectedElement) {
+  checkElementInternal(entry, '', expectedIdentifier, expectedID, beforeRender,
+      expectedElement);
+  assert_equals(entry.name, 'text-paint');
+  assert_equals(entry.responseEnd, 0);
+}
diff --git a/third_party/blink/web_tests/external/wpt/native-file-system/FileSystemBaseHandle-copyTo.tentative.window.js b/third_party/blink/web_tests/external/wpt/native-file-system/FileSystemBaseHandle-copyTo.tentative.window.js
deleted file mode 100644
index 972d1da..0000000
--- a/third_party/blink/web_tests/external/wpt/native-file-system/FileSystemBaseHandle-copyTo.tentative.window.js
+++ /dev/null
@@ -1,132 +0,0 @@
-// META: script=resources/test-helpers.js
-promise_test(async t => cleanupSandboxedFileSystem(),
-    'Cleanup to setup test environment');
-
-promise_test(async t => {
-    const dir = await FileSystemDirectoryHandle.getSystemDirectory({ type: 'sandbox' });
-    const old_handle = await createFileWithContents(t, 'old-file', '12345', dir);
-    const new_handle = await old_handle.copyTo(dir, 'new-name');
-    t.add_cleanup(() => new_handle.remove());
-
-    // Verify new file.
-    assert_true(new_handle.isFile);
-    assert_false(new_handle.isDirectory);
-    assert_equals(new_handle.name, 'new-name');
-    assert_equals(await getFileContents(new_handle), '12345');
-
-    // And verify old file is still around as well.
-    assert_equals(await getFileContents(old_handle), '12345');
-
-    // Verify directory entries.
-    assert_array_equals(await getSortedDirectoryEntries(dir), ['new-name', 'old-file']);
-}, 'copyTo() into the same parent directory');
-
-promise_test(async t => {
-    const dir = await FileSystemDirectoryHandle.getSystemDirectory({ type: 'sandbox' });
-    const old_handle = await createFileWithContents(t, 'old-file', '12345');
-    const target_dir = await dir.getDirectory('dir-name', { create: true });
-    t.add_cleanup(() => target_dir.removeRecursively());
-
-    const new_handle = await old_handle.copyTo(target_dir);
-
-    // Verify new file.
-    assert_true(new_handle.isFile);
-    assert_false(new_handle.isDirectory);
-    assert_equals(new_handle.name, 'old-file');
-    assert_equals(await getFileContents(new_handle), '12345');
-
-    // And verify old file is still around as well.
-    assert_equals(await getFileContents(old_handle), '12345');
-
-    // Verify directory entries.
-    assert_array_equals(await getSortedDirectoryEntries(dir), ['dir-name/', 'old-file']);
-    assert_array_equals(await getSortedDirectoryEntries(target_dir), ['old-file']);
-}, 'copyTo() to copy a file into a sub-directory');
-
-
-promise_test(async t => {
-    const dir = await FileSystemDirectoryHandle.getSystemDirectory({ type: 'sandbox' });
-    const handle = await createFileWithContents(t, 'old-file', '12345', dir);
-
-    await promise_rejects(t, 'InvalidModificationError', handle.copyTo(dir));
-    await promise_rejects(t, 'InvalidModificationError', handle.copyTo(dir, handle.name));
-
-    // Verify file still exists.
-    assert_equals(await getFileContents(handle), '12345');
-    assert_array_equals(await getSortedDirectoryEntries(dir), ['old-file']);
-}, 'copyTo() with existing name and parent should fail');
-
-promise_test(async t => {
-    const dir = await FileSystemDirectoryHandle.getSystemDirectory({ type: 'sandbox' });
-    const handle = await createFileWithContents(t, 'old-file', '12345', dir);
-    const target_handle = await createFileWithContents(t, 'target', 'abc', dir);
-
-    await handle.copyTo(dir, target_handle.name);
-
-    // Verify state of files.
-    assert_equals(await getFileContents(handle), '12345');
-    assert_equals(await getFileContents(target_handle), '12345');
-    assert_array_equals(await getSortedDirectoryEntries(dir), ['old-file', 'target']);
-}, 'copyTo() when target file already exists should overwrite target');
-
-promise_test(async t => {
-    const dir = await FileSystemDirectoryHandle.getSystemDirectory({ type: 'sandbox' });
-
-    const subdir_name = 'subdir-name';
-    const subdir = await createDirectory(t, subdir_name, /*parent=*/dir);
-
-    const file_name = 'file-name';
-    const file = await createEmptyFile(t, file_name, /*parent=*/subdir);
-
-    // An empty name indicates that the filename should remain unchanged.
-    await file.copyTo(dir, /*name=*/"");
-    await dir.getFile(file_name);
-}, `copyTo() when target is empty`);
-
-promise_test(async t => {
-    const dir = await FileSystemDirectoryHandle.getSystemDirectory({ type: 'sandbox' });
-
-    const subdir_name = 'subdir-name';
-    const subdir = await createDirectory(t, subdir_name, /*parent=*/dir);
-
-    const file_name = 'file-name';
-    const file = await createEmptyFile(t, file_name, /*parent=*/subdir);
-
-    await promise_rejects(t, 'SecurityError', file.copyTo(dir, /*name=*/kCurrentDirectory));
-}, `copyTo() when target is ${kCurrentDirectory}`);
-
-promise_test(async t => {
-    const dir = await FileSystemDirectoryHandle.getSystemDirectory({ type: 'sandbox' });
-
-    const first_subdir_name = 'first-subdir-name';
-    const first_subdir = await createDirectory(t, first_subdir_name, /*parent=*/dir);
-
-    const second_subdir_name = 'second-subdir-name';
-    const second_subdir = await createDirectory(t, second_subdir_name, /*parent=*/first_subdir);
-
-    const file_name = 'file-name';
-    const file = await createEmptyFile(t, file_name, /*parent=*/second_subdir);
-
-    await promise_rejects(t, 'SecurityError', file.copyTo(first_subdir, /*name=*/kParentDirectory));
-}, `copyTo() when target is ${kParentDirectory}`);
-
-promise_test(async t => {
-    const dir = await FileSystemDirectoryHandle.getSystemDirectory({ type: 'sandbox' });
-
-    const file_name = 'file-name';
-    const file = await createEmptyFile(t, file_name, /*parent=*/dir);
-
-    const first_subdir_name = 'first-subdir-name';
-    const first_subdir = await createDirectory(t, first_subdir_name, /*parent=*/dir);
-
-    const second_subdir_name = 'second-subdir-name';
-    const second_subdir = await createDirectory(t, second_subdir_name, /*parent=*/first_subdir);
-
-    for (let i = 0; i < kPathSeparators.length; ++i) {
-        const path_with_separator = `${second_subdir_name}${kPathSeparators[i]}${file_name}`;
-        await promise_rejects(t, 'SecurityError', file.copyTo(first_subdir, path_with_separator),
-            `copyTo() must reject names containing "${kPathSeparators[i]}"`);
-    }
-}, 'copyTo() when target contains path separator');
-
-// TODO(mek): Tests to copy directories.
diff --git a/third_party/blink/web_tests/external/wpt/native-file-system/FileSystemBaseHandle-moveTo.tentative.window.js b/third_party/blink/web_tests/external/wpt/native-file-system/FileSystemBaseHandle-moveTo.tentative.window.js
deleted file mode 100644
index 8728918..0000000
--- a/third_party/blink/web_tests/external/wpt/native-file-system/FileSystemBaseHandle-moveTo.tentative.window.js
+++ /dev/null
@@ -1,131 +0,0 @@
-// META: script=resources/test-helpers.js
-promise_test(async t => cleanupSandboxedFileSystem(),
-    'Cleanup to setup test environment');
-
-promise_test(async t => {
-    const dir = await FileSystemDirectoryHandle.getSystemDirectory({ type: 'sandbox' });
-    const old_handle = await createFileWithContents(t, 'old-file', '12345', dir);
-    const new_handle = await old_handle.moveTo(dir, 'new-name');
-    t.add_cleanup(() => new_handle.remove());
-
-    // Verify new file.
-    assert_true(new_handle.isFile);
-    assert_false(new_handle.isDirectory);
-    assert_equals(new_handle.name, 'new-name');
-    assert_equals(await getFileContents(new_handle), '12345');
-
-    // And verify old file is gone.
-    await promise_rejects(t, 'NotFoundError', getFileContents(old_handle));
-
-    // Verify directory entries.
-    assert_array_equals(await getSortedDirectoryEntries(dir), ['new-name']);
-}, 'moveTo() to rename a file');
-
-promise_test(async t => {
-    const dir = await FileSystemDirectoryHandle.getSystemDirectory({ type: 'sandbox' });
-    const old_handle = await createFileWithContents(t, 'old-file', '12345');
-    const target_dir = await dir.getDirectory('dir-name', { create: true });
-    t.add_cleanup(() => target_dir.removeRecursively());
-
-    const new_handle = await old_handle.moveTo(target_dir);
-
-    // Verify new file.
-    assert_true(new_handle.isFile);
-    assert_false(new_handle.isDirectory);
-    assert_equals(new_handle.name, 'old-file');
-    assert_equals(await getFileContents(new_handle), '12345');
-
-    // And verify old file is gone.
-    await promise_rejects(t, 'NotFoundError', getFileContents(old_handle));
-
-    // Verify directory entries.
-    assert_array_equals(await getSortedDirectoryEntries(dir), ['dir-name/']);
-    assert_array_equals(await getSortedDirectoryEntries(target_dir), ['old-file']);
-}, 'moveTo() to move a file into a sub-directory');
-
-promise_test(async t => {
-    const dir = await FileSystemDirectoryHandle.getSystemDirectory({ type: 'sandbox' });
-    const handle = await createFileWithContents(t, 'old-file', '12345', dir);
-
-    await promise_rejects(t, 'InvalidModificationError', handle.moveTo(dir));
-    await promise_rejects(t, 'InvalidModificationError', handle.moveTo(dir, handle.name));
-
-    // Verify file still exists.
-    assert_equals(await getFileContents(handle), '12345');
-    assert_array_equals(await getSortedDirectoryEntries(dir), ['old-file']);
-}, 'moveTo() with existing name and parent should fail');
-
-promise_test(async t => {
-    const dir = await FileSystemDirectoryHandle.getSystemDirectory({ type: 'sandbox' });
-    const handle = await createFileWithContents(t, 'old-file', '12345', dir);
-    const target_handle = await createFileWithContents(t, 'target', 'abc', dir);
-
-    await handle.moveTo(dir, target_handle.name);
-
-    // Verify state of files.
-    await promise_rejects(t, 'NotFoundError', getFileContents(handle));
-    assert_equals(await getFileContents(target_handle), '12345');
-    assert_array_equals(await getSortedDirectoryEntries(dir), ['target']);
-}, 'moveTo() when target file already exists should overwrite target');
-
-promise_test(async t => {
-    const dir = await FileSystemDirectoryHandle.getSystemDirectory({ type: 'sandbox' });
-
-    const subdir_name = 'subdir-name';
-    const subdir = await createDirectory(t, subdir_name, /*parent=*/dir);
-
-    const file_name = 'file-name';
-    const file = await createEmptyFile(t, file_name, /*parent=*/subdir);
-
-    // An empty name indicates that the filename should remain unchanged.
-    await file.moveTo(dir, /*name=*/"");
-    await dir.getFile(file_name);
-}, `moveTo() when target is empty`);
-
-promise_test(async t => {
-    const dir = await FileSystemDirectoryHandle.getSystemDirectory({ type: 'sandbox' });
-
-    const subdir_name = 'subdir-name';
-    const subdir = await createDirectory(t, subdir_name, /*parent=*/dir);
-
-    const file_name = 'file-name';
-    const file = await createEmptyFile(t, file_name, /*parent=*/subdir);
-
-    await promise_rejects(t, 'SecurityError', file.moveTo(dir, /*name=*/kCurrentDirectory));
-}, `moveTo() when target is ${kCurrentDirectory}`);
-
-promise_test(async t => {
-    const dir = await FileSystemDirectoryHandle.getSystemDirectory({ type: 'sandbox' });
-
-    const first_subdir_name = 'first-subdir-name';
-    const first_subdir = await createDirectory(t, first_subdir_name, /*parent=*/dir);
-
-    const second_subdir_name = 'second-subdir-name';
-    const second_subdir = await createDirectory(t, second_subdir_name, /*parent=*/first_subdir);
-
-    const file_name = 'file-name';
-    const file = await createEmptyFile(t, file_name, /*parent=*/second_subdir);
-
-    await promise_rejects(t, 'SecurityError', file.moveTo(first_subdir, /*name=*/kParentDirectory));
-}, `moveTo() when target is ${kParentDirectory}`);
-
-promise_test(async t => {
-    const dir = await FileSystemDirectoryHandle.getSystemDirectory({ type: 'sandbox' });
-
-    const file_name = 'file-name';
-    const file = await createEmptyFile(t, file_name, /*parent=*/dir);
-
-    const first_subdir_name = 'first-subdir-name';
-    const first_subdir = await createDirectory(t, first_subdir_name, /*parent=*/dir);
-
-    const second_subdir_name = 'second-subdir-name';
-    const second_subdir = await createDirectory(t, second_subdir_name, /*parent=*/first_subdir);
-
-    for (let i = 0; i < kPathSeparators.length; ++i) {
-        const path_with_separator = `${second_subdir_name}${kPathSeparators[i]}${file_name}`;
-        await promise_rejects(t, 'SecurityError', file.moveTo(first_subdir, path_with_separator),
-            `moveTo() must reject names containing "${kPathSeparators[i]}"`);
-    }
-}, 'moveTo() when target contains path separator');
-
-// TODO(mek): Tests to move directories.
diff --git a/third_party/blink/web_tests/external/wpt/payment-request/idlharness.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/payment-request/idlharness.https.window-expected.txt
index ab11665e..62e7edf 100644
--- a/third_party/blink/web_tests/external/wpt/payment-request/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/payment-request/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 109 tests; 105 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 109 tests; 107 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS PaymentRequest interface: existence and properties of interface object
 PASS PaymentRequest interface object length
@@ -67,8 +67,8 @@
 PASS PaymentResponse interface: attribute payerEmail
 PASS PaymentResponse interface: attribute payerPhone
 PASS PaymentResponse interface: operation complete(PaymentComplete)
-FAIL PaymentResponse interface: operation retry(PaymentValidationErrors) assert_own_property: interface prototype object missing non-static operation expected property "retry" missing
-FAIL PaymentResponse interface: attribute onpayerdetailchange assert_true: The prototype object must have a property "onpayerdetailchange" expected true got false
+PASS PaymentResponse interface: operation retry(PaymentValidationErrors)
+PASS PaymentResponse interface: attribute onpayerdetailchange
 PASS MerchantValidationEvent interface: existence and properties of interface object
 PASS MerchantValidationEvent interface object length
 PASS MerchantValidationEvent interface object name
diff --git a/third_party/blink/web_tests/external/wpt/payment-request/payment-response/onpayerdetailchange-attribute.https-expected.txt b/third_party/blink/web_tests/external/wpt/payment-request/payment-response/onpayerdetailchange-attribute.https-expected.txt
deleted file mode 100644
index 8b57681..0000000
--- a/third_party/blink/web_tests/external/wpt/payment-request/payment-response/onpayerdetailchange-attribute.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-PASS PaymentResponse inherits from EventTarget
-FAIL PaymentResponse has an onpayerdetailchange in the prototype chain assert_true: expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/fast/css/move-inline-with-mask-crash.html b/third_party/blink/web_tests/fast/css/move-inline-with-mask-crash.html
new file mode 100644
index 0000000..49c33af
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css/move-inline-with-mask-crash.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<div id="container">
+  x
+  <span style="-webkit-mask:url(#boink);">y</span>
+</div>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+requestAnimationFrame(()=> {
+    requestAnimationFrame(()=> {
+        container.style.width = "0";
+    });
+});
+test(()=> { }, "no crash");
+</script>
diff --git a/third_party/blink/web_tests/fast/dom/timer-throttling-hidden-page-expected.txt b/third_party/blink/web_tests/fast/dom/timer-throttling-hidden-page-expected.txt
index 42422aa..a1be7f2 100644
--- a/third_party/blink/web_tests/fast/dom/timer-throttling-hidden-page-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/timer-throttling-hidden-page-expected.txt
@@ -2,12 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-PASS timerIntervalWhilePageVisible is within 20 of 100
-PASS firstTimerIntervalWhilePageNotVisible is >= 80
-PASS firstTimerIntervalWhilePageNotVisible <= 1120 is true
-PASS timerIntervalWhilePageNotVisible is within 20 of 1000
-PASS timerIntervalWhilePageVisible is within 20 of 100
-PASS timerIntervalWhilePageVisible is within 20 of 100
+PASS timerIntervalWhilePageVisible is within 100 of 100
+PASS firstTimerIntervalWhilePageNotVisible is >= 0
+PASS firstTimerIntervalWhilePageNotVisible <= 1200 is true
+PASS timerIntervalWhilePageNotVisible is within 100 of 1000
+PASS timerIntervalWhilePageVisible is within 100 of 100
+PASS timerIntervalWhilePageVisible is within 100 of 100
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/blink/web_tests/fast/dom/timer-throttling-hidden-page.html b/third_party/blink/web_tests/fast/dom/timer-throttling-hidden-page.html
index 2401d93..d154307 100644
--- a/third_party/blink/web_tests/fast/dom/timer-throttling-hidden-page.html
+++ b/third_party/blink/web_tests/fast/dom/timer-throttling-hidden-page.html
@@ -10,7 +10,7 @@
         var firstTimerWhileNotVisible = true;
         var isPageVisible = true;
         var timeoutInterval = 100;
-        var tolerance = 20;
+        var tolerance = 100;
         var timerAlignmentInterval = 1000;
 
         function testTimer()
diff --git a/third_party/blink/web_tests/fast/dom/timer-throttling-out-of-view-cross-origin-page.html b/third_party/blink/web_tests/fast/dom/timer-throttling-out-of-view-cross-origin-page.html
index 2fafd8b..affb47c 100644
--- a/third_party/blink/web_tests/fast/dom/timer-throttling-out-of-view-cross-origin-page.html
+++ b/third_party/blink/web_tests/fast/dom/timer-throttling-out-of-view-cross-origin-page.html
@@ -15,12 +15,14 @@
   testRunner.dumpAsText();
 let throttlingTest = async_test("Test timer throttling in out-of-view cross origin frames");
 
-addEventListener('load', () => {
+var frame = document.querySelector('#frame');
+frame.addEventListener('load', () => {
   // Throttling only happens after the first layout, so start the test after
   // that.
   requestAnimationFrame(() => {
-    var frame = document.querySelector('#frame');
-    frame.contentWindow.postMessage(null, '*');
+    setTimeout(() => {
+      frame.contentWindow.postMessage(null, '*');
+    });
   });
 });
 
diff --git a/third_party/blink/web_tests/fast/events/touch/compositor-touch-hit-rects-scroll-expected.txt b/third_party/blink/web_tests/fast/events/touch/compositor-touch-hit-rects-scroll-expected.txt
index cf0faec..fba4b10 100644
--- a/third_party/blink/web_tests/fast/events/touch/compositor-touch-hit-rects-scroll-expected.txt
+++ b/third_party/blink/web_tests/fast/events/touch/compositor-touch-hit-rects-scroll-expected.txt
@@ -14,7 +14,7 @@
 overflowwithborder: layer(13,365 290x70) has hit test rect (0,0 290x70)
 overflowwithborder: layer(0,0 263x124) has hit test rect (4,4 255x116)
 
-withTransform: layer(15,455 271x12) has hit test rect (0,0 271x12)
+withTransform: layer(15,435 271x12) has hit test rect (0,0 271x12)
 withTransform: layer(0,0 282x77) has hit test rect (0,13 273x14)
 
 
diff --git a/third_party/blink/web_tests/fast/scrolling/no-hover-during-smooth-keyboard-scroll.html b/third_party/blink/web_tests/fast/scrolling/no-hover-during-smooth-keyboard-scroll.html
new file mode 100644
index 0000000..d5ed3ca
--- /dev/null
+++ b/third_party/blink/web_tests/fast/scrolling/no-hover-during-smooth-keyboard-scroll.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<script src='../../resources/testharness.js'></script>
+<script src='../../resources/testharnessreport.js'></script>
+<script src='../../resources/gesture-util.js'></script>
+
+<style>
+  body, html {
+    margin: 0;
+    height: 500vh;
+  }
+  div {
+    height: 50px;
+    width: 100%;
+  }
+
+  .hoverme {
+    background-color: rgb(0, 0, 255);
+  }
+
+  .hoverme:hover {
+    background-color: rgb(255, 255, 0);
+  }
+
+  .message {
+    width: 100%;
+    text-align: left;
+  }
+</style>
+
+<div class="message">
+  First move your mouse cursor to the page, you will see the color under the mouse cursor changed from blue to yellow. <br>
+  Do a keyboard scroll, you will see the hover update on a text under the mouse cursor when the scrolling finishes.
+</div>
+<div class="hoverme">hover over me</div>
+<div class="hoverme">hover over me</div>
+<div class="hoverme">hover over me</div>
+<div class="hoverme">hover over me</div>
+<div class="hoverme">hover over me</div>
+
+<script>
+  const elementHeight = 50;
+
+  window.onload = async () => {
+
+    promise_test(async () => {
+      if (window.internals) {
+        internals.runtimeFlags.updateHoverFromScrollAtBeginFrameEnabled = true;
+      }
+
+      await waitForCompositorCommit();
+      array = document.getElementsByClassName('hoverme');
+
+      let x = array[0].offsetLeft + 10;
+      let y = array[0].offsetTop + 25;
+      // Move cursor to 1st element.
+      await mouseMoveTo(x, y);
+      await waitFor( () => { return array[0].matches(":hover");}, 'wait for move to 1st element');
+      assert_true(array[0].matches(":hover"));
+      assert_false(array[1].matches(":hover"));
+      assert_equals(document.scrollingElement.scrollTop, 0);
+
+      // Keyboard scroll end up at 4th element. Hover state does not update during scrolling,
+      // only the 4th element has a hover effect.
+      eventSender.keyDown('ArrowDown');
+      eventSender.keyDown('ArrowDown');
+      eventSender.keyDown('ArrowDown');
+      eventSender.keyDown('ArrowDown');
+      // Wait enough time to see if we fire a fake mouse move event to update the hover state.
+      await waitForAnimationEnd(() => { return document.scrollingElement.scrollTop; }, 300, 15);
+      assert_approx_equals(document.scrollingElement.scrollTop, 3 * elementHeight, 25);
+      assert_false(array[0].matches(":hover"));
+      assert_false(array[1].matches(":hover"));
+      assert_false(array[2].matches(":hover"));
+      assert_true(array[3].matches(":hover"));
+      assert_false(array[4].matches(":hover"));
+    }, 'Keyboard smooth scroll on the page, no hover update during scrolling, but updating hover at the end of scroll.');
+  }
+
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/direct-image-compositing-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/direct-image-compositing-expected.png
deleted file mode 100644
index bb3c623..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/direct-image-compositing-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/geometry/layer-due-to-layer-children-deep-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/geometry/layer-due-to-layer-children-deep-expected.png
index 53b62c7..0d320820 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/geometry/layer-due-to-layer-children-deep-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/geometry/layer-due-to-layer-children-deep-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/geometry/layer-due-to-layer-children-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/geometry/layer-due-to-layer-children-expected.png
index 0cc88ee..75438dca 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/geometry/layer-due-to-layer-children-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/geometry/layer-due-to-layer-children-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/gestures/gesture-tapHighlight-pixel-rotated-link-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/gestures/gesture-tapHighlight-pixel-rotated-link-expected.png
index 802b6318..c6b5938 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/gestures/gesture-tapHighlight-pixel-rotated-link-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/gestures/gesture-tapHighlight-pixel-rotated-link-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/overlap-blending/reflection-opacity-huge-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/overlap-blending/reflection-opacity-huge-expected.png
index 067d40e..00310eb 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/overlap-blending/reflection-opacity-huge-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/overlap-blending/reflection-opacity-huge-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/reflections/reflection-opacity-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/reflections/reflection-opacity-expected.png
index 2b31cd05..e8eb3c1 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/reflections/reflection-opacity-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/reflections/reflection-opacity-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/css3/filters/blur-filter-page-scroll-self-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/css3/filters/blur-filter-page-scroll-self-expected.png
new file mode 100644
index 0000000..dffda23
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/css3/filters/blur-filter-page-scroll-self-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/css3/flexbox/flexbox-baseline-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/css3/flexbox/flexbox-baseline-expected.png
index e18184b..416f40f 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/css3/flexbox/flexbox-baseline-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/css3/flexbox/flexbox-baseline-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-canvas-border-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-canvas-border-expected.png
index c337eb8..37e80f81 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-canvas-border-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-canvas-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-canvas-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-canvas-expected.png
index 6b041059..c8a0095 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-canvas-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-canvas-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-video-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-video-expected.png
deleted file mode 100644
index 286f1e2f..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-video-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-video-ratio-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-video-ratio-expected.png
deleted file mode 100644
index b07697d1..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-video-ratio-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-video-shadow-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-video-shadow-expected.png
deleted file mode 100644
index 8cd050d6..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/border-radius-mask-video-shadow-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/inline-mask-overlay-image-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/inline-mask-overlay-image-expected.png
index 61f4ab6..fdf4417 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/inline-mask-overlay-image-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/inline-mask-overlay-image-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/inline-mask-overlay-image-outset-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/inline-mask-overlay-image-outset-expected.png
index 611bded..cea4a04 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/inline-mask-overlay-image-outset-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/inline-mask-overlay-image-outset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/inline-mask-overlay-image-outset-vertical-rl-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/inline-mask-overlay-image-outset-vertical-rl-expected.png
index c8358206..880ff15 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/inline-mask-overlay-image-outset-vertical-rl-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/borders/inline-mask-overlay-image-outset-vertical-rl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/forms/select/select-item-background-clip-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/forms/select/select-item-background-clip-expected.png
index 449a0e3..24d3fb1 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/forms/select/select-item-background-clip-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/forms/select/select-item-background-clip-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png
index cae844d..15f7531 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/forms/textarea/basic-textareas-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/forms/textarea/basic-textareas-expected.png
index c3c2066d..51c056f6 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/forms/textarea/basic-textareas-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/forms/textarea/basic-textareas-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/forms/textarea/basic-textareas-quirks-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/forms/textarea/basic-textareas-quirks-expected.png
index ae956b6..cb9f036 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/forms/textarea/basic-textareas-quirks-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/forms/textarea/basic-textareas-quirks-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/layers/scroll-with-transform-layer-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/layers/scroll-with-transform-layer-expected.png
index 500bd6e..307ac41f 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/layers/scroll-with-transform-layer-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/layers/scroll-with-transform-layer-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/multicol/composited-opacity-2nd-and-3rd-column-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/multicol/composited-opacity-2nd-and-3rd-column-expected.png
index 23ad64c2..5d12857a 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/multicol/composited-opacity-2nd-and-3rd-column-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/multicol/composited-opacity-2nd-and-3rd-column-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/multicol/composited-relpos-overlapping-will-change-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/multicol/composited-relpos-overlapping-will-change-expected.png
index f582966..51ca193f 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/multicol/composited-relpos-overlapping-will-change-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/multicol/composited-relpos-overlapping-will-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/multicol/vertical-lr/composited-relpos-overlapping-will-change-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/multicol/vertical-lr/composited-relpos-overlapping-will-change-expected.png
index 9eafea09..b0e5e90 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/multicol/vertical-lr/composited-relpos-overlapping-will-change-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/multicol/vertical-lr/composited-relpos-overlapping-will-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change-expected.png
index 9bad00d2..35b9d86 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/image-selection-highlight-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/image-selection-highlight-expected.png
index c83697bc..e17c2fc3 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/image-selection-highlight-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/image-selection-highlight-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-float-stacking-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-float-stacking-expected.png
index 5850317..29b0b82 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-float-stacking-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-float-stacking-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-rtl-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-rtl-expected.png
index 8337e32..677ffb9 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-rtl-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-rtl-vertical-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-rtl-vertical-expected.png
index c7882058..d4076496 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-rtl-vertical-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-rtl-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-stacking-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-stacking-expected.png
index 58d3942..b0879df8 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-stacking-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-stacking-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-text-hit-testing-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-text-hit-testing-expected.png
index d7bb8b4d..676249b 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-text-hit-testing-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/overflow-text-hit-testing-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/table-overflow-float-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/table-overflow-float-expected.png
index b2d7bfd5..82df536 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/table-overflow-float-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/overflow/table-overflow-float-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/table/frame-and-rules-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/table/frame-and-rules-expected.png
deleted file mode 100644
index a6fd87e..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/table/frame-and-rules-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/text/font-format-support-color-cff2-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/text/font-format-support-color-cff2-expected.png
index f78d79c..0a198f0 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/text/font-format-support-color-cff2-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/text/font-format-support-color-cff2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/text/selection/mixed-directionality-selection-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/text/selection/mixed-directionality-selection-expected.png
index b00d680..58ea6dc 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/text/selection/mixed-directionality-selection-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/text/selection/mixed-directionality-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/images/color-profile-mask-image-svg-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/images/color-profile-mask-image-svg-expected.png
index 956e482..bc492ce 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/images/color-profile-mask-image-svg-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/images/color-profile-mask-image-svg-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/scrollbars/listbox-scrollbar-combinations-expected.png
index 8469f0f..ac0cc170d 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/scrollbars/listbox-scrollbar-combinations-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/scrollbars/listbox-scrollbar-combinations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/W3C-SVG-1.1/masking-mask-01-b-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/W3C-SVG-1.1/masking-mask-01-b-expected.png
index b9ad34d..5afaa13 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/W3C-SVG-1.1/masking-mask-01-b-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/W3C-SVG-1.1/masking-mask-01-b-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/W3C-SVG-1.1/masking-path-04-b-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/W3C-SVG-1.1/masking-path-04-b-expected.png
index 15a42cf..9c988a8 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/W3C-SVG-1.1/masking-path-04-b-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/W3C-SVG-1.1/masking-path-04-b-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/as-background-image/background-image-preserveaspectRatio-support-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/as-background-image/background-image-preserveaspectRatio-support-expected.png
index d9c715c..4570a36c 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/as-background-image/background-image-preserveaspectRatio-support-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/as-background-image/background-image-preserveaspectRatio-support-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/batik/text/textEffect2-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/batik/text/textEffect2-expected.png
index 8e76814..82f8ff7 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/batik/text/textEffect2-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/batik/text/textEffect2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/batik/text/textProperties-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/batik/text/textProperties-expected.png
index bf80b965..263e978e 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/batik/text/textProperties-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/batik/text/textProperties-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/clip-path/clip-path-with-text-clipped-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/clip-path/clip-path-with-text-clipped-expected.png
index 9d1a71c..510898892 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/clip-path/clip-path-with-text-clipped-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/clip-path/clip-path-with-text-clipped-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/zoom/page/zoom-mask-with-percentages-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/zoom/page/zoom-mask-with-percentages-expected.png
index 8e2fd36d..a15fcd90 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/zoom/page/zoom-mask-with-percentages-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/svg/zoom/page/zoom-mask-with-percentages-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/tables/mozilla/bugs/bug131020-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/tables/mozilla/bugs/bug131020-expected.png
index 4439367..521f303 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/tables/mozilla/bugs/bug131020-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/tables/mozilla/bugs/bug131020-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/tables/mozilla_expected_failures/marvin/backgr_fixed-bg-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/tables/mozilla_expected_failures/marvin/backgr_fixed-bg-expected.png
index c0e325c..62a22d4e 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/tables/mozilla_expected_failures/marvin/backgr_fixed-bg-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/tables/mozilla_expected_failures/marvin/backgr_fixed-bg-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png
index 8469f0f..ac0cc170d 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-self-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-self-expected.png
new file mode 100644
index 0000000..74b0f7a
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-self-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
new file mode 100644
index 0000000..443b9e30
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,73 @@
+  
+  
+  
+  
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutImage IMG id='grayscale-box'",
+      "bounds": [320, 180],
+      "transform": 1
+    },
+    {
+      "name": "LayoutImage IMG id='saturate-box'",
+      "bounds": [320, 180],
+      "transform": 2
+    },
+    {
+      "name": "LayoutImage IMG id='invert-box'",
+      "bounds": [320, 180],
+      "transform": 3
+    },
+    {
+      "name": "LayoutImage IMG id='brightness-box'",
+      "bounds": [320, 180],
+      "transform": 4
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [36, 36, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [36, 264, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [36, 492, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [36, 720, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
new file mode 100644
index 0000000..5f3bc1c
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer' class='final-drop-shadow'",
+      "position": [-150, -150],
+      "bounds": [520, 520],
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [200, 200, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
new file mode 100644
index 0000000..2efc314
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited'",
+      "position": [-150, -150],
+      "bounds": [500, 500],
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [200, 200, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
new file mode 100644
index 0000000..ba07a38
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,76 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [200, 200, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [1712, 1170],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "HorizontalScrollbar",
+      "position": [0, 1170],
+      "bounds": [1600, 30]
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [200, 200, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
new file mode 100644
index 0000000..570ff175
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,63 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [200, 200],
+      "bounds": [400, 400],
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
new file mode 100644
index 0000000..570ff175
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,63 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [200, 200],
+      "bounds": [400, 400],
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-reflected-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-reflected-expected.png
new file mode 100644
index 0000000..4fbab80e
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/composited-reflected-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
new file mode 100644
index 0000000..14103d63
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
new file mode 100644
index 0000000..5b7c2b94
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png
new file mode 100644
index 0000000..05274de
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
new file mode 100644
index 0000000..487badf5
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png
new file mode 100644
index 0000000..3ee4f62
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png
new file mode 100644
index 0000000..a3a7015
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png
new file mode 100644
index 0000000..6598a85
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
new file mode 100644
index 0000000..2efc3e4
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png
new file mode 100644
index 0000000..bdc645b4
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-hidpi-hw-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-hidpi-hw-expected.png
new file mode 100644
index 0000000..37d7407d
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-hidpi-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png
new file mode 100644
index 0000000..58dedbfe
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-subregion-chained-hw-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-subregion-chained-hw-expected.png
new file mode 100644
index 0000000..48d39c2
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-subregion-chained-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
new file mode 100644
index 0000000..a86197db
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png
new file mode 100644
index 0000000..e8c50b4
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png
new file mode 100644
index 0000000..27a4ba9a
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filter-change-repaint-composited-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filter-change-repaint-composited-expected.png
new file mode 100644
index 0000000..9c7d6c9
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filter-change-repaint-composited-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filter-change-repaint-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filter-change-repaint-expected.png
new file mode 100644
index 0000000..22560a8
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filter-change-repaint-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png
new file mode 100644
index 0000000..6dab0112
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-crash-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-crash-expected.png
new file mode 100644
index 0000000..f7af01d
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-crash-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-expected.png
new file mode 100644
index 0000000..f7af01d
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
new file mode 100644
index 0000000..4cec350
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
@@ -0,0 +1,44 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='filtered box'",
+      "position": [16, 16],
+      "bounds": [288, 288]
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='compositing box'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [60, 60, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, 0, 2, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
new file mode 100644
index 0000000..dac6c08
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
@@ -0,0 +1,11 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/writing-mode/japanese-lr-selection-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/editing/selection/japanese-lr-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/writing-mode/japanese-lr-selection-expected.png
rename to third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/editing/selection/japanese-lr-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/writing-mode/japanese-rl-selection-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/editing/selection/japanese-rl-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/writing-mode/japanese-rl-selection-expected.png
rename to third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/editing/selection/japanese-rl-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/writing-mode/vertical-lr-replaced-selection-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/editing/selection/vertical-lr-replaced-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/writing-mode/vertical-lr-replaced-selection-expected.png
rename to third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/editing/selection/vertical-lr-replaced-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/writing-mode/vertical-rl-replaced-selection-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/editing/selection/vertical-rl-replaced-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/writing-mode/vertical-rl-replaced-selection-expected.png
rename to third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/editing/selection/vertical-rl-replaced-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/network/network-condition-a11y-test-expected.txt b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/network/network-condition-a11y-test-expected.txt
new file mode 100644
index 0000000..ad4b8a2
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/network/network-condition-a11y-test-expected.txt
@@ -0,0 +1,4 @@
+Tests accessibility in Network conditions view using the axe-core linter.
+aXe violations: []
+
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/network/network-condition-a11y-test.js b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/network/network-condition-a11y-test.js
new file mode 100644
index 0000000..6e6699b8
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/network/network-condition-a11y-test.js
@@ -0,0 +1,15 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  TestRunner.addResult('Tests accessibility in Network conditions view using the axe-core linter.');
+
+  await TestRunner.loadModule('axe_core_test_runner');
+  const view = 'network.config';
+  await UI.viewManager.showView(view);
+  const widget = await UI.viewManager.view(view).widget();
+  await AxeCoreTestRunner.runValidation(widget.element);
+
+  TestRunner.completeTest();
+})();
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/shadow/shadow-distribution.js b/third_party/blink/web_tests/http/tests/devtools/elements/shadow/shadow-distribution.js
index 4c76a8b..cf05fbe 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/shadow/shadow-distribution.js
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/shadow/shadow-distribution.js
@@ -65,6 +65,14 @@
           var parent = resolveElement(parentId);
           parent.appendChild(element);
       }
+
+      // In order for the elements tree to reflect the changes made during this test,
+      // there needs to be a step that ensures that Shadow DOM distributions are updated.
+      // Forcing a style recalc ensures that Shadow DOM distributions are updated, and that
+      // the relevant DevTools CDP events are sent to the front end.
+      function updateDistributionIfNeeded() {
+        getComputedStyle(document.documentElement).left;
+      }
   `);
 
   TestRunner.runTestSuite([
@@ -134,7 +142,11 @@
   ]);
 
   function evalAndDump(code, nodeId, next) {
-    TestRunner.evaluateInPage(code, ElementsTestRunner.expandElementsTree.bind(ElementsTestRunner, dump));
+    TestRunner.evaluateInPage(code, ElementsTestRunner.expandElementsTree.bind(ElementsTestRunner, callback));
+
+    function callback() {
+      TestRunner.evaluateInPage('updateDistributionIfNeeded()', ElementsTestRunner.expandElementsTree.bind(ElementsTestRunner, dump));
+    }
 
     function dump() {
       ElementsTestRunner.dumpElementsTree(ElementsTestRunner.expandedNodeWithId(nodeId));
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/styles-grid-template-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/styles-grid-template-expected.txt
new file mode 100644
index 0000000..c8f2b88
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/styles-grid-template-expected.txt
@@ -0,0 +1,56 @@
+Tests that properties defining grid templates are correct.
+
+[expanded] 
+element.style { ()
+    grid-template-areas:
+        'a a'
+        'b b';
+    grid-template: 'a a';
+
+[expanded] 
+#container { (styles-grid…mplate.js:7 -> styles-grid-template.js:7:21)
+    grid:
+        [first-row-start] "a a" 10px [first-row-end]
+        [second-row-start] "b b" 20px / 100px;
+
+[expanded] 
+#container { (styles-grid…mplate.js:6 -> styles-grid-template.js:6:21)
+/-- overloaded --/     grid:
+        "a a" 10px
+        'b b' 20px / 100px;
+
+[expanded] 
+#container { (styles-grid…mplate.js:5 -> styles-grid-template.js:5:21)
+/-- overloaded --/     grid:
+        "a a"
+        'b b';
+
+[expanded] 
+#container { (styles-grid…mplate.js:4 -> styles-grid-template.js:4:21)
+/-- overloaded --/     grid:
+        "a a"
+        "b b";
+
+[expanded] 
+#container { (styles-grid…mplate.js:3 -> styles-grid-template.js:3:21)
+/-- overloaded --/     grid:
+        " a a "
+        " b b";
+
+[expanded] 
+#container { (styles-grid…mplate.js:2 -> styles-grid-template.js:2:21)
+/-- overloaded --/     grid: "a a";
+
+[expanded] 
+div { (user agent stylesheet)
+    display: block;
+
+======== Inherited from html ========
+[expanded] 
+html { (user agent stylesheet)
+    color: -internal-root-color;
+
+Start editing "grid-template-areas"
+Prompt text 'a a'
+'b b'
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/styles-grid-template.js b/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/styles-grid-template.js
new file mode 100644
index 0000000..4189b15
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/styles-grid-template.js
@@ -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.
+
+(async function() {
+  TestRunner.addResult(`Tests that properties defining grid templates are correct.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <style>
+        #container { grid: "a a"; }
+        #container { grid:   " a a "    " b b" ; }
+        #container { grid: "a a" "b b"; }
+        #container { grid: "a a" 'b b'; }
+        #container { grid: "a a" 10px 'b b' 20px / 100px; }
+        #container { grid: [first-row-start] "a a" 10px [first-row-end] [second-row-start] "b b" 20px / 100px; }
+
+      /*# sourceURL=styles-grid-template.js*/
+      </style>
+      <div id="container" style="grid-template-areas: 'a a' 'b b'; grid-template: 'a a'"></div>
+    `);
+
+  TestRunner.evaluateInPage('loadIframe()');
+  ElementsTestRunner.selectNodeAndWaitForStyles('container', step1);
+
+  function step1() {
+    ElementsTestRunner.dumpSelectedElementStyles(true /** excludeComputed */,
+                                                 false,
+                                                 true /** omitLonghands */,
+                                                 false,
+                                                 true /** printInnerText */);
+    step2();
+  }
+
+  function step2() {
+    const treeElement = ElementsTestRunner.getElementStylePropertyTreeItem('grid-template-areas');
+    TestRunner.addResult('Start editing "grid-template-areas"');
+    treeElement.startEditing(treeElement.valueElement);
+
+    TestRunner.addResult(`Prompt text ${treeElement._prompt.text()}`);
+
+    TestRunner.completeTest();
+  }
+})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/styles/styles-format-style-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/styles/styles-format-style-expected.txt
index 14930d4..1427b2f 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/styles/styles-format-style-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/styles/styles-format-style-expected.txt
@@ -39,3 +39,15 @@
  color: blue;
 
 
+Raw CSS: grid: "a a" 10px "b b" 20px / 100px
+New CSS: 
+ grid:
+  "a a" 10px
+  "b b" 20px / 100px;
+
+
+Raw CSS: grid: [first-row-start] "a a" 10px [first-row-end] [second-row-start] "b b" 20px / 100px
+New CSS: 
+ grid: [first-row-start] "a a" 10px [first-row-end] [second-row-start] "b b" 20px / 100px;
+
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/styles/styles-format-style.js b/third_party/blink/web_tests/http/tests/devtools/elements/styles/styles-format-style.js
index 8f0cf86..fada11c2 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/styles/styles-format-style.js
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/styles/styles-format-style.js
@@ -13,7 +13,9 @@
   testText('color: var(-);margin: 0;padding:0');
   testText('color: red;/* a comment */;color: blue');
   testText(`:; color: red; color: blue`);
-  testText('color: red;/* a comment;;; */ :; color: blue;')
+  testText('color: red;/* a comment;;; */ :; color: blue;');
+  testText('grid: "a a" 10px "b b" 20px / 100px');
+  testText('grid: [first-row-start] "a a" 10px [first-row-end] [second-row-start] "b b" 20px / 100px');
   TestRunner.completeTest();
 
   function testText(cssText) {
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/page/createIsolatedWorld-reload-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/page/createIsolatedWorld-reload-expected.txt
new file mode 100644
index 0000000..25ed0a2
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/page/createIsolatedWorld-reload-expected.txt
@@ -0,0 +1,5 @@
+Verifies that isolated worlds can be created after same-process navigation
+Isolated world evaluation result: 56
+Reload.
+Isolated world evaluation result: 56
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/page/createIsolatedWorld-reload.js b/third_party/blink/web_tests/http/tests/inspector-protocol/page/createIsolatedWorld-reload.js
new file mode 100644
index 0000000..64603a2
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/page/createIsolatedWorld-reload.js
@@ -0,0 +1,30 @@
+(async function(testRunner) { const {page, session, dp} = await testRunner.startBlank(
+      "Verifies that isolated worlds can be created after same-process navigation");
+
+  await dp.Runtime.enable();
+  await dp.Page.enable();
+
+  const getResourceTreeResponse = await dp.Page.getResourceTree();
+  const mainFrameId = getResourceTreeResponse.result.frameTree.frame.id;
+
+  await evaluateInIsolatedWorld(mainFrameId);
+  testRunner.log('Reload.');
+  await Promise.all([
+    dp.Page.reload(),
+    dp.Page.onceLoadEventFired(),
+  ]);
+  await evaluateInIsolatedWorld(mainFrameId);
+
+  testRunner.completeTest();
+
+  async function evaluateInIsolatedWorld(frameId) {
+    const isolatedWorldResponse = await dp.Page.createIsolatedWorld(
+        {frameId, worldName: 'Test world', grantUniveralAccess: false});
+    const contextId = isolatedWorldResponse.result.executionContextId;
+    const response = await dp.Runtime.evaluate({
+      contextId: contextId,
+      expression: `7 * 8`,
+    });
+    testRunner.log(`Isolated world evaluation result: ${response.result.result.value}`);
+  }
+})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/webaudio/webaudio-basic-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/webaudio/webaudio-basic-expected.txt
index 708880a..4cf13c747 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/webaudio/webaudio-basic-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/webaudio/webaudio-basic-expected.txt
@@ -22,6 +22,8 @@
 [
     [0] : currentTime
     [1] : renderCapacity
+    [2] : callbackIntervalMean
+    [3] : callbackIntervalVariance
 ]
 Disabled successfully: true
 
diff --git a/third_party/blink/web_tests/inspector-protocol/dom-snapshot/dom-snapshot-captureSnapshot-includeDOMRects-expected.txt b/third_party/blink/web_tests/inspector-protocol/dom-snapshot/dom-snapshot-captureSnapshot-includeDOMRects-expected.txt
new file mode 100644
index 0000000..79a4a51
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/dom-snapshot/dom-snapshot-captureSnapshot-includeDOMRects-expected.txt
@@ -0,0 +1,124 @@
+Tests DOMSnapshot.getSnapshot reports offset, scroll, and client rects of each node.
+{
+  "documents": [
+    {
+      "documentURL": 0,
+      "baseURL": 0,
+      "contentLanguage": -1,
+      "encodingName": 1,
+      "publicId": -1,
+      "systemId": -1,
+      "frameId": 2,
+      "nodes": "<object>",
+      "layout": {
+        "nodeIndex": "<object>",
+        "styles": "<object>",
+        "bounds": [
+          [
+            0,
+            0,
+            800,
+            600
+          ],
+          [
+            0,
+            0,
+            800,
+            0
+          ],
+          [
+            0,
+            0,
+            800,
+            0
+          ],
+          [
+            100,
+            100,
+            100,
+            100
+          ],
+          [
+            100,
+            100,
+            96,
+            16
+          ]
+        ],
+        "text": "<object>",
+        "stackingContexts": "<object>",
+        "offsetRects": [
+          [],
+          [
+            0,
+            0,
+            800,
+            0
+          ],
+          [
+            0,
+            0,
+            800,
+            0
+          ],
+          [
+            100,
+            100,
+            100,
+            100
+          ],
+          []
+        ],
+        "scrollRects": [
+          [],
+          [
+            0,
+            0,
+            800,
+            600
+          ],
+          [
+            0,
+            0,
+            800,
+            0
+          ],
+          [
+            0,
+            0,
+            100,
+            100
+          ],
+          []
+        ],
+        "clientRects": [
+          [],
+          [
+            0,
+            0,
+            800,
+            600
+          ],
+          [
+            0,
+            0,
+            800,
+            0
+          ],
+          [
+            0,
+            0,
+            100,
+            100
+          ],
+          []
+        ]
+      },
+      "textBoxes": "<object>",
+      "scrollOffsetX": 0,
+      "scrollOffsetY": 0
+    }
+  ],
+  "strings": "<object>"
+}
+
diff --git a/third_party/blink/web_tests/inspector-protocol/dom-snapshot/dom-snapshot-captureSnapshot-includeDOMRects.js b/third_party/blink/web_tests/inspector-protocol/dom-snapshot/dom-snapshot-captureSnapshot-includeDOMRects.js
new file mode 100644
index 0000000..f0ff40a
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/dom-snapshot/dom-snapshot-captureSnapshot-includeDOMRects.js
@@ -0,0 +1,17 @@
+(async function(testRunner) {
+  const {dp} = await testRunner.startURL('../resources/dom-snapshot-includeDOMRects.html', 'Tests DOMSnapshot.getSnapshot reports offset, scroll, and client rects of each node.');
+
+  function stabilize(key, value) {
+    const unstableKeys = new Set(['strings', 'textBoxes', 'nodes', 'nodeIndex', 'styles', 'text', 'stackingContexts']);
+    if (unstableKeys.has(key))
+      return '<' + typeof(value) + '>';
+    return value;
+  }
+
+  const response = await dp.DOMSnapshot.captureSnapshot({'computedStyles': [], 'includeDOMRects': true});
+  if (response.error)
+    testRunner.log(response);
+  else
+    testRunner.log(JSON.stringify(response.result, stabilize, 2));
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/inspector-protocol/resources/dom-snapshot-includeDOMRects.html b/third_party/blink/web_tests/inspector-protocol/resources/dom-snapshot-includeDOMRects.html
new file mode 100644
index 0000000..e36c4e3f
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/resources/dom-snapshot-includeDOMRects.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- domSnapshot test for a document to include offsetRects, clientRects, and scrollRects.  -->
+<style>
+@font-face {
+  font-family: 'ahem';
+  src: url(../../resources/Ahem.ttf);
+}
+</style>
+<style>
+* {
+  margin: 0;
+  padding: 0;
+}
+div {
+  position: absolute;
+  top: 100px;
+  left: 100px;
+  width: 100px;
+  height: 100px;
+}
+</style>
+<!-- The div's box should have size and position of 100x100 to match the
+     computed style, even though it's visually scaled by the viewport. The
+     text's box should also be inside the div's box, not outside it. -->
+<div style='font-family: ahem;' id='100x100'>Boxes!</div>
+
diff --git a/third_party/blink/web_tests/nfc/resources/nfc-helpers.js b/third_party/blink/web_tests/nfc/resources/nfc-helpers.js
index c1f70d3..51a13a1 100644
--- a/third_party/blink/web_tests/nfc/resources/nfc-helpers.js
+++ b/third_party/blink/web_tests/nfc/resources/nfc-helpers.js
@@ -47,8 +47,8 @@
   return { target, timeout, ignoreRead };
 }
 
-function createNFCWatchOptions(url, recordType, mediaType, mode) {
-  return { url, recordType, mediaType, mode};
+function createNFCReaderOptions(url, recordType, mediaType, compatibility) {
+  return { url, recordType, mediaType, compatibility };
 }
 
 function createTextRecord(text) {
@@ -95,10 +95,12 @@
   return device.mojom.NFCPushTarget.ANY;
 }
 
-function toMojoNFCWatchMode(mode) {
-  if (mode === 'web-nfc-only')
-    return device.mojom.NFCWatchMode.WEBNFC_ONLY;
-  return device.mojom.NFCWatchMode.ANY;
+function toMojoNDEFCompatibility(compatibility) {
+  if (compatibility === 'nfc-forum')
+    return device.mojom.NDEFCompatibility.NFC_FORUM;
+  if (compatibility === 'vendor')
+    return device.mojom.NDEFCompatibility.VENDOR;
+  return device.mojom.NDEFCompatibility.ANY;
 }
 
 // Converts between NDEFMessage https://w3c.github.io/web-nfc/#dom-ndefmessage
@@ -220,9 +222,9 @@
     assert_equals(received.target, device.mojom.NFCPushTarget.ANY);
 }
 
-// Compares NFCWatchOptions structures that were provided to API and
+// Compares NFCReaderOptions structures that were provided to API and
 // received by the mock mojo service.
-function assertNFCWatchOptionsEqual(provided, received) {
+function assertNFCReaderOptionsEqual(provided, received) {
   if (provided.url !== undefined)
     assert_equals(provided.url, received.url);
   else
@@ -233,10 +235,10 @@
   else
     assert_equals(received.mediaType, '');
 
-  if (provided.mode !== undefined)
-    assert_equals(toMojoNFCWatchMode(provided.mode), received.mode);
+  if (provided.compatibility !== undefined)
+    assert_equals(toMojoNDEFCompatibility(provided.compatibility), received.compatibility);
   else
-    assert_equals(received.mode, device.mojom.NFCWatchMode.WEBNFC_ONLY);
+    assert_equals(received.compatibility, device.mojom.NDEFCompatibility.NFC_FORUM);
 
   if (provided.recordType !== undefined) {
     assert_equals(!+received.record_filter, true);
diff --git a/third_party/blink/web_tests/nfc/watch.html b/third_party/blink/web_tests/nfc/watch.html
index 3932350..c54db47 100644
--- a/third_party/blink/web_tests/nfc/watch.html
+++ b/third_party/blink/web_tests/nfc/watch.html
@@ -24,15 +24,15 @@
 
 nfc_test(async () => {
   await navigator.nfc.watch(noop);
-  assertNFCWatchOptionsEqual(createNFCWatchOptions(), mockNFC.watchOptions());
-}, 'Test that default NFCWatchOptions values are set correctly.')
+  assertNFCReaderOptionsEqual(createNFCReaderOptions(), mockNFC.watchOptions());
+}, 'Test that default NFCReaderOptions values are set correctly.')
 
 nfc_test(async () => {
-  let watchOptions = createNFCWatchOptions(test_message_origin, 'json',
+  let watchOptions = createNFCReaderOptions(test_message_origin, 'json',
       'application/json', 'any');
   await navigator.nfc.watch(noop, watchOptions);
-  assertNFCWatchOptionsEqual(watchOptions, mockNFC.watchOptions());
-}, 'Test that NFCWatchOptions values are correctly converted.')
+  assertNFCReaderOptionsEqual(watchOptions, mockNFC.watchOptions());
+}, 'Test that NFCReaderOptions values are correctly converted.')
 
 nfc_test(() => {
   return assertRejectsWithError(navigator.nfc.cancelWatch(1), 'NotFoundError');
@@ -75,35 +75,35 @@
 nfc_test(() => {
   return assertRejectsWithError(navigator.nfc.watch(noop, {url:"www.a.com"}),
       'SyntaxError');
-}, 'Test that nfc.watch fails if NFCWatchOptions.url is missing components.');
+}, 'Test that nfc.watch fails if NFCReaderOptions.url is missing components.');
 
 nfc_test(() => {
   return assertRejectsWithError(navigator.nfc.watch(noop, {url:"invalid"}),
       'SyntaxError');
-}, 'Test that nfc.watch fails if NFCWatchOptions.url is invalid.');
+}, 'Test that nfc.watch fails if NFCReaderOptions.url is invalid.');
 
 nfc_test(() => {
   return assertRejectsWithError(navigator.nfc.watch(noop, {url:"http://a.com"}),
       'SyntaxError');
-}, 'Test that nfc.watch fails if NFCWatchOptions.url has wrong protocol.');
+}, 'Test that nfc.watch fails if NFCReaderOptions.url has wrong protocol.');
 
 nfc_test(() => {
   return navigator.nfc.watch(noop, {url:"https://a.com"});
-}, 'Test that nfc.watch succeeds if NFCWatchOptions.url is valid URL.');
+}, 'Test that nfc.watch succeeds if NFCReaderOptions.url is valid URL.');
 
 nfc_test(() => {
   return navigator.nfc.watch(noop, {url:"https://a.com/*"});
-}, 'Test that nfc.watch succeeds if NFCWatchOptions.url is valid URL with "*"' +
+}, 'Test that nfc.watch succeeds if NFCReaderOptions.url is valid URL with "*"' +
    ' wildcard character in path.');
 
 nfc_test(() => {
   return navigator.nfc.watch(noop, {url:"https://foo.com/*/bar"});
-}, 'Test that nfc.watch succeeds if NFCWatchOptions.url is valid URL with "*"' +
+}, 'Test that nfc.watch succeeds if NFCReaderOptions.url is valid URL with "*"' +
    ' wildcard character in the beginning of path component followed by' +
    ' subpath.');
 
 nfc_test(() => {
   return navigator.nfc.watch(noop, {url:""});
-}, 'Test that nfc.watch succeeds if NFCWatchOptions.url is empty.')
+}, 'Test that nfc.watch succeeds if NFCReaderOptions.url is empty.')
 
 </script>
diff --git a/third_party/blink/web_tests/paint/invalidation/compositing/scrolling-neg-z-index-descendants-expected.txt b/third_party/blink/web_tests/paint/invalidation/compositing/scrolling-neg-z-index-descendants-expected.txt
index 834f486..2ef9ba3 100644
--- a/third_party/blink/web_tests/paint/invalidation/compositing/scrolling-neg-z-index-descendants-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/compositing/scrolling-neg-z-index-descendants-expected.txt
@@ -24,7 +24,7 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='neg-z'",
-      "position": [9, 19],
+      "position": [9, -81],
       "bounds": [100, 410],
       "backfaceVisibility": "hidden"
     },
diff --git a/third_party/blink/web_tests/platform/linux/fast/writing-mode/japanese-lr-selection-expected.png b/third_party/blink/web_tests/platform/linux/editing/selection/japanese-lr-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/linux/fast/writing-mode/japanese-lr-selection-expected.png
rename to third_party/blink/web_tests/platform/linux/editing/selection/japanese-lr-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/writing-mode/japanese-rl-selection-expected.png b/third_party/blink/web_tests/platform/linux/editing/selection/japanese-rl-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/linux/fast/writing-mode/japanese-rl-selection-expected.png
rename to third_party/blink/web_tests/platform/linux/editing/selection/japanese-rl-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/writing-mode/vertical-lr-replaced-selection-expected.png b/third_party/blink/web_tests/platform/linux/editing/selection/vertical-lr-replaced-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/linux/fast/writing-mode/vertical-lr-replaced-selection-expected.png
rename to third_party/blink/web_tests/platform/linux/editing/selection/vertical-lr-replaced-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/writing-mode/vertical-rl-replaced-selection-expected.png b/third_party/blink/web_tests/platform/linux/editing/selection/vertical-rl-replaced-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/linux/fast/writing-mode/vertical-rl-replaced-selection-expected.png
rename to third_party/blink/web_tests/platform/linux/editing/selection/vertical-rl-replaced-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/writing-mode/japanese-lr-selection-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/editing/selection/japanese-lr-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/mac-mac10.10/fast/writing-mode/japanese-lr-selection-expected.png
rename to third_party/blink/web_tests/platform/mac-mac10.10/editing/selection/japanese-lr-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/writing-mode/japanese-rl-selection-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/editing/selection/japanese-rl-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/mac-mac10.10/fast/writing-mode/japanese-rl-selection-expected.png
rename to third_party/blink/web_tests/platform/mac-mac10.10/editing/selection/japanese-rl-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/writing-mode/japanese-lr-selection-expected.png b/third_party/blink/web_tests/platform/mac/editing/selection/japanese-lr-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/mac/fast/writing-mode/japanese-lr-selection-expected.png
rename to third_party/blink/web_tests/platform/mac/editing/selection/japanese-lr-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/writing-mode/japanese-rl-selection-expected.png b/third_party/blink/web_tests/platform/mac/editing/selection/japanese-rl-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/mac/fast/writing-mode/japanese-rl-selection-expected.png
rename to third_party/blink/web_tests/platform/mac/editing/selection/japanese-rl-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/writing-mode/vertical-lr-replaced-selection-expected.png b/third_party/blink/web_tests/platform/mac/editing/selection/vertical-lr-replaced-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/mac/fast/writing-mode/vertical-lr-replaced-selection-expected.png
rename to third_party/blink/web_tests/platform/mac/editing/selection/vertical-lr-replaced-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/writing-mode/vertical-rl-replaced-selection-expected.png b/third_party/blink/web_tests/platform/mac/editing/selection/vertical-rl-replaced-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/mac/fast/writing-mode/vertical-rl-replaced-selection-expected.png
rename to third_party/blink/web_tests/platform/mac/editing/selection/vertical-rl-replaced-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/writing-mode/japanese-lr-selection-expected.png b/third_party/blink/web_tests/platform/win/editing/selection/japanese-lr-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/win/fast/writing-mode/japanese-lr-selection-expected.png
rename to third_party/blink/web_tests/platform/win/editing/selection/japanese-lr-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/writing-mode/japanese-rl-selection-expected.png b/third_party/blink/web_tests/platform/win/editing/selection/japanese-rl-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/win/fast/writing-mode/japanese-rl-selection-expected.png
rename to third_party/blink/web_tests/platform/win/editing/selection/japanese-rl-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/writing-mode/vertical-lr-replaced-selection-expected.png b/third_party/blink/web_tests/platform/win/editing/selection/vertical-lr-replaced-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/win/fast/writing-mode/vertical-lr-replaced-selection-expected.png
rename to third_party/blink/web_tests/platform/win/editing/selection/vertical-lr-replaced-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/writing-mode/vertical-rl-replaced-selection-expected.png b/third_party/blink/web_tests/platform/win/editing/selection/vertical-rl-replaced-selection-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/win/fast/writing-mode/vertical-rl-replaced-selection-expected.png
rename to third_party/blink/web_tests/platform/win/editing/selection/vertical-rl-replaced-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index c0b271c..5afccc46 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -6607,6 +6607,7 @@
 interface SpeechSynthesisEvent : Event
     attribute @@toStringTag
     getter charIndex
+    getter charLength
     getter elapsedTime
     getter name
     getter utterance
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 1e91d5a..ca7b4494 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -5330,6 +5330,7 @@
     attribute @@toStringTag
     getter details
     getter methodName
+    getter onpayerdetailchange
     getter payerEmail
     getter payerName
     getter payerPhone
@@ -5338,7 +5339,9 @@
     getter shippingOption
     method complete
     method constructor
+    method retry
     method toJSON
+    setter onpayerdetailchange
 interface Performance : EventTarget
     attribute @@toStringTag
     getter memory
diff --git a/third_party/closure_compiler/externs/mojo.js b/third_party/closure_compiler/externs/mojo.js
index 944bf21..78c5797 100644
--- a/third_party/closure_compiler/externs/mojo.js
+++ b/third_party/closure_compiler/externs/mojo.js
@@ -109,7 +109,7 @@
 /** @const */
 mojo.internal = {};
 
-mojo.internal.InterfaceProxyBase = class {
+mojo.internal.InterfaceRemoteBase = class {
   /**
    * @param {MojoHandle=} opt_handle
    */
diff --git a/third_party/dav1d/BUILD.gn b/third_party/dav1d/BUILD.gn
index 56d709e..37ced87 100644
--- a/third_party/dav1d/BUILD.gn
+++ b/third_party/dav1d/BUILD.gn
@@ -91,6 +91,9 @@
     "libdav1d/include/dav1d",
     platform_config_root,
   ]
+  if (is_win && !is_clang) {
+    include_dirs += [ "libdav1d/include/compat/msvc" ]
+  }
 }
 
 dav1d_copts = [
@@ -98,7 +101,11 @@
   "-D_POSIX_C_SOURCE=200112L",
 ]
 
-if (!is_win) {
+if (is_win) {
+  if (!is_clang) {
+    dav1d_copts += [ "/wd4028" ]
+  }
+} else {
   dav1d_copts += [ "-std=c99" ]
   if (needs_stack_alignment) {
     dav1d_copts += [ stackalign_flag ]
diff --git a/third_party/harfbuzz-ng/BUILD.gn b/third_party/harfbuzz-ng/BUILD.gn
index d081632f..d241a31 100644
--- a/third_party/harfbuzz-ng/BUILD.gn
+++ b/third_party/harfbuzz-ng/BUILD.gn
@@ -228,16 +228,6 @@
       # that is part of HarfBuzz.
       "HB_NO_UCD",
 
-      # Disables support for MATH and name table parsing. MATH table support
-      # will be put back in when we start to integrate MathML support.
-      "HB_NO_MATH",
-      "HB_NO_NAME",
-
-      # Disables serialization of hb_buffer_t for purposes of tools like
-      # hb-shape, not needed when used as a library.
-
-      "HB_NO_BUFFER_SERIALIZE",
-
       # TODO(https://crbug.com/949962): Remove once this is fixed upstream.
       "U_DISABLE_VERSION_SUFFIX=0",
     ]
diff --git a/tools/ipc_fuzzer/message_lib/all_messages.h b/tools/ipc_fuzzer/message_lib/all_messages.h
index 91f12c8..68df5e2d 100644
--- a/tools/ipc_fuzzer/message_lib/all_messages.h
+++ b/tools/ipc_fuzzer/message_lib/all_messages.h
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Multiply-included file, hence no include guard.
+// no-include-guard-because-multiply-included
+
 // Inclusion of all message files recognized by message_lib. All messages
 // received by RenderProcessHost should be included here for the IPC fuzzer.
 
@@ -23,7 +24,6 @@
 #include "components/guest_view/common/guest_view_message_generator.h"
 #include "components/network_hints/common/network_hints_message_generator.h"
 #include "components/subresource_filter/content/common/subresource_filter_messages.h"
-#include "components/tracing/common/tracing_messages.h"
 #include "content/common/all_messages.h"
 #include "extensions/common/extension_message_generator.h"
 #include "gpu/ipc/common/gpu_message_generator.h"
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 13b5a93..db81257 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -6050,6 +6050,15 @@
   </description>
 </action>
 
+<action name="GridTabSwitcher.DropTabToMerge">
+  <owner>yusufo@chromium.org</owner>
+  <owner>wychen@chromium.org</owner>
+  <description>
+    User drags a tab and drops it on another tab to form a group in grid tab
+    switcher.
+  </description>
+</action>
+
 <action name="GridTabSwitcher.UndoCloseTabGroup">
   <owner>yusufo@chromium.org</owner>
   <owner>wychen@chromium.org</owner>
@@ -20630,6 +20639,12 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="TabGrid.DragToReorder">
+  <owner>yusufo@chromium.org</owner>
+  <owner>wychen@chromium.org</owner>
+  <description>User drags a tab to reorder it.</description>
+</action>
+
 <action name="TabGridSheet.UndoCloseTab">
   <owner>yusufo@chromium.org</owner>
   <owner>wychen@chromium.org</owner>
@@ -21997,6 +22012,7 @@
   <suffix name="DownloadPageScreenshot"
       label="For DownloadPageScreenshot feature."/>
   <suffix name="ExploreSitesTile" label="For Explore Sites feature."/>
+  <suffix name="FeedCardMenu" label="For FeedCardMenu feature."/>
   <suffix name="HomePageButton" label="For HomePageButton feature."/>
   <suffix name="HomepageTile" label="For HomepageTile feature."/>
   <suffix name="IncognitoWindow" label="For IncognitoWindow feature."/>
@@ -22155,4 +22171,14 @@
   <affected-action name="MobileStackViewSwipeCloseTab"/>
 </action-suffix>
 
+<action-suffix separator="." ordering="suffix">
+  <suffix name="GridTabSwitcher"
+      label="Users drag to reorder tabs in grid layout TabSwitcher."/>
+  <suffix name="TabGridDialog"
+      label="Users drag to reorder tabs in tab grid dialog."/>
+  <suffix name="TabGridSheet"
+      label="Users drag to reorder tabs in tab group bottom sheet."/>
+  <affected-action name="TabGrid.DragToReorder"/>
+</action-suffix>
+
 </actions>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 47baa36..397d556 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -8942,6 +8942,9 @@
   <int value="21" label="QUIC/43"/>
   <int value="22" label="QUIC/99"/>
   <int value="23" label="QUIC/44"/>
+  <int value="24" label="QUIC/45"/>
+  <int value="25" label="QUIC/46"/>
+  <int value="26" label="QUIC/47"/>
   <int value="27" label="QUIC/999"/>
 </enum>
 
@@ -15007,6 +15010,12 @@
   <int value="11" label="Exceeded its maximum estimated device life time"/>
 </enum>
 
+<enum name="EmptyStaleResultLocation">
+  <int value="0" label="Before connection is started"/>
+  <int value="1" label="During stale and fresh host comparison"/>
+  <int value="2" label="The stale DNS request has valid results"/>
+</enum>
+
 <enum name="EncodingMethod">
   <int value="0" label="UNKNOWN"/>
   <int value="1" label="Big5"/>
@@ -20080,6 +20089,7 @@
   <int value="216" label="kEnterpriseHardwarePlatform"/>
   <int value="217" label="kLoginScreenUi"/>
   <int value="218" label="kDeclarativeNetRequestFeedback"/>
+  <int value="219" label="kTransientBackground"/>
 </enum>
 
 <enum name="ExtensionServiceVerifyAllSuccess">
@@ -33080,6 +33090,7 @@
   <int value="-2113705745"
       label="CrossOriginMediaPlaybackRequiresUserGesture:enabled"/>
   <int value="-2108564200" label="AutofillUpstream:disabled"/>
+  <int value="-2105133782" label="GesturePropertiesDBusService:enabled"/>
   <int value="-2104950596" label="HandwritingGesture:enabled"/>
   <int value="-2101682955" label="EnableNotificationIndicator:enabled"/>
   <int value="-2101337189" label="AutofillOffNoServerData:disabled"/>
@@ -34334,6 +34345,7 @@
   <int value="-322937746" label="disable-desktop-capture-picker-new-ui"/>
   <int value="-322827131" label="tab-management-experiment-type-basil"/>
   <int value="-320820051" label="enable-zero-copy"/>
+  <int value="-319002650" label="ArcUsbStorageUI:enabled"/>
   <int value="-315604713" label="WebUSB:enabled"/>
   <int value="-314910380" label="disable-distance-field-text"/>
   <int value="-314605926" label="protect-sync-credential-on-reauth:enabled"/>
@@ -34419,6 +34431,7 @@
   <int value="-174564579"
       label="ServiceWorkerImportedScriptUpdateCheck:enabled"/>
   <int value="-174319545" label="BulkPrinters:enabled"/>
+  <int value="-173268856" label="GesturePropertiesDBusService"/>
   <int value="-171232290" label="ListAllDisplayModes:disabled"/>
   <int value="-171173736" label="VrBrowsingExperimentalFeatures:disabled"/>
   <int value="-170986053" label="EnableManualFallbacksFilling:enabled"/>
@@ -35361,6 +35374,7 @@
       label="SyncPseudoUSSHistoryDeleteDirectives:disabled"/>
   <int value="1260186484" label="spurious-power-button-screen-accel"/>
   <int value="1261713150" label="ChromeHomeOptOutSnackbar:disabled"/>
+  <int value="1262469513" label="GesturePropertiesDBusService:disabled"/>
   <int value="1266156008" label="IdentityDisc:disabled"/>
   <int value="1266525177"
       label="AutofillUpstreamUseGooglePayOnAndroidBranding:disabled"/>
@@ -35407,6 +35421,7 @@
   <int value="1319068611" label="SecondaryUiMd:disabled"/>
   <int value="1319725131" label="enable-distance-field-text"/>
   <int value="1320201920" label="enable-touchpad-three-finger-click"/>
+  <int value="1320450434" label="ArcUsbStorageUI:disabled"/>
   <int value="1324623677"
       label="SessionRestorePrioritizesBackgroundUseCases:enabled"/>
   <int value="1330264457" label="OmniboxUIExperimentVerticalLayout:disabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index a70a777..73a9f0ce 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -15254,6 +15254,24 @@
   </summary>
 </histogram>
 
+<histogram name="Browser.WindowCount.Guest" expires_after="M85">
+  <owner>rhalavati@chromium.org</owner>
+  <owner>chrome-privacy-core@google.com</owner>
+  <summary>
+    Number of open guest browser windows at the same time. Recorded when a new
+    browser is created.
+  </summary>
+</histogram>
+
+<histogram name="Browser.WindowCount.Incognito" expires_after="M85">
+  <owner>rhalavati@chromium.org</owner>
+  <owner>chrome-privacy-core@google.com</owner>
+  <summary>
+    Number of open incognito browser windows at the same time. Recorded when a
+    new browser is created.
+  </summary>
+</histogram>
+
 <histogram name="BrowserActions.NumTabCreatedInBackground" units="tabNum">
   <owner>peconn@chromium.org</owner>
   <summary>
@@ -42316,7 +42334,7 @@
 
 <histogram name="FileSystem.OriginFailedCanCommitURL" enum="BooleanHit"
     expires_after="M77">
-  <owner>nick@chromium.org</owner>
+  <owner>alexmos@chromium.org</owner>
   <summary>
     Logged when we fail the permission validation of the filesystem URL origin,
     from ChildProcessSecurityPolicy::HasPermissionsForFileSystemFile. This is
@@ -56957,6 +56975,10 @@
 
 <histogram name="Media.Video.TimeFromForegroundToFirstFrame.DisableTrack"
     units="ms">
+  <obsolete>
+    Deprecated as of 06/2019 -- no longer used after background track disable
+    has launched.
+  </obsolete>
   <owner>dalecurtis@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -56969,6 +56991,10 @@
 </histogram>
 
 <histogram name="Media.Video.TimeFromForegroundToFirstFrame.Paused" units="ms">
+  <obsolete>
+    Deprecated as of 06/2019 -- no longer used after background track disable
+    has launched.
+  </obsolete>
   <owner>dalecurtis@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -71716,6 +71742,16 @@
   </summary>
 </histogram>
 
+<histogram name="Net.QuicSession.SessionAvailableWhenValidatingDNS"
+    enum="BooleanAvailable" expires_after="2019-12-31">
+  <owner>renjietang@chromium.org</owner>
+  <owner>zhongyi@chromium.org</owner>
+  <summary>
+    True if at the time of Dns comparison between fresh resolution and stale
+    resolution, session_ is valid.
+  </summary>
+</histogram>
+
 <histogram name="Net.QuicSession.SmoothedRTT" units="ms">
   <owner>rch@chromium.org</owner>
   <summary>
@@ -71735,6 +71771,16 @@
   <summary>True if the stale host is used in racing connection.</summary>
 </histogram>
 
+<histogram name="Net.QuicSession.StaleHostResolveFailed"
+    enum="EmptyStaleResultLocation" expires_after="2019-12-31">
+  <owner>renjietang@chromium.org</owner>
+  <owner>zhongyi@chromium.org</owner>
+  <summary>
+    Logs places in code where stale host resolution doesn't have valid results.
+    For control purpose, it also logs when a valid stale result is used.
+  </summary>
+</histogram>
+
 <histogram name="Net.QuicSession.StreamCloseErrorCodeClient.HandshakeConfirmed"
     enum="QuicErrorCodes">
   <owner>rch@chromium.org</owner>
@@ -82377,6 +82423,9 @@
 
 <histogram name="OfflinePages.CanSaveRecentPage" enum="Boolean"
     expires_after="2019-05-31">
+  <obsolete>
+    Removed on 6/2019. No longer considered useful.
+  </obsolete>
   <owner>carlosk@chromium.org</owner>
   <owner>fgorski@chromium.org</owner>
   <summary>
@@ -84511,6 +84560,27 @@
   </summary>
 </histogram>
 
+<histogram name="Omnibox.RichEntityShown" units="count" expires_after="M84">
+  <owner>chrome-android-omnibox-team@google.com</owner>
+  <owner>jdonnelly@google.com</owner>
+  <owner>mpearson@google.com</owner>
+  <summary>
+    Records number of presented Rich Entity suggestions at the time the user
+    exited the omnibox. Exiting the omnibox includes navigating (to entered text
+    or any suggestion), pressing the system back key, clearing omnibox, blanking
+    screen / locking the phone (whether intentionally or due to inactivity), or
+    closing the Chrome app. This metric is logged every time the omnibox is
+    exited, including when no entities are present in the list of suggestions.
+
+    Nota bene: in some circumstances this histogram is double counting on
+    purpose. Scenarios cover repetitive actions that expose same entity
+    suggestions, ie. leave-enter-leave application, or leave-enter-clear where
+    no new suggestion fetches are done.
+
+    This histogram is related to Omnibox.SuggestionUsed.RichEntity.
+  </summary>
+</histogram>
+
 <histogram name="Omnibox.SaveStateForTabSwitch.UserInputInProgress"
     units="count" expires_after="M77">
   <owner>mpearson@chromium.org</owner>
@@ -84756,6 +84826,19 @@
   </summary>
 </histogram>
 
+<histogram name="Omnibox.SuggestionUsed.RichEntity" enum="BooleanUsage"
+    expires_after="M84">
+  <owner>chrome-android-omnibox-team@google.com</owner>
+  <owner>jdonnelly@google.com</owner>
+  <owner>mpearson@google.com</owner>
+  <summary>
+    Whether a Rich Entity omnibox suggestion was selected when the user used the
+    omnibox to go somewhere.
+
+    This histogram is related to Omnibox.RichEntityShown.
+  </summary>
+</histogram>
+
 <histogram
     name="Omnibox.SuggestionUsed.Search.Experimental.ForegroundToFirstMeaningfulPaint.Prerender"
     units="ms">
@@ -115059,7 +115142,8 @@
     foreground from the start of the navigation to the page visit completing due
     to a new navigation or the tab being closed). This aggregates all foreground
     time over the entire visit (multiple times in the foreground are added
-    together).
+    together). For pages that were never backgrounded, this histogram
+    (accidentally) collects 0 instead of the actual foreground time.
   </summary>
 </histogram>
 
@@ -121455,7 +121539,8 @@
   </summary>
 </histogram>
 
-<histogram name="SiteIsolation.IsolateAllSitesProcessCountEstimate">
+<histogram name="SiteIsolation.IsolateAllSitesProcessCountEstimate"
+    expires_after="M82">
   <owner>creis@chromium.org</owner>
   <summary>
     The upper bound of the predicted renderer process count if we isolated all
@@ -121463,7 +121548,8 @@
   </summary>
 </histogram>
 
-<histogram name="SiteIsolation.IsolateAllSitesProcessCountLowerBound">
+<histogram name="SiteIsolation.IsolateAllSitesProcessCountLowerBound"
+    expires_after="M82">
   <owner>creis@chromium.org</owner>
   <summary>
     The lower bound of the predicted renderer process count if we isolated all
@@ -121472,7 +121558,8 @@
   </summary>
 </histogram>
 
-<histogram name="SiteIsolation.IsolateAllSitesProcessCountNoLimit">
+<histogram name="SiteIsolation.IsolateAllSitesProcessCountNoLimit"
+    expires_after="M82">
   <owner>creis@chromium.org</owner>
   <summary>
     The predicted renderer process count if we isolated all sites and if there
@@ -121480,7 +121567,8 @@
   </summary>
 </histogram>
 
-<histogram name="SiteIsolation.IsolateAllSitesTotalProcessCountEstimate">
+<histogram name="SiteIsolation.IsolateAllSitesTotalProcessCountEstimate"
+    expires_after="M82">
   <owner>creis@chromium.org</owner>
   <summary>
     The predicted total process count if we isolated all sites, subject to the
@@ -121490,7 +121578,8 @@
 
 <histogram name="SiteIsolation.IsolateExtensionsProcessCountEstimate"
     expires_after="M77">
-  <owner>creis@chromium.orgnick@chromium.org</owner>
+  <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <owner>site-isolation-dev@chromium.org</owner>
   <summary>
     The upper bound of the predicted renderer process count if we isolated only
@@ -121500,7 +121589,8 @@
 
 <histogram name="SiteIsolation.IsolateExtensionsProcessCountLowerBound"
     expires_after="M77">
-  <owner>creis@chromium.orgnick@chromium.org</owner>
+  <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <owner>site-isolation-dev@chromium.org</owner>
   <summary>
     The lower bound of the predicted renderer process count if we isolated only
@@ -121510,7 +121600,8 @@
 
 <histogram name="SiteIsolation.IsolateExtensionsProcessCountNoLimit"
     expires_after="M77">
-  <owner>creis@chromium.orgnick@chromium.org</owner>
+  <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <owner>site-isolation-dev@chromium.org</owner>
   <summary>
     The predicted renderer process count if we isolated only Chrome extensions
@@ -121520,7 +121611,8 @@
 
 <histogram name="SiteIsolation.IsolateExtensionsTotalProcessCountEstimate"
     expires_after="M77">
-  <owner>creis@chromium.orgnick@chromium.org</owner>
+  <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <owner>site-isolation-dev@chromium.org</owner>
   <summary>
     The predicted total process count if we isolated only Chrome extensions,
@@ -121528,8 +121620,10 @@
   </summary>
 </histogram>
 
-<histogram name="SiteIsolation.IsolateHttpsSitesProcessCountEstimate">
-  <owner>creis@chromium.orgnick@chromium.org</owner>
+<histogram name="SiteIsolation.IsolateHttpsSitesProcessCountEstimate"
+    expires_after="M82">
+  <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <owner>site-isolation-dev@chromium.org</owner>
   <summary>
     The upper bound of the predicted renderer process count if we isolated only
@@ -121538,8 +121632,10 @@
   </summary>
 </histogram>
 
-<histogram name="SiteIsolation.IsolateHttpsSitesProcessCountLowerBound">
-  <owner>creis@chromium.orgnick@chromium.org</owner>
+<histogram name="SiteIsolation.IsolateHttpsSitesProcessCountLowerBound"
+    expires_after="M82">
+  <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <owner>site-isolation-dev@chromium.org</owner>
   <summary>
     The lower bound of the predicted renderer process count if we isolated only
@@ -121548,8 +121644,10 @@
   </summary>
 </histogram>
 
-<histogram name="SiteIsolation.IsolateHttpsSitesProcessCountNoLimit">
-  <owner>creis@chromium.orgnick@chromium.org</owner>
+<histogram name="SiteIsolation.IsolateHttpsSitesProcessCountNoLimit"
+    expires_after="M82">
+  <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <owner>site-isolation-dev@chromium.org</owner>
   <summary>
     The predicted renderer process count if we isolated only HTTPS (not HTTP)
@@ -121557,8 +121655,10 @@
   </summary>
 </histogram>
 
-<histogram name="SiteIsolation.IsolateHttpsSitesTotalProcessCountEstimate">
-  <owner>creis@chromium.orgnick@chromium.org</owner>
+<histogram name="SiteIsolation.IsolateHttpsSitesTotalProcessCountEstimate"
+    expires_after="M82">
+  <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <owner>site-isolation-dev@chromium.org</owner>
   <summary>
     The predicted total process count if we isolated only HTTPS (not HTTP)
@@ -121567,8 +121667,9 @@
 </histogram>
 
 <histogram name="SiteIsolation.IsolateNothingProcessCountEstimate"
-    expires_after="M77">
-  <owner>creis@chromium.orgnick@chromium.org</owner>
+    expires_after="M82">
+  <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <owner>site-isolation-dev@chromium.org</owner>
   <summary>
     The upper bound of the estimated renderer process count if we isolated no
@@ -121577,8 +121678,9 @@
 </histogram>
 
 <histogram name="SiteIsolation.IsolateNothingProcessCountLowerBound"
-    expires_after="M77">
-  <owner>creis@chromium.orgnick@chromium.org</owner>
+    expires_after="M82">
+  <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <owner>site-isolation-dev@chromium.org</owner>
   <summary>
     The lower bound of the predicted renderer process count if we isolated no
@@ -121588,8 +121690,9 @@
 </histogram>
 
 <histogram name="SiteIsolation.IsolateNothingProcessCountNoLimit"
-    expires_after="M77">
-  <owner>creis@chromium.orgnick@chromium.org</owner>
+    expires_after="M82">
+  <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <owner>site-isolation-dev@chromium.org</owner>
   <summary>
     The predicted renderer process count if we isolated no sites and if there
@@ -121598,8 +121701,9 @@
 </histogram>
 
 <histogram name="SiteIsolation.IsolateNothingTotalProcessCountEstimate"
-    expires_after="M77">
-  <owner>creis@chromium.orgnick@chromium.org</owner>
+    expires_after="M82">
+  <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <owner>site-isolation-dev@chromium.org</owner>
   <summary>
     The predicted total process count if we isolated no sites, subject to the
@@ -121645,7 +121749,8 @@
 </histogram>
 
 <histogram name="SiteIsolation.ProxyCount">
-  <owner>creis@chromium.orgnick@chromium.org</owner>
+  <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <owner>site-isolation-dev@chromium.org</owner>
   <summary>
     The count of all RenderFrameProxyHosts. Recorded once per UMA ping.
@@ -121653,7 +121758,8 @@
 </histogram>
 
 <histogram name="SiteIsolation.ProxyCountPerBrowsingInstance">
-  <owner>creis@chromium.orgnick@chromium.org</owner>
+  <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <owner>site-isolation-dev@chromium.org</owner>
   <summary>
     The count of RenderFrameProxyHosts in each BrowsingInstance. Recorded each
@@ -123791,7 +123897,7 @@
 
 <histogram name="Stability.BadMessageTerminated.Chrome"
     enum="BadMessageReasonChrome">
-  <owner>nick@chromium.org</owner>
+  <owner>creis@chromium.org</owner>
   <owner>jamescook@chromium.org</owner>
   <summary>
     Count of processes killed by chrome/browser because they sent an IPC that
@@ -123831,7 +123937,7 @@
 
 <histogram name="Stability.BadMessageTerminated.NaCl"
     enum="BadMessageReasonNaCl">
-  <owner>nick@chromium.org</owner>
+  <owner>creis@chromium.org</owner>
   <owner>jamescook@chromium.org</owner>
   <summary>
     Count of processes killed because they sent a NaCl IPC that couldn't be
@@ -123841,7 +123947,7 @@
 
 <histogram name="Stability.BadMessageTerminated.PasswordManager"
     enum="BadMessageReasonPasswordManager">
-  <owner>nick@chromium.org</owner>
+  <owner>creis@chromium.org</owner>
   <owner>jamescook@chromium.org</owner>
   <summary>
     Count of processes killed because they sent a bad IPC that couldn't be
@@ -131638,6 +131744,30 @@
   </summary>
 </histogram>
 
+<histogram name="Tabs.ScrubbedInInterval.KeyPress" units="tabs"
+    expires_after="2019-12-01">
+  <owner>corising@chromium.org</owner>
+  <owner>chrome-desktop-ui-sea@google.com</owner>
+  <summary>
+    This histogram records the number of tabs that are 'scrubbed' by key press
+    during a given interval of time (i.e. ctrl+tab navigation). For this metric,
+    a tab is considered 'scrubbed' if it was active for less than or equal to a
+    given amount of time.
+  </summary>
+</histogram>
+
+<histogram name="Tabs.ScrubbedInInterval.MousePress" units="tabs"
+    expires_after="2019-12-01">
+  <owner>corising@chromium.org</owner>
+  <owner>chrome-desktop-ui-sea@google.com</owner>
+  <summary>
+    This histogram records the number of tabs that are 'scrubbed' by mouse press
+    during a given interval of time. For this metric, a tab is considered
+    'scrubbed' if it was active for less than or equal to a given amount of
+    time.
+  </summary>
+</histogram>
+
 <histogram name="Tabs.ScrubDistance" units="tabs" expires_after="2018-08-30">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
@@ -132889,6 +133019,9 @@
 
 <histogram name="Thumbnails.CaptureOutcome" enum="ThumbnailCaptureOutcome"
     expires_after="M77">
+  <obsolete>
+    Removed 2019-03.
+  </obsolete>
   <owner>treib@chromium.org</owner>
   <summary>
     The result of trying to capture a thumbnail of the current page.
@@ -146860,6 +146993,7 @@
   <suffix name="Addresses" label="Address suggestions."/>
   <suffix name="CreditCards" label="Payment suggestions."/>
   <suffix name="Passwords" label="Password suggestions and generation."/>
+  <suffix name="TouchToFill" label="Password suggestions filled on touch."/>
   <affected-histogram name="KeyboardAccessory.AccessorySheetSuggestionCount"/>
   <affected-histogram
       name="KeyboardAccessory.AccessorySheetSuggestionsSelected"/>
@@ -147550,17 +147684,19 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="AutofillAddressFormType" separator=".">
-  <suffix name="AddressOnly" label="Form has name and address fields"/>
+  <suffix name="AddressOnly" label="Form has address and maybe name fields"/>
   <suffix name="AddressPlusContact"
       label="Encompasses AddressPlusEmail, AddressPlusPhone, and
              AddressPlusEmailPlusPhone"/>
   <suffix name="AddressPlusEmail"
-      label="Form has name, address, and email fields"/>
+      label="Form has address, email, and maybe name fields"/>
   <suffix name="AddressPlusEmailPlusPhone"
-      label="Form has name, address, email, and phone fields"/>
+      label="Form has address, email, phone, and maybe name fields"/>
   <suffix name="AddressPlusPhone"
-      label="Form has name, address, and phone fields"/>
-  <suffix name="ContactOnly" label="Form has name and phone or email fields"/>
+      label="Form has address, phone, and maybe name fields"/>
+  <suffix name="ContactOnly"
+      label="Form has name and phone; name and email; or phone, email, and
+             maybe name fields"/>
   <suffix name="Other"
       label="Another form type was encountered, such as a form with all
              unknown fields"/>
@@ -152070,6 +152206,8 @@
   <suffix name="IPH_DownloadSettings"
       label="In product help to access download settings from download home."/>
   <suffix name="IPH_ExploreSitesTile" label="For Explore Sites feature."/>
+  <suffix name="IPH_FeedCardMenu"
+      label="In product help feed card menu on NTP."/>
   <suffix name="IPH_HomePageButton" label="In product help home page button."/>
   <suffix name="IPH_HomepageTile" label="In product help homepage tile."/>
   <suffix name="IPH_IncognitoWindow" label="In product help incognito window."/>
@@ -161183,6 +161321,9 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="ThumbnailCaptureTrigger" separator=".">
+  <obsolete>
+    Removed 2019-03.
+  </obsolete>
   <suffix name="NavigatingAway" label="Triggered by navigating away."/>
   <suffix name="TabHidden" label="Triggered by the tab being hidden."/>
   <affected-histogram name="Thumbnails.CaptureOutcome"/>
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 9d45779..2e707a95d 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -135,11 +135,6 @@
 # build/scripts/slave/recipe_modules/chromium_tests and must be kept in sync
 # to generate the correct json for each tester
 #
-# The dimensions in pinpoint configs, excluding the dimension "pool",
-# must be kept in sync with the dimensions here.
-# This is to make sure the same type of machines are used between waterfall
-# tests and pinpoint jobs
-#
 # On desktop builders, chromedriver is added as an additional compile target.
 # The perf waterfall builds this target for each commit, and the resulting
 # ChromeDriver is archived together with Chrome for use in bisecting.
@@ -477,10 +472,8 @@
     'target_bits': 64,
     'dimension': {
       'pool': 'chrome.tests.perf',
-      # TODO Add more specific windows version.
-      # See crbug.com/966238 for more detail
       'os': 'Windows-10',
-      'synthetic_product_name': 'OptiPlex 7050 (Dell Inc.)'
+      'gpu': '8086:5912'
     },
   },
   'Win 7 Perf': {
diff --git a/tools/perf/core/perf_json_config_validator.py b/tools/perf/core/perf_json_config_validator.py
index 45e1d4c..ff1770d 100644
--- a/tools/perf/core/perf_json_config_validator.py
+++ b/tools/perf/core/perf_json_config_validator.py
@@ -11,8 +11,7 @@
 
 _VALID_SWARMING_DIMENSIONS = {
     'gpu', 'device_ids', 'os', 'pool', 'perf_tests', 'perf_tests_with_args',
-    'device_os', 'device_type', 'device_os_flavor', 'id',
-    'synthetic_product_name'}
+    'device_os', 'device_type', 'device_os_flavor', 'id'}
 _DEFAULT_VALID_PERF_POOLS = {
     'chrome.tests.perf', 'chrome.tests.perf-webview',
     'chrome.tests.perf-fyi', 'chrome.tests.perf-webview-fyi'}
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index a3063b4..704e331 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -339,11 +339,6 @@
 crbug.com/954949 [ Nexus5X_Webview ] system_health.memory_mobile/browse:news:washingtonpost [ Skip ]
 crbug.com/961417 [ Android_Go ] system_health.memory_mobile/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
 
-crbug.com/964804 [ Nexus_5X ] system_health.memory_mobile/browse:media:flickr_infinite_scroll [ Skip ]
-crbug.com/964804 [ Nexus_5X ] system_health.memory_mobile/browse:search:amp:2018 [ Skip ]
-crbug.com/964804 [ Nexus_5X ] system_health.memory_mobile/load:games:lazors [ Skip ]
-crbug.com/964804 [ Nexus_5X ] system_health.memory_mobile/load:games:spychase:2018 [ Skip ]
-crbug.com/964804 [ Nexus_5X ] system_health.memory_mobile/load:media:flickr:2018 [ Skip ]
 crbug.com/964804 [ Nexus_5X ] system_health.memory_mobile/load:news:irctc [ Skip ]
 crbug.com/964804 [ Nexus_5X ] system_health.memory_mobile/load:search:baidu:2018 [ Skip ]
 crbug.com/964804 [ Nexus_5X ] system_health.memory_mobile/load:search:google:2018 [ Skip ]
diff --git a/ui/accessibility/ax_enum_util.cc b/ui/accessibility/ax_enum_util.cc
index 97591d4..d1a9587 100644
--- a/ui/accessibility/ax_enum_util.cc
+++ b/ui/accessibility/ax_enum_util.cc
@@ -2254,10 +2254,6 @@
       return "false";
     case ax::mojom::InvalidState::kTrue:
       return "true";
-    case ax::mojom::InvalidState::kSpelling:
-      return "spelling";
-    case ax::mojom::InvalidState::kGrammar:
-      return "grammar";
     case ax::mojom::InvalidState::kOther:
       return "other";
   }
@@ -2272,10 +2268,6 @@
     return ax::mojom::InvalidState::kFalse;
   if (0 == strcmp(invalid_state, "true"))
     return ax::mojom::InvalidState::kTrue;
-  if (0 == strcmp(invalid_state, "spelling"))
-    return ax::mojom::InvalidState::kSpelling;
-  if (0 == strcmp(invalid_state, "grammar"))
-    return ax::mojom::InvalidState::kGrammar;
   if (0 == strcmp(invalid_state, "other"))
     return ax::mojom::InvalidState::kOther;
   return ax::mojom::InvalidState::kNone;
diff --git a/ui/accessibility/ax_enums.mojom b/ui/accessibility/ax_enums.mojom
index b55eeef..bec9f85 100644
--- a/ui/accessibility/ax_enums.mojom
+++ b/ui/accessibility/ax_enums.mojom
@@ -852,8 +852,6 @@
   kNone,
   kFalse,
   kTrue,
-  kSpelling,
-  kGrammar,
   kOther,
 };
 
diff --git a/ui/accessibility/ax_event_generator_unittest.cc b/ui/accessibility/ax_event_generator_unittest.cc
index d51a24e..7b47f1bc 100644
--- a/ui/accessibility/ax_event_generator_unittest.cc
+++ b/ui/accessibility/ax_event_generator_unittest.cc
@@ -455,7 +455,7 @@
 
   AXEventGenerator event_generator(&tree);
   AXTreeUpdate update = initial_state;
-  update.nodes[0].SetInvalidState(ax::mojom::InvalidState::kSpelling);
+  update.nodes[0].SetInvalidState(ax::mojom::InvalidState::kTrue);
   ASSERT_TRUE(tree.Unserialize(update));
   EXPECT_EQ("INVALID_STATUS_CHANGED on 1", DumpEvents(&event_generator));
 }
diff --git a/ui/accessibility/ax_node.cc b/ui/accessibility/ax_node.cc
index f140e988..1f79991b 100644
--- a/ui/accessibility/ax_node.cc
+++ b/ui/accessibility/ax_node.cc
@@ -23,21 +23,22 @@
                size_t index_in_parent)
     : tree_(tree),
       index_in_parent_(index_in_parent),
+      unignored_child_count_(0),
       parent_(parent),
       language_info_(nullptr) {
   data_.id = id;
+  // If this node is the root, use the given index_in_parent to provide
+  // consistency.
+  if (!parent)
+    unignored_index_in_parent_ = index_in_parent_;
+  else
+    unignored_index_in_parent_ = 0;
 }
 
 AXNode::~AXNode() = default;
 
 size_t AXNode::GetUnignoredChildCount() const {
-  size_t count = 0;
-  for (const AXNode* child : children_) {
-    count += (child->data().HasState(ax::mojom::State::kIgnored))
-                 ? child->GetUnignoredChildCount()
-                 : 1;
-  }
-  return count;
+  return unignored_child_count_;
 }
 
 AXNodeData&& AXNode::TakeData() {
@@ -70,15 +71,8 @@
 }
 
 size_t AXNode::GetUnignoredIndexInParent() const {
-  AXNode* parent = GetUnignoredParent();
-  if (parent) {
-    for (size_t i = 0; i < parent->GetUnignoredChildCount(); ++i) {
-      if (parent->GetUnignoredChildAtIndex(i) == this)
-        return i;
-    }
-  }
-
-  return 0;
+  DCHECK(!data().HasState(ax::mojom::State::kIgnored));
+  return unignored_index_in_parent_;
 }
 
 bool AXNode::IsText() const {
@@ -112,6 +106,11 @@
   index_in_parent_ = index_in_parent;
 }
 
+void AXNode::UpdateUnignoredCachedValues() {
+  if (!data().HasState(ax::mojom::State::kIgnored))
+    UpdateUnignoredCachedValuesRecursive(0);
+}
+
 void AXNode::SwapChildren(std::vector<AXNode*>& children) {
   children.swap(children_);
 }
@@ -682,6 +681,20 @@
   }
 }
 
+int AXNode::UpdateUnignoredCachedValuesRecursive(int startIndex) {
+  int count = 0;
+  for (AXNode* child : children_) {
+    if (child->data().HasState(ax::mojom::State::kIgnored)) {
+      child->unignored_index_in_parent_ = 0;
+      count += child->UpdateUnignoredCachedValuesRecursive(startIndex + count);
+    } else {
+      child->unignored_index_in_parent_ = startIndex + count++;
+    }
+  }
+  unignored_child_count_ = count;
+  return count;
+}
+
 // Finds ordered set that immediately contains node.
 // Is not required for set's role to match node's role.
 AXNode* AXNode::GetOrderedSet() const {
diff --git a/ui/accessibility/ax_node.h b/ui/accessibility/ax_node.h
index 0c140c7..68a05367 100644
--- a/ui/accessibility/ax_node.h
+++ b/ui/accessibility/ax_node.h
@@ -90,6 +90,9 @@
   // Set the index in parent, for example if siblings were inserted or deleted.
   void SetIndexInParent(size_t index_in_parent);
 
+  // Update the unignored index in parent for unignored children.
+  void UpdateUnignoredCachedValues();
+
   // Swap the internal children vector with |children|. This instance
   // now owns all of the passed children.
   void SwapChildren(std::vector<AXNode*>& children);
@@ -304,11 +307,15 @@
   void IdVectorToNodeVector(std::vector<int32_t>& ids,
                             std::vector<AXNode*>* nodes) const;
 
+  int UpdateUnignoredCachedValuesRecursive(int startIndex);
+
   // Finds and returns a pointer to ordered set containing node.
   AXNode* GetOrderedSet() const;
 
   OwnerTree* tree_;  // Owns this.
   size_t index_in_parent_;
+  size_t unignored_index_in_parent_;
+  size_t unignored_child_count_;
   AXNode* parent_;
   std::vector<AXNode*> children_;
   AXNodeData data_;
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc
index 751c6b6a..ff9e226 100644
--- a/ui/accessibility/ax_node_data.cc
+++ b/ui/accessibility/ax_node_data.cc
@@ -1202,12 +1202,6 @@
           case ax::mojom::InvalidState::kTrue:
             result += " invalid_state=true";
             break;
-          case ax::mojom::InvalidState::kSpelling:
-            result += " invalid_state=spelling";
-            break;
-          case ax::mojom::InvalidState::kGrammar:
-            result += " invalid_state=grammar";
-            break;
           case ax::mojom::InvalidState::kOther:
             result += " invalid_state=other";
             break;
diff --git a/ui/accessibility/ax_table_info.cc b/ui/accessibility/ax_table_info.cc
index 80006e6..5f97728 100644
--- a/ui/accessibility/ax_table_info.cc
+++ b/ui/accessibility/ax_table_info.cc
@@ -379,8 +379,13 @@
   data.id = id;
   data.role = ax::mojom::Role::kColumn;
   node->SetData(data);
-  for (AXTreeObserver& observer : tree_->observers())
+  for (AXTreeObserver& observer : tree_->observers()) {
     observer.OnNodeCreated(tree_, node);
+    observer.OnAtomicUpdateFinished(
+        tree_, false,
+        {AXTreeObserver::Change(node,
+                                AXTreeObserver::ChangeType::NODE_CREATED)});
+  }
   return node;
 }
 
@@ -392,8 +397,14 @@
   data.id = id;
   data.role = ax::mojom::Role::kTableHeaderContainer;
   node->SetData(data);
-  for (AXTreeObserver& observer : tree_->observers())
+
+  for (AXTreeObserver& observer : tree_->observers()) {
     observer.OnNodeCreated(tree_, node);
+    observer.OnAtomicUpdateFinished(
+        tree_, false,
+        {AXTreeObserver::Change(node,
+                                AXTreeObserver::ChangeType::NODE_CREATED)});
+  }
 
   return node;
 }
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index 928a0db..a58715a 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -134,6 +134,10 @@
   // decisions about when to notify observers of removals or reparenting.
   std::set<int> changed_node_ids;
 
+  // keeps track of parents whose unignored children have changed. Used for
+  // caching unignored relationships.
+  std::unordered_set<int> changed_unignored_parent_ids;
+
   // Keeps track of new nodes created during this update.
   std::set<const AXNode*> new_nodes;
 
@@ -500,6 +504,12 @@
     changes.push_back(AXTreeObserver::Change(node, change));
   }
 
+  for (int parent_id : update_state.changed_unignored_parent_ids) {
+    AXNode* parent = GetFromId(parent_id);
+    if (parent)
+      parent->UpdateUnignoredCachedValues();
+  }
+
   // Tree is no longer updating.
   SetTreeUpdateInProgressState(false);
 
@@ -568,6 +578,10 @@
     else
       observer.OnNodeReparented(this, new_node);
   }
+  AXNode* unignored_parent = new_node->GetUnignoredParent();
+  if (unignored_parent) {
+    update_state->changed_unignored_parent_ids.insert(unignored_parent->id());
+  }
   return new_node;
 }
 
@@ -591,10 +605,18 @@
     if (!update_state->IsNewNode(node) ||
         update_state->IsReparentedNode(node)) {
       auto it = update_state->reparented_node_id_to_data.find(node->id());
-      if (it != update_state->reparented_node_id_to_data.end())
-        CallNodeChangeCallbacks(node, it->second, src);
-      else
-        CallNodeChangeCallbacks(node, node->data(), src);
+      const AXNodeData& old_data =
+          it != update_state->reparented_node_id_to_data.end() ? it->second
+                                                               : node->data();
+      CallNodeChangeCallbacks(node, old_data, src);
+      if (old_data.HasState(ax::mojom::State::kIgnored) !=
+          src.HasState(ax::mojom::State::kIgnored)) {
+        AXNode* unignored_parent = node->GetUnignoredParent();
+        if (unignored_parent) {
+          update_state->changed_unignored_parent_ids.insert(
+              unignored_parent->id());
+        }
+      }
     }
     UpdateReverseRelations(node, src);
     node->SetData(src);
@@ -853,6 +875,10 @@
   for (auto* child : node->children())
     DestroyNodeAndSubtree(child, update_state);
   if (update_state) {
+    AXNode* unignored_parent = node->GetUnignoredParent();
+    if (unignored_parent) {
+      update_state->changed_unignored_parent_ids.insert(unignored_parent->id());
+    }
     update_state->pending_nodes.erase(node);
     update_state->removed_node_ids.insert(node->id());
   }
@@ -972,8 +998,8 @@
   bool node_is_radio_button =
       (original_node.data().role == ax::mojom::Role::kRadioButton);
 
-  for (size_t i = 0; i < local_parent->children().size(); ++i) {
-    const AXNode* child = local_parent->children()[i];
+  for (size_t i = 0; i < local_parent->GetUnignoredChildCount(); ++i) {
+    const AXNode* child = local_parent->GetUnignoredChildAtIndex(i);
 
     // Invisible children should not be counted.
     // However, in the collapsed container case (e.g. a combobox), items can
diff --git a/ui/accessibility/ax_tree_unittest.cc b/ui/accessibility/ax_tree_unittest.cc
index 58153b7..83f5094c 100644
--- a/ui/accessibility/ax_tree_unittest.cc
+++ b/ui/accessibility/ax_tree_unittest.cc
@@ -1403,6 +1403,119 @@
   EXPECT_EQ(1, root->GetUnignoredChildAtIndex(0)->GetUnignoredParent()->id());
 }
 
+TEST(AXTreeTest, CachedUnignoredValues) {
+  AXTreeUpdate initial_state;
+  initial_state.root_id = 1;
+  initial_state.nodes.resize(5);
+  initial_state.nodes[0].id = 1;
+  initial_state.nodes[0].child_ids = {2, 3};
+  initial_state.nodes[1].id = 2;
+  initial_state.nodes[1].AddState(ax::mojom::State::kIgnored);
+  initial_state.nodes[1].child_ids = {4, 5};
+  initial_state.nodes[2].id = 3;
+  initial_state.nodes[3].id = 4;
+  initial_state.nodes[4].id = 5;
+
+  AXTree tree(initial_state);
+  AXNode* root = tree.root();
+  ASSERT_EQ(2u, root->children().size());
+  ASSERT_EQ(2, root->children()[0]->id());
+  ASSERT_EQ(3, root->children()[1]->id());
+
+  EXPECT_EQ(3u, root->GetUnignoredChildCount());
+  EXPECT_EQ(4, root->GetUnignoredChildAtIndex(0)->id());
+  EXPECT_EQ(5, root->GetUnignoredChildAtIndex(1)->id());
+  EXPECT_EQ(3, root->GetUnignoredChildAtIndex(2)->id());
+  EXPECT_EQ(0u, root->GetUnignoredChildAtIndex(0)->GetUnignoredIndexInParent());
+  EXPECT_EQ(1u, root->GetUnignoredChildAtIndex(1)->GetUnignoredIndexInParent());
+  EXPECT_EQ(2u, root->GetUnignoredChildAtIndex(2)->GetUnignoredIndexInParent());
+
+  EXPECT_EQ(1, root->GetUnignoredChildAtIndex(0)->GetUnignoredParent()->id());
+
+  // Ensure when a node goes from ignored to unignored, its children have their
+  // unignored_index_in_parent updated.
+  AXTreeUpdate update = initial_state;
+  update.nodes[1].RemoveState(ax::mojom::State::kIgnored);
+
+  EXPECT_TRUE(tree.Unserialize(update));
+
+  root = tree.root();
+  EXPECT_EQ(2u, root->GetUnignoredChildCount());
+  EXPECT_EQ(2, root->GetUnignoredChildAtIndex(0)->id());
+  EXPECT_EQ(0u, tree.GetFromId(4)->GetUnignoredIndexInParent());
+  EXPECT_EQ(1u, tree.GetFromId(5)->GetUnignoredIndexInParent());
+
+  // Ensure when a node goes from unignored to unignored, siblings are correctly
+  // updated.
+  AXTreeUpdate update2 = update;
+  update2.nodes[3].AddState(ax::mojom::State::kIgnored);
+
+  EXPECT_TRUE(tree.Unserialize(update2));
+
+  EXPECT_EQ(0u, tree.GetFromId(5)->GetUnignoredIndexInParent());
+
+  // Ensure siblings of a deleted node are updated.
+  AXTreeUpdate update3 = update2;
+  update3.nodes.resize(1);
+  update3.nodes[0].id = 1;
+  update3.nodes[0].child_ids = {3};
+
+  EXPECT_TRUE(tree.Unserialize(update3));
+
+  EXPECT_EQ(0u, tree.GetFromId(3)->GetUnignoredIndexInParent());
+
+  // Ensure new nodes are correctly updated.
+  AXTreeUpdate update4 = update3;
+  update4.nodes.resize(3);
+  update4.nodes[0].id = 1;
+  update4.nodes[0].child_ids = {3, 6};
+  update4.nodes[1].id = 6;
+  update4.nodes[1].child_ids = {7};
+  update4.nodes[2].id = 7;
+
+  EXPECT_TRUE(tree.Unserialize(update4));
+
+  EXPECT_EQ(0u, tree.GetFromId(3)->GetUnignoredIndexInParent());
+  EXPECT_EQ(1u, tree.GetFromId(6)->GetUnignoredIndexInParent());
+  EXPECT_EQ(0u, tree.GetFromId(7)->GetUnignoredIndexInParent());
+
+  // Ensure reparented nodes are correctly updated.
+  AXTreeUpdate update5 = update4;
+  update5.nodes.resize(2);
+  update5.node_id_to_clear = 6;
+  update5.nodes[0].id = 1;
+  update5.nodes[0].child_ids = {3, 7};
+  update5.nodes[1].id = 7;
+  update5.nodes[1].child_ids = {};
+
+  EXPECT_TRUE(tree.Unserialize(update5));
+
+  EXPECT_EQ(2u, tree.GetFromId(1)->GetUnignoredChildCount());
+  EXPECT_EQ(0u, tree.GetFromId(3)->GetUnignoredIndexInParent());
+  EXPECT_EQ(1u, tree.GetFromId(7)->GetUnignoredIndexInParent());
+
+  AXTreeUpdate update6;
+  update6.nodes.resize(1);
+  update6.nodes[0].id = 7;
+  update6.nodes[0].AddState(ax::mojom::State::kIgnored);
+
+  EXPECT_TRUE(tree.Unserialize(update6));
+
+  EXPECT_EQ(1u, tree.GetFromId(1)->GetUnignoredChildCount());
+  EXPECT_EQ(0u, tree.GetFromId(3)->GetUnignoredIndexInParent());
+
+  AXTreeUpdate update7 = update6;
+  update7.nodes.resize(2);
+  update7.nodes[0].id = 7;
+  update7.nodes[0].child_ids = {8};
+  update7.nodes[1].id = 8;
+
+  EXPECT_TRUE(tree.Unserialize(update7));
+
+  EXPECT_EQ(2u, tree.GetFromId(1)->GetUnignoredChildCount());
+  EXPECT_EQ(0u, tree.GetFromId(3)->GetUnignoredIndexInParent());
+}
+
 TEST(AXTreeTest, TestRecursionUnignoredChildCount) {
   AXTreeUpdate tree_update;
   tree_update.root_id = 1;
diff --git a/ui/base/cursor/cursor_loader_win.cc b/ui/base/cursor/cursor_loader_win.cc
index 6967a29a..3124e34 100644
--- a/ui/base/cursor/cursor_loader_win.cc
+++ b/ui/base/cursor/cursor_loader_win.cc
@@ -72,6 +72,10 @@
       return MAKEINTRESOURCE(IDC_ROWRESIZE);
     case CursorType::kMiddlePanning:
       return MAKEINTRESOURCE(IDC_PAN_MIDDLE);
+    case CursorType::kMiddlePanningVertical:
+      return MAKEINTRESOURCE(IDC_PAN_MIDDLE_VERTICAL);
+    case CursorType::kMiddlePanningHorizontal:
+      return MAKEINTRESOURCE(IDC_PAN_MIDDLE_HORIZONTAL);
     case CursorType::kEastPanning:
       return MAKEINTRESOURCE(IDC_PAN_EAST);
     case CursorType::kNorthPanning:
diff --git a/ui/base/cursor/cursor_loader_x11.cc b/ui/base/cursor/cursor_loader_x11.cc
index 3903be2..7148f5e 100644
--- a/ui/base/cursor/cursor_loader_x11.cc
+++ b/ui/base/cursor/cursor_loader_x11.cc
@@ -27,6 +27,10 @@
   switch (id) {
     case ui::CursorType::kMiddlePanning:
       return "all-scroll";
+    case ui::CursorType::kMiddlePanningVertical:
+      return "v-scroll";
+    case ui::CursorType::kMiddlePanningHorizontal:
+      return "h-scroll";
     case ui::CursorType::kEastPanning:
       return "e-resize";
     case ui::CursorType::kNorthPanning:
@@ -187,6 +191,8 @@
     { "wait",        nullptr,           XC_watch },
     { "cell",        nullptr,           XC_plus },
     { "all-scroll",  nullptr,           XC_fleur},
+    { "v-scroll",    "all-scroll",      XC_fleur},
+    { "h-scroll",    "all-scroll",      XC_fleur},
     { "crosshair",   nullptr,           XC_cross },
     { "text",        nullptr,           XC_xterm },
     { "not-allowed", "crossed_circle",  x11::None },
diff --git a/ui/base/cursor/cursor_type.h b/ui/base/cursor/cursor_type.h
index 3fad02b8..74a34a53 100644
--- a/ui/base/cursor/cursor_type.h
+++ b/ui/base/cursor/cursor_type.h
@@ -13,6 +13,7 @@
 
   // These cursors mirror WebKit cursors from WebCursorInfo, but are replicated
   // here so we don't introduce a WebKit dependency.
+  // TODO(crbug.com/969904): Merge this with WebCursorInfo
   kPointer = 1,
   kCross = 2,
   kHand = 3,
@@ -56,13 +57,15 @@
   kZoomOut = 41,
   kGrab = 42,
   kGrabbing = 43,
-  kCustom = 44,
+  kMiddlePanningVertical = 44,
+  kMiddlePanningHorizontal = 45,
+  kCustom = 46,
 
   // These additional drag and drop cursors are not listed in WebCursorInfo.
-  kDndNone = 45,
-  kDndMove = 46,
-  kDndCopy = 47,
-  kDndLink = 48,
+  kDndNone = 47,
+  kDndMove = 48,
+  kDndCopy = 49,
+  kDndLink = 50,
 };
 
 enum class CursorSize { kNormal, kLarge };
diff --git a/ui/base/idle/BUILD.gn b/ui/base/idle/BUILD.gn
index d9e3eea..530d02a 100644
--- a/ui/base/idle/BUILD.gn
+++ b/ui/base/idle/BUILD.gn
@@ -13,6 +13,13 @@
 
   defines = [ "IS_UI_BASE_IDLE_IMPL" ]
 
+  # All targets in this file are allowed to access any of the headers.
+  friend = [ ":*" ]
+
+  public = [
+    "idle.h",
+  ]
+
   deps = [
     "//base",
     "//ui/base",
@@ -26,6 +33,8 @@
     "idle.cc",
     "idle.h",
     "idle_chromeos.cc",
+    "idle_internal.cc",
+    "idle_internal.h",
     "idle_mac.mm",
     "idle_win.cc",
   ]
@@ -66,3 +75,17 @@
     ]
   }
 }
+
+static_library("test_support") {
+  testonly = true
+
+  sources = [
+    "scoped_set_idle_state.cc",
+    "scoped_set_idle_state.h",
+  ]
+
+  deps = [
+    ":idle",
+    "//base",
+  ]
+}
diff --git a/ui/base/idle/idle.cc b/ui/base/idle/idle.cc
index 0333fbc..3f0e34d 100644
--- a/ui/base/idle/idle.cc
+++ b/ui/base/idle/idle.cc
@@ -4,9 +4,14 @@
 
 #include "ui/base/idle/idle.h"
 
+#include "ui/base/idle/idle_internal.h"
+
 namespace ui {
 
 IdleState CalculateIdleState(int idle_threshold) {
+  if (IdleStateForTesting().has_value())
+    return IdleStateForTesting().value();
+
   if (CheckIdleStateIsLocked())
     return IDLE_STATE_LOCKED;
 
diff --git a/ui/base/idle/idle_android.cc b/ui/base/idle/idle_android.cc
index f21e03d..40190e8 100644
--- a/ui/base/idle/idle_android.cc
+++ b/ui/base/idle/idle_android.cc
@@ -9,6 +9,7 @@
 #include "base/logging.h"
 #include "base/memory/singleton.h"
 #include "jni/IdleDetector_jni.h"
+#include "ui/base/idle/idle_internal.h"
 
 using base::android::AttachCurrentThread;
 using base::android::ConvertJavaStringToUTF8;
@@ -71,6 +72,9 @@
 }
 
 bool CheckIdleStateIsLocked() {
+  if (IdleStateForTesting().has_value())
+    return IdleStateForTesting().value() == IDLE_STATE_LOCKED;
+
   return AndroidIdleMonitor::GetInstance()->CheckIdleStateIsLocked();
 }
 
diff --git a/ui/base/idle/idle_chromeos.cc b/ui/base/idle/idle_chromeos.cc
index a58a285..a0f4cde 100644
--- a/ui/base/idle/idle_chromeos.cc
+++ b/ui/base/idle/idle_chromeos.cc
@@ -6,6 +6,7 @@
 
 #include "base/time/time.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
+#include "ui/base/idle/idle_internal.h"
 #include "ui/base/user_activity/user_activity_detector.h"
 
 namespace ui {
@@ -17,6 +18,9 @@
 }
 
 bool CheckIdleStateIsLocked() {
+  if (IdleStateForTesting().has_value())
+    return IdleStateForTesting().value() == IDLE_STATE_LOCKED;
+
   return chromeos::SessionManagerClient::Get()->IsScreenLocked();
 }
 
diff --git a/ui/base/idle/idle_internal.cc b/ui/base/idle/idle_internal.cc
new file mode 100644
index 0000000..ac62882
--- /dev/null
+++ b/ui/base/idle/idle_internal.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 "ui/base/idle/idle_internal.h"
+
+#include "base/no_destructor.h"
+
+namespace ui {
+
+base::Optional<IdleState>& IdleStateForTesting() {
+  static base::NoDestructor<base::Optional<IdleState>> idle_state;
+  return *idle_state;
+}
+
+}  // namespace ui
diff --git a/ui/base/idle/idle_internal.h b/ui/base/idle/idle_internal.h
new file mode 100644
index 0000000..01e9598
--- /dev/null
+++ b/ui/base/idle/idle_internal.h
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_IDLE_IDLE_INTERNAL_H_
+#define UI_BASE_IDLE_IDLE_INTERNAL_H_
+
+#include "base/component_export.h"
+#include "base/optional.h"
+#include "ui/base/idle/idle.h"
+
+namespace ui {
+
+// An optional idle state set by tests via a ScopedSetIdleState to override the
+// actual idle state of the system.
+COMPONENT_EXPORT(UI_BASE_IDLE) base::Optional<IdleState>& IdleStateForTesting();
+
+}  // namespace ui
+
+#endif  // UI_BASE_IDLE_IDLE_INTERNAL_H_
diff --git a/ui/base/idle/idle_linux.cc b/ui/base/idle/idle_linux.cc
index 7058b87..ec0ed5c 100644
--- a/ui/base/idle/idle_linux.cc
+++ b/ui/base/idle/idle_linux.cc
@@ -4,6 +4,8 @@
 
 #include "ui/base/idle/idle.h"
 
+#include "ui/base/idle/idle_internal.h"
+
 #if defined(USE_X11)
 #include "ui/base/idle/idle_query_x11.h"
 #include "ui/base/idle/screensaver_window_finder_x11.h"
@@ -21,6 +23,9 @@
 }
 
 bool CheckIdleStateIsLocked() {
+  if (IdleStateForTesting().has_value())
+    return IdleStateForTesting().value() == IDLE_STATE_LOCKED;
+
 #if defined(USE_X11)
   // Usually the screensaver is used to lock the screen.
   return ScreensaverWindowFinder::ScreensaverWindowExists();
diff --git a/ui/base/idle/idle_mac.mm b/ui/base/idle/idle_mac.mm
index dc35550..5453586 100644
--- a/ui/base/idle/idle_mac.mm
+++ b/ui/base/idle/idle_mac.mm
@@ -7,6 +7,8 @@
 #include <ApplicationServices/ApplicationServices.h>
 #import <Cocoa/Cocoa.h>
 
+#include "ui/base/idle/idle_internal.h"
+
 @interface MacScreenMonitor : NSObject {
  @private
   BOOL screensaverRunning_;
@@ -92,6 +94,9 @@
 }
 
 bool CheckIdleStateIsLocked() {
+  if (IdleStateForTesting().has_value())
+    return IdleStateForTesting().value() == IDLE_STATE_LOCKED;
+
   return [g_screenMonitor isScreensaverRunning] ||
       [g_screenMonitor isScreenLocked];
 }
diff --git a/ui/base/idle/idle_win.cc b/ui/base/idle/idle_win.cc
index 4946406..2c1d3a7 100644
--- a/ui/base/idle/idle_win.cc
+++ b/ui/base/idle/idle_win.cc
@@ -7,6 +7,7 @@
 #include <limits.h>
 #include <windows.h>
 
+#include "ui/base/idle/idle_internal.h"
 #include "ui/base/win/lock_state.h"
 
 namespace ui {
@@ -51,6 +52,9 @@
 }
 
 bool CheckIdleStateIsLocked() {
+  if (IdleStateForTesting().has_value())
+    return IdleStateForTesting().value() == IDLE_STATE_LOCKED;
+
   return ui::IsWorkstationLocked() || IsScreensaverRunning();
 }
 
diff --git a/ui/base/idle/scoped_set_idle_state.cc b/ui/base/idle/scoped_set_idle_state.cc
new file mode 100644
index 0000000..93baa00
--- /dev/null
+++ b/ui/base/idle/scoped_set_idle_state.cc
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/idle/scoped_set_idle_state.h"
+
+#include "ui/base/idle/idle_internal.h"
+
+namespace ui {
+
+ScopedSetIdleState::ScopedSetIdleState(IdleState state)
+    : previous_state_(IdleStateForTesting()) {
+  IdleStateForTesting() = state;
+}
+
+ScopedSetIdleState::~ScopedSetIdleState() {
+  IdleStateForTesting() = previous_state_;
+}
+
+}  // namespace ui
diff --git a/ui/base/idle/scoped_set_idle_state.h b/ui/base/idle/scoped_set_idle_state.h
new file mode 100644
index 0000000..fa3e617
--- /dev/null
+++ b/ui/base/idle/scoped_set_idle_state.h
@@ -0,0 +1,26 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_IDLE_SCOPED_SET_IDLE_STATE_H_
+#define UI_BASE_IDLE_SCOPED_SET_IDLE_STATE_H_
+
+#include "base/optional.h"
+#include "ui/base/idle/idle.h"
+
+namespace ui {
+
+class ScopedSetIdleState {
+ public:
+  explicit ScopedSetIdleState(IdleState state);
+  ~ScopedSetIdleState();
+
+ private:
+  base::Optional<IdleState> previous_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedSetIdleState);
+};
+
+}  // namespace ui
+
+#endif  // UI_BASE_IDLE_SCOPED_SET_IDLE_STATE_H_
\ No newline at end of file
diff --git a/ui/base/models/tree_model.h b/ui/base/models/tree_model.h
index 73ae4f0..815cebbd 100644
--- a/ui/base/models/tree_model.h
+++ b/ui/base/models/tree_model.h
@@ -39,14 +39,14 @@
   // Notification that nodes were added to the specified parent.
   virtual void TreeNodesAdded(TreeModel* model,
                               TreeModelNode* parent,
-                              int start,
-                              int count) = 0;
+                              size_t start,
+                              size_t count) = 0;
 
   // Notification that nodes were removed from the specified parent.
   virtual void TreeNodesRemoved(TreeModel* model,
                                 TreeModelNode* parent,
-                                int start,
-                                int count) = 0;
+                                size_t start,
+                                size_t count) = 0;
 
   // Notification that the contents of a node has changed.
   virtual void TreeNodeChanged(TreeModel* model, TreeModelNode* node) = 0;
diff --git a/ui/base/models/tree_node_model.h b/ui/base/models/tree_node_model.h
index d69e90f..90c7b31f 100644
--- a/ui/base/models/tree_node_model.h
+++ b/ui/base/models/tree_node_model.h
@@ -87,10 +87,9 @@
 
   // Adds |node| as a child of this node, at |index|. Returns a raw pointer to
   // the node.
-  NodeType* Add(std::unique_ptr<NodeType> node, int index) {
+  NodeType* Add(std::unique_ptr<NodeType> node, size_t index) {
     DCHECK(node);
-    DCHECK_GE(index, 0);
-    DCHECK_LE(size_t{index}, children_.size());
+    DCHECK_LE(index, children_.size());
     DCHECK(!node->parent_);
     node->parent_ = static_cast<NodeType*>(this);
     NodeType* node_ptr = node.get();
@@ -100,13 +99,12 @@
 
   // Shorthand for "add at end".
   NodeType* Add(std::unique_ptr<NodeType> node) {
-    return Add(std::move(node), child_count());
+    return Add(std::move(node), children_.size());
   }
 
   // Removes the node at the given index. Returns the removed node.
-  std::unique_ptr<NodeType> Remove(int index) {
-    DCHECK_GE(index, 0);
-    DCHECK_LT(size_t{index}, children_.size());
+  std::unique_ptr<NodeType> Remove(size_t index) {
+    DCHECK_LT(index, children_.size());
     children_[index]->parent_ = nullptr;
     std::unique_ptr<NodeType> ptr = std::move(children_[index]);
     children_.erase(children_.begin() + index);
@@ -235,8 +233,11 @@
     return static_cast<const NodeType*>(model_node);
   }
 
-  NodeType* Add(NodeType* parent, std::unique_ptr<NodeType> node, int index) {
-    DCHECK(parent && node);
+  NodeType* Add(NodeType* parent,
+                std::unique_ptr<NodeType> node,
+                size_t index) {
+    DCHECK(parent);
+    DCHECK(node);
     NodeType* node_ptr = parent->Add(std::move(node), index);
     NotifyObserverTreeNodesAdded(parent, index, 1);
     return node_ptr;
@@ -244,10 +245,10 @@
 
   // Shorthand for "add at end".
   NodeType* Add(NodeType* parent, std::unique_ptr<NodeType> node) {
-    return Add(parent, std::move(node), parent->child_count());
+    return Add(parent, std::move(node), parent->children().size());
   }
 
-  std::unique_ptr<NodeType> Remove(NodeType* parent, int index) {
+  std::unique_ptr<NodeType> Remove(NodeType* parent, size_t index) {
     DCHECK(parent);
     std::unique_ptr<NodeType> owned_node = parent->Remove(index);
     NotifyObserverTreeNodesRemoved(parent, index, 1);
@@ -256,15 +257,19 @@
 
   std::unique_ptr<NodeType> Remove(NodeType* parent, NodeType* node) {
     DCHECK(parent);
-    return Remove(parent, parent->GetIndexOf(node));
+    return Remove(parent, size_t{parent->GetIndexOf(node)});
   }
 
-  void NotifyObserverTreeNodesAdded(NodeType* parent, int start, int count) {
+  void NotifyObserverTreeNodesAdded(NodeType* parent,
+                                    size_t start,
+                                    size_t count) {
     for (TreeModelObserver& observer : observer_list_)
       observer.TreeNodesAdded(this, parent, start, count);
   }
 
-  void NotifyObserverTreeNodesRemoved(NodeType* parent, int start, int count) {
+  void NotifyObserverTreeNodesRemoved(NodeType* parent,
+                                      size_t start,
+                                      size_t count) {
     for (TreeModelObserver& observer : observer_list_)
       observer.TreeNodesRemoved(this, parent, start, count);
   }
diff --git a/ui/base/models/tree_node_model_unittest.cc b/ui/base/models/tree_node_model_unittest.cc
index 0de74e9..351096f 100644
--- a/ui/base/models/tree_node_model_unittest.cc
+++ b/ui/base/models/tree_node_model_unittest.cc
@@ -19,11 +19,8 @@
 
 class TreeNodeModelTest : public testing::Test, public TreeModelObserver {
  public:
-  TreeNodeModelTest()
-      : added_count_(0),
-        removed_count_(0),
-        changed_count_(0) {}
-  ~TreeNodeModelTest() override {}
+  TreeNodeModelTest() = default;
+  ~TreeNodeModelTest() override = default;
 
  protected:
   std::string GetObserverCountStateAndClear() {
@@ -37,23 +34,23 @@
   // Overridden from TreeModelObserver:
   void TreeNodesAdded(TreeModel* model,
                       TreeModelNode* parent,
-                      int start,
-                      int count) override {
+                      size_t start,
+                      size_t count) override {
     added_count_++;
   }
   void TreeNodesRemoved(TreeModel* model,
                         TreeModelNode* parent,
-                        int start,
-                        int count) override {
+                        size_t start,
+                        size_t count) override {
     removed_count_++;
   }
   void TreeNodeChanged(TreeModel* model, TreeModelNode* node) override {
     changed_count_++;
   }
 
-  int added_count_;
-  int removed_count_;
-  int changed_count_;
+  int added_count_ = 0;
+  int removed_count_ = 0;
+  int changed_count_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(TreeNodeModelTest);
 };
@@ -77,7 +74,7 @@
 
   EXPECT_EQ("added=1 removed=0 changed=0", GetObserverCountStateAndClear());
 
-  for (int i = 0; i < 2; ++i)
+  for (size_t i = 0; i < 2; ++i)
     child1->Add(std::make_unique<TestNode>(), i);
 
   TestNode* child2 = model.Add(root, std::make_unique<TestNode>(), 1);
@@ -129,7 +126,7 @@
   TestNode* foo = child1->Add(std::make_unique<TestNode>(), 0);
 
   // Add some nodes to |foo|.
-  for (int i = 0; i < 3; ++i)
+  for (size_t i = 0; i < 3; ++i)
     foo->Add(std::make_unique<TestNode>(), i);  // bar[n]
 
   EXPECT_EQ(3, root.child_count());
diff --git a/ui/base/mojo/cursor.mojom b/ui/base/mojo/cursor.mojom
index 6e4ea64..ad16723 100644
--- a/ui/base/mojo/cursor.mojom
+++ b/ui/base/mojo/cursor.mojom
@@ -35,6 +35,8 @@
   kColumnResize,
   kRowResize,
   kMiddlePanning,
+  kMiddlePanningVertical,
+  kMiddlePanningHorizontal,
   kEastPanning,
   kNorthPanning,
   kNorthEastPanning,
diff --git a/ui/base/mojo/cursor_struct_traits.cc b/ui/base/mojo/cursor_struct_traits.cc
index 8acf377..075db62 100644
--- a/ui/base/mojo/cursor_struct_traits.cc
+++ b/ui/base/mojo/cursor_struct_traits.cc
@@ -60,6 +60,10 @@
       return ui::mojom::CursorType::kRowResize;
     case ui::CursorType::kMiddlePanning:
       return ui::mojom::CursorType::kMiddlePanning;
+    case ui::CursorType::kMiddlePanningVertical:
+      return ui::mojom::CursorType::kMiddlePanningVertical;
+    case ui::CursorType::kMiddlePanningHorizontal:
+      return ui::mojom::CursorType::kMiddlePanningHorizontal;
     case ui::CursorType::kEastPanning:
       return ui::mojom::CursorType::kEastPanning;
     case ui::CursorType::kNorthPanning:
@@ -190,6 +194,12 @@
     case ui::mojom::CursorType::kMiddlePanning:
       *out = ui::CursorType::kMiddlePanning;
       return true;
+    case ui::mojom::CursorType::kMiddlePanningVertical:
+      *out = ui::CursorType::kMiddlePanningVertical;
+      return true;
+    case ui::mojom::CursorType::kMiddlePanningHorizontal:
+      *out = ui::CursorType::kMiddlePanningHorizontal;
+      return true;
     case ui::mojom::CursorType::kEastPanning:
       *out = ui::CursorType::kEastPanning;
       return true;
diff --git a/ui/base/win/system_media_controls/mock_system_media_controls_service.h b/ui/base/win/system_media_controls/mock_system_media_controls_service.h
index 125a1ee..817b51f 100644
--- a/ui/base/win/system_media_controls/mock_system_media_controls_service.h
+++ b/ui/base/win/system_media_controls/mock_system_media_controls_service.h
@@ -25,6 +25,7 @@
   MOCK_METHOD1(AddObserver, void(SystemMediaControlsServiceObserver* observer));
   MOCK_METHOD1(RemoveObserver,
                void(SystemMediaControlsServiceObserver* observer));
+  MOCK_METHOD1(SetEnabled, void(bool enabled));
   MOCK_METHOD1(SetIsNextEnabled, void(bool value));
   MOCK_METHOD1(SetIsPreviousEnabled, void(bool value));
   MOCK_METHOD1(SetIsPlayEnabled, void(bool value));
diff --git a/ui/base/win/system_media_controls/system_media_controls_service.h b/ui/base/win/system_media_controls/system_media_controls_service.h
index 55e6850..8b5eba6 100644
--- a/ui/base/win/system_media_controls/system_media_controls_service.h
+++ b/ui/base/win/system_media_controls/system_media_controls_service.h
@@ -31,6 +31,9 @@
   virtual void AddObserver(SystemMediaControlsServiceObserver* observer) = 0;
   virtual void RemoveObserver(SystemMediaControlsServiceObserver* observer) = 0;
 
+  // Enables/disables the service.
+  virtual void SetEnabled(bool enabled) = 0;
+
   // TODO(steimel): Add other controls.
   // Enable or disable specific controls.
   virtual void SetIsNextEnabled(bool value) = 0;
diff --git a/ui/base/win/system_media_controls/system_media_controls_service_impl.cc b/ui/base/win/system_media_controls/system_media_controls_service_impl.cc
index 76fa9f4..7862e3c 100644
--- a/ui/base/win/system_media_controls/system_media_controls_service_impl.cc
+++ b/ui/base/win/system_media_controls/system_media_controls_service_impl.cc
@@ -118,6 +118,12 @@
   observers_.RemoveObserver(observer);
 }
 
+void SystemMediaControlsServiceImpl::SetEnabled(bool enabled) {
+  DCHECK(initialized_);
+  HRESULT hr = system_media_controls_->put_IsEnabled(enabled);
+  DCHECK(SUCCEEDED(hr));
+}
+
 void SystemMediaControlsServiceImpl::SetIsNextEnabled(bool value) {
   DCHECK(initialized_);
   HRESULT hr = system_media_controls_->put_IsNextEnabled(value);
diff --git a/ui/base/win/system_media_controls/system_media_controls_service_impl.h b/ui/base/win/system_media_controls/system_media_controls_service_impl.h
index 57e71e1d..025ab92 100644
--- a/ui/base/win/system_media_controls/system_media_controls_service_impl.h
+++ b/ui/base/win/system_media_controls/system_media_controls_service_impl.h
@@ -37,6 +37,7 @@
   // SystemMediaControlsService implementation.
   void AddObserver(SystemMediaControlsServiceObserver* observer) override;
   void RemoveObserver(SystemMediaControlsServiceObserver* observer) override;
+  void SetEnabled(bool enabled) override;
   void SetIsNextEnabled(bool value) override;
   void SetIsPreviousEnabled(bool value) override;
   void SetIsPlayEnabled(bool value) override;
diff --git a/ui/chromeos/file_manager_strings.grdp b/ui/chromeos/file_manager_strings.grdp
index 6babc24c..56d1c23 100644
--- a/ui/chromeos/file_manager_strings.grdp
+++ b/ui/chromeos/file_manager_strings.grdp
@@ -113,6 +113,28 @@
   <message name="IDS_FILE_BROWSER_COLUMN_SORTED_DESC_MESSAGE" desc="Message read by Chromevox/screenreader after sorting a column in the File list in descending order.">
     File list sorted by <ph name="COLUMN_NAME">$1<ex>date modified</ex></ph> in descending order.
   </message>
+
+  <!-- File selection a11y -->
+  <message name="IDS_FILE_BROWSER_SELECTION_ADD_SINGLE_ENTRY" desc="Message spoken by Chromevox/screenreader when adding a single entry (file or folder) to the existing selection.">
+    Added <ph name="ENTRY_NAME">$1<ex>file.txt</ex></ph> to selection.
+  </message>
+  <message name="IDS_FILE_BROWSER_SELECTION_REMOVE_SINGLE_ENTRY" desc="Message spoken by Chromevox/screenreader when remove a single entry (file or folder) from the existing selection.">
+    Removed <ph name="ENTRY_NAME">$1<ex>file.txt</ex></ph> from selection.
+  </message>
+  <message name="IDS_FILE_BROWSER_SELECTION_SINGLE_ENTRY" desc="Message spoken by Chromevox/screenreader when selection only one entry (without multiple selection).">
+    Selected <ph name="ENTRY_NAME">$1<ex>file.txt</ex></ph>.
+  </message>
+  <message name="IDS_FILE_BROWSER_SELECTION_ADD_RANGE" desc="Message spoken by Chromevox/screenreader when user selects a range of entries.">
+    Selected a range of <ph name="ENTRY_COUNT">$1<ex>10</ex></ph> entries from <ph name="FROM_ENTRY_NAME">$2<ex>file.txt</ex></ph> to <ph name="TO_ENTRY_NAME">$3<ex>another_file.txt</ex></ph>.
+  </message>
+
+  <message name="IDS_FILE_BROWSER_SELECTION_CANCELLATION" desc="Message spoken by Chromevox/screenreader user cancels a selection via 'Cancel selection' button or hitting Esc key.">
+    Removed all entries from selection.
+  </message>
+  <message name="IDS_FILE_BROWSER_SELECTION_ALL_ENTRIES" desc="Message spoken by Chromevox/screenreader user selects all entries e.g.: using Ctlr+A.">
+    Selected all entries.
+  </message>
+
   <message name="IDS_FILE_BROWSER_SIZE_BYTES" desc="Size in bytes.">
     <ph name="NUMBER_OF_BYTES">$1<ex>42</ex></ph> bytes
   </message>
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_ADD_RANGE.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_ADD_RANGE.png.sha1
new file mode 100644
index 0000000..45139ca
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_ADD_RANGE.png.sha1
@@ -0,0 +1 @@
+b6ca789018038780c9a28d2f478afb8583816a2a
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_ADD_SINGLE_ENTRY.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_ADD_SINGLE_ENTRY.png.sha1
new file mode 100644
index 0000000..aac8a92
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_ADD_SINGLE_ENTRY.png.sha1
@@ -0,0 +1 @@
+589945b82c1f270ec8c2ba102be0d49f6e424b5c
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_ALL_ENTRIES.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_ALL_ENTRIES.png.sha1
new file mode 100644
index 0000000..dd40f3fe
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_ALL_ENTRIES.png.sha1
@@ -0,0 +1 @@
+a7c5c7ab0d8b043a7288d17a666146c73885f2f9
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_CANCELLATION.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_CANCELLATION.png.sha1
new file mode 100644
index 0000000..70843349
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_CANCELLATION.png.sha1
@@ -0,0 +1 @@
+6ae8c9af482130d9fa23bb66e9d91c9566cfdbb9
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_REMOVE_SINGLE_ENTRY.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_REMOVE_SINGLE_ENTRY.png.sha1
new file mode 100644
index 0000000..4f2f20f7
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_REMOVE_SINGLE_ENTRY.png.sha1
@@ -0,0 +1 @@
+cc589193dd76253dad91587b34bd8aa854877d68
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_SINGLE_ENTRY.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_SINGLE_ENTRY.png.sha1
new file mode 100644
index 0000000..8a36dce
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SELECTION_SINGLE_ENTRY.png.sha1
@@ -0,0 +1 @@
+b85cccc457069752f37732cb7caf4d194c8ba26c
\ No newline at end of file
diff --git a/ui/chromeos/translations/ui_chromeos_strings_ja.xtb b/ui/chromeos/translations/ui_chromeos_strings_ja.xtb
index 9d25824..3c33828 100644
--- a/ui/chromeos/translations/ui_chromeos_strings_ja.xtb
+++ b/ui/chromeos/translations/ui_chromeos_strings_ja.xtb
@@ -313,7 +313,7 @@
 <translation id="4839847978919684242"><ph name="SELCTED_FILES_COUNT" /> 個のアイテムを選択</translation>
 <translation id="4850886885716139402">表示</translation>
 <translation id="485316830061041779">ドイツ語</translation>
-<translation id="4867079195717347957">クリックすると、降順に列を並べ替えます。</translation>
+<translation id="4867079195717347957">クリックすると、列が降順で並べ替えられます。</translation>
 <translation id="4873265419374180291"><ph name="NUMBER_OF_BYTES" /> バイト</translation>
 <translation id="4880214202172289027">音量スライダー</translation>
 <translation id="4880520557730313061">自動修正</translation>
@@ -354,7 +354,7 @@
 <translation id="5167131699331641907">オランダ語キーボード</translation>
 <translation id="5170477580121653719">Google ドライブの残容量: <ph name="SPACE_AVAILABLE" /></translation>
 <translation id="5177526793333269655">サムネイル ビュー</translation>
-<translation id="5194713942430106590">クリックすると、昇順に列を並べ替えます。</translation>
+<translation id="5194713942430106590">クリックすると、列が昇順で並べ替えられます。</translation>
 <translation id="5218183485292899140">フランス語(スイス)</translation>
 <translation id="5234764350956374838">閉じる</translation>
 <translation id="5241298539944515331">ベトナム語キーボード(VIQR)</translation>
diff --git a/ui/file_manager/externs/metadata_worker_window.js b/ui/file_manager/externs/metadata_worker_window.js
index 117e158b..fa90527 100644
--- a/ui/file_manager/externs/metadata_worker_window.js
+++ b/ui/file_manager/externs/metadata_worker_window.js
@@ -5,36 +5,38 @@
 /**
  * @interface
  */
-function MetadataParserLogger() {}
+class MetadataParserLogger {
+  constructor() {
+    /**
+     * Verbose logging for the dispatcher.
+     * Individual parsers also take this as their default verbosity setting.
+     * @public {boolean}
+     */
+    this.verbose;
+  }
 
-/**
- * Verbose logging for the dispatcher.
- *
- * Individual parsers also take this as their default verbosity setting.
- */
-MetadataParserLogger.prototype.verbose;
+  /**
+   * Indicate to the caller that an operation has failed.
+   *
+   * No other messages relating to the failed operation should be sent.
+   * @param {...(Object|string)} var_args Arguments.
+   */
+  error(var_args) {}
 
-/**
- * Indicate to the caller that an operation has failed.
- *
- * No other messages relating to the failed operation should be sent.
- * @param {...(Object|string)} var_args Arguments.
- */
-MetadataParserLogger.prototype.error = function(var_args) {};
+  /**
+   * Send a log message to the caller.
+   *
+   * Callers must not parse log messages for control flow.
+   * @param {...(Object|string)} var_args Arguments.
+   */
+  log(var_args) {}
 
-/**
- * Send a log message to the caller.
- *
- * Callers must not parse log messages for control flow.
- * @param {...(Object|string)} var_args Arguments.
- */
-MetadataParserLogger.prototype.log = function(var_args) {};
-
-/**
- * Send a log message to the caller only if this.verbose is true.
- * @param {...(Object|string)} var_args Arguments.
- */
-MetadataParserLogger.prototype.vlog = function(var_args) {};
+  /**
+   * Send a log message to the caller only if this.verbose is true.
+   * @param {...(Object|string)} var_args Arguments.
+   */
+  vlog(var_args) {}
+}
 
 /**
  * @param {function(new:MetadataParser, !MetadataParserLogger)} parserClass
@@ -47,5 +49,5 @@
  * @param {function(!Entry)} successCallback
  * @param {function(!FileError)=} opt_errorCallback
  */
-var webkitResolveLocalFileSystemURL =
-    function(url, successCallback, opt_errorCallback) {};
+var webkitResolveLocalFileSystemURL = function(
+    url, successCallback, opt_errorCallback) {};
diff --git a/ui/file_manager/file_manager/background/js/device_handler.js b/ui/file_manager/file_manager/background/js/device_handler.js
index 1f610a99..c85fc864 100644
--- a/ui/file_manager/file_manager/background/js/device_handler.js
+++ b/ui/file_manager/file_manager/background/js/device_handler.js
@@ -254,7 +254,7 @@
                   /** @type {string} */ (metadata.devicePath));
             } else {
               chrome.fileManagerPrivate.getPreferences(pref => {
-                if (!pref.arcEnabled) {
+                if (!pref.arcEnabled || !util.isArcUsbStorageUIEnabled()) {
                   DeviceHandler.Notification.DEVICE_NAVIGATION.show(
                       /** @type {string} */ (metadata.devicePath));
                 } else if (pref.arcRemovableMediaAccessEnabled) {
diff --git a/ui/file_manager/file_manager/background/js/device_handler_unittest.js b/ui/file_manager/file_manager/background/js/device_handler_unittest.js
index ce3d64a..1659752 100644
--- a/ui/file_manager/file_manager/background/js/device_handler_unittest.js
+++ b/ui/file_manager/file_manager/background/js/device_handler_unittest.js
@@ -23,6 +23,12 @@
   window.loadTimeData.getString = id => {
     return window.loadTimeData.data_[id] || id;
   };
+  window.loadTimeData.getBoolean = id => {
+    return id === 'ARC_USB_STORAGE_UI_ENABLED' ? true : false;
+  };
+  window.loadTimeData.valueExists = id => {
+    return id === 'ARC_USB_STORAGE_UI_ENABLED';
+  };
 
   setupChromeApis();
   volumeManager = new MockVolumeManager();
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js
index ce7afe1..f8709a15 100644
--- a/ui/file_manager/file_manager/common/js/util.js
+++ b/ui/file_manager/file_manager/common/js/util.js
@@ -1386,8 +1386,8 @@
 };
 
 /**
- * Examines whether the new feedback panel mode is enabled.
- * @return {boolean} True if the new feedback panel UI mode is enabled.
+ * Examines whether the feedback panel mode is enabled.
+ * @return {boolean} True if the feedback panel UI mode is enabled.
  */
 util.isFeedbackPanelEnabled = () => {
   return loadTimeData.getBoolean('FEEDBACK_PANEL_ENABLED');
@@ -1553,6 +1553,12 @@
 };
 
 /** @return {boolean} */
+util.isArcUsbStorageUIEnabled = () => {
+  return loadTimeData.valueExists('ARC_USB_STORAGE_UI_ENABLED') &&
+      loadTimeData.getBoolean('ARC_USB_STORAGE_UI_ENABLED');
+};
+
+/** @return {boolean} */
 util.isMyFilesVolumeEnabled = () => {
   return loadTimeData.valueExists('MY_FILES_VOLUME_ENABLED') &&
       loadTimeData.getBoolean('MY_FILES_VOLUME_ENABLED');
diff --git a/ui/file_manager/file_manager/foreground/elements/activity_complete.js b/ui/file_manager/file_manager/foreground/elements/activity_complete.js
new file mode 100644
index 0000000..240229f9
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/elements/activity_complete.js
@@ -0,0 +1,103 @@
+// 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.
+
+/**
+ * Activity complete indicator custom element for use in PanelEntry(s).
+ */
+class ActivityComplete extends HTMLElement {
+  constructor() {
+    super();
+    const host = document.createElement('template');
+    host.innerHTML = this.constructor.template_;
+    this.attachShadow({mode: 'open'}).appendChild(host.content.cloneNode(true));
+  }
+
+  /**
+   * Static getter for the custom element template.
+   * @return {string}
+   * @private
+   */
+  static get template_() {
+    return `<style>
+                    .complete {
+                        height: 36px;
+                        width: 36px;
+                        stroke-width: 3px;
+                        fill: none;
+                    }
+                </style>
+                <div class='complete'>
+                    <svg xmlns='http://www.w3.org/2000/svg'
+                        viewBox='0 0 36 36'>
+                        <path id='success' display='inherit'
+                            d='M4 20l9 9l22 -22' stroke='#34A853'/>
+                        <g id='failure' display='none' stroke='#EA4335'>
+                            <circle cx='18' cy='18' r='14'/>
+                            <path d='M18 10l0 10M18 23l0 3'/>
+                        </g>
+                    </svg>
+                </div>`;
+  }
+
+  /**
+   * Registers this instance to listen to these attribute changes.
+   * @private
+   * @return {Array<String>}
+   */
+  static get observedAttributes() {
+    return ['status'];
+  }
+
+  /**
+   * Callback triggered by the browser when our attribute values change.
+   * @param {string} name Attribute that's changed.
+   * @param {string} oldValue Old value of the attribute.
+   * @param {string} newValue New value of the attribute.
+   * @private
+   */
+  attributeChangedCallback(name, oldValue, newValue) {
+    if (name === 'status') {
+      if (oldValue != newValue) {
+        const success = this.shadowRoot.querySelector('#success');
+        const failure = this.shadowRoot.querySelector('#failure');
+        switch (newValue) {
+          case 'success':
+            success.setAttribute('display', 'inherit');
+            failure.setAttribute('display', 'none');
+            break;
+
+          case 'failure':
+            failure.setAttribute('display', 'inherit');
+            success.setAttribute('display', 'none');
+            break;
+
+          default:
+            assertNotReached();
+            break;
+        }
+      }
+    }
+  }
+
+  /**
+   * Getter for the current state of the progress indication.
+   * @return {string}
+   * @public
+   */
+  get status() {
+    return this.getAttribute('status');
+  }
+
+  /**
+   * Setter to set the success/failure indication.
+   * @param {string} status Status value being set.
+   * @public
+   */
+  set status(status) {
+    // Reflect the status property into the attribute.
+    this.setAttribute('status', status);
+  }
+}
+
+window.customElements.define('xf-activity-complete', ActivityComplete);
diff --git a/ui/file_manager/file_manager/foreground/elements/display_panel.js b/ui/file_manager/file_manager/foreground/elements/display_panel.js
index 47f1dd5..6f676620 100644
--- a/ui/file_manager/file_manager/foreground/elements/display_panel.js
+++ b/ui/file_manager/file_manager/foreground/elements/display_panel.js
@@ -43,20 +43,20 @@
    */
   static get template_() {
     return `<style>
-                    #container {
-                        align-items: center;
-                        background-color: #FFF;
-                        box-shadow: -2px -1px rgba(60, 64, 67, 0.15),
-                                    -1px 2px rgba(60, 64, 67, 0.3),
-                                    2px 0px rgba(60, 64, 67, 0.15);
-                        border-radius: 4px;
-                        display: flex;
-                        flex-direction: column;
-                        max-width: 400px;
-                        z-index: 100;
-                    }
-                </style>
-                <div id="container"></div>`;
+              #container {
+                  align-items: stretch;
+                  background-color: #FFF;
+                  box-shadow: -2px -1px rgba(60, 64, 67, 0.15),
+                              -1px 2px rgba(60, 64, 67, 0.3),
+                              2px 0px rgba(60, 64, 67, 0.15);
+                  border-radius: 4px;
+                  display: flex;
+                  flex-direction: column;
+                  max-width: min-content;
+                  z-index: 100;
+              }
+            </style>
+            <div id="container"></div>`;
   }
 
   /**
diff --git a/ui/file_manager/file_manager/foreground/elements/display_panel_test.html b/ui/file_manager/file_manager/foreground/elements/display_panel_test.html
index 03a491e..12de435 100644
--- a/ui/file_manager/file_manager/foreground/elements/display_panel_test.html
+++ b/ui/file_manager/file_manager/foreground/elements/display_panel_test.html
@@ -7,8 +7,11 @@
 <html>
     <head>
         <title>Display Panel test harness</title>
-        <link rel="stylesheet" href="files_visual_signals.css">
-        <script type="module" src="display_panel_test.mjs"></script>
+        <link rel="stylesheet" href="panel/files_visual_signals.css">
+        <script src="panel/circular_progress.js"></script>
+        <script src="panel/activity_complete.js"></script>
+        <script src="panel/panel_item.js"></script>
+        <script src="panel/display_panel.js"></script>
     </head>
     <body>
         <xf-display-panel id="main-panel">
diff --git a/ui/file_manager/file_manager/foreground/elements/display_panel_test.mjs b/ui/file_manager/file_manager/foreground/elements/display_panel_test.mjs
deleted file mode 100644
index 70c5bef8..0000000
--- a/ui/file_manager/file_manager/foreground/elements/display_panel_test.mjs
+++ /dev/null
@@ -1,5 +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.
-
-import {DisplayPanel} from './display_panel.mjs';
diff --git a/ui/file_manager/file_manager/foreground/elements/files_visual_signals.css b/ui/file_manager/file_manager/foreground/elements/files_visual_signals.css
index 33bac764..88d51ed 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_visual_signals.css
+++ b/ui/file_manager/file_manager/foreground/elements/files_visual_signals.css
@@ -26,6 +26,7 @@
     max-width: 216px;
     overflow: hidden;
     text-overflow: ellipsis;
+    white-space: nowrap;
 }
 
 .xf-panel-label-text {
@@ -41,13 +42,26 @@
 }
 
 .xf-padder-16 {
-    min-width: 16px;
+    width: 16px;
 }
 
 .xf-padder-24 {
-    min-width: 24px;
+    flex-grow: 16;
+    width: 24px;
 }
 
 xf-circular-progress {
     padding: 16px;
 }
+
+xf-activity-complete {
+    padding: 16px;
+}
+
+.xf-success {
+    color: rgb(52, 168, 83);
+}
+
+.xf-failure {
+    color: rgb(234, 67, 53);
+}
diff --git a/ui/file_manager/file_manager/foreground/elements/panel_item.js b/ui/file_manager/file_manager/foreground/elements/panel_item.js
index 304a993..bb0253a 100644
--- a/ui/file_manager/file_manager/foreground/elements/panel_item.js
+++ b/ui/file_manager/file_manager/foreground/elements/panel_item.js
@@ -26,9 +26,8 @@
                 <div class='xf-panel-item'>
                     <div class='xf-panel-text'>
                         <span class='xf-panel-label-text'>
-                                Copying File_With_a_long_name.jpg</span>
-                        <span class='xf-panel-secondary-text'>
-                                To SubFolder A1</span>
+                                Placeholder text</span>
+                        <br/>
                     </div>
                     <div class='xf-padder-24'></div>
                     <button class='xf-button' src='pause-icon.png'
@@ -38,6 +37,7 @@
                     <button class='xf-button' src='cancel-icon.png'
                             tabindex='-1'>
                     </button>
+                    <div class='xf-padder-16'></div>
                 </div>`;
   }
 
@@ -46,7 +46,9 @@
    * @private
    */
   static get observedAttributes() {
-    return ['indicator', 'progress'];
+    return [
+      'indicator', 'progress', 'status', 'primary-text', 'secondary-text'
+    ];
   }
 
   /**
@@ -57,19 +59,43 @@
    * @private
    */
   attributeChangedCallback(name, oldValue, newValue) {
+    /** @type {HTMLElement} */
     let indicator;
+    /** @type {HTMLSpanElement} */
+    let textNode;
+    if (oldValue === newValue) {
+      return;
+    }
     switch (name) {
       case 'indicator':
-        if (oldValue !== newValue) {
-          if (newValue === 'progress' || newValue === 'largeprogress') {
+        // Get rid of any existing indicator.
+        const oldIndicator = this.shadowRoot.querySelector('#indicator');
+        if (oldIndicator) {
+          oldIndicator.remove();
+        }
+        switch (newValue) {
+          default:  // Always set something so the panel doesn't shrink.
+          case 'progress':
+          case 'largeprogress':
             indicator = document.createElement('xf-circular-progress');
-            indicator.setAttribute('id', 'indicator');
             if (newValue === 'largeprogress') {
               indicator.setAttribute('radius', '14');
+            } else {
+              indicator.setAttribute('radius', '10');
             }
-            const itemRoot = this.shadowRoot.querySelector('.xf-panel-item');
-            itemRoot.prepend(indicator);
-          }
+            break;
+          case 'status':
+            indicator = document.createElement('xf-activity-complete');
+            const status = this.getAttribute('status');
+            if (status) {
+              indicator.status = status;
+            }
+            break;
+        }
+        if (indicator) {
+          const itemRoot = this.shadowRoot.querySelector('.xf-panel-item');
+          indicator.setAttribute('id', 'indicator');
+          itemRoot.prepend(indicator);
         }
         break;
       case 'progress':
@@ -78,8 +104,84 @@
           indicator.progress = Number(newValue);
         }
         break;
+      case 'status':
+        indicator = this.shadowRoot.querySelector('#indicator');
+        if (indicator) {
+          indicator.status = newValue;
+        }
+        break;
+      case 'primary-text':
+        textNode = this.shadowRoot.querySelector('.xf-panel-label-text');
+        if (textNode) {
+          textNode.textContent = newValue;
+        }
+        break;
+      case 'secondary-text':
+        textNode = this.shadowRoot.querySelector('.xf-panel-secondary-text');
+        if (!textNode) {
+          const parent = this.shadowRoot.querySelector('.xf-panel-text');
+          if (!parent) {
+            return;
+          }
+          textNode = document.createElement('span');
+          textNode.setAttribute('class', 'xf-panel-secondary-text');
+          parent.appendChild(textNode);
+        }
+        // Remove the secondary text node if the text is empty
+        if (newValue == '') {
+          textNode.remove();
+        } else {
+          textNode.textContent = newValue;
+        }
+        break;
     }
   }
+
+  /**
+   * Setter to set the indicator type.
+   * @param {string} indicator Progress (optionally large) or status.
+   */
+  set indicator(indicator) {
+    // Reflect the status property into the attribute.
+    this.setAttribute('indicator', indicator);
+  }
+
+  /**
+   * Setter to set the success/failure indication.
+   * @param {string} status Status value being set.
+   */
+  set status(status) {
+    // Reflect the status property into the attribute.
+    this.setAttribute('status', status);
+  }
+
+  /**
+   * Setter to set the progress property, sent to any child indicator.
+   * @param {string} progress Progress value being set.
+   * @public
+   */
+  set progress(progress) {
+    // Reflect the progress property into the attribute.
+    this.setAttribute('progress', progress);
+  }
+
+  /**
+   * Setter to set the primary text on the panel.
+   * @param {string} text Text to be shown.
+   */
+  set primaryText(text) {
+    // Reflect the status property into the attribute.
+    this.setAttribute('primary-text', text);
+  }
+
+  /**
+   * Setter to set the secondary text on the panel.
+   * @param {string} text Text to be shown.
+   */
+  set secondaryText(text) {
+    // Reflect the status property into the attribute.
+    this.setAttribute('secondary-text', text);
+  }
 }
 
 window.customElements.define('xf-panel-item', PanelItem);
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index 92522b1..ec2a06e 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -570,7 +570,8 @@
     this.toolbarController_ = new ToolbarController(
         this.ui_.toolbar, this.ui_.dialogNavigationList, this.ui_.listContainer,
         assert(this.ui_.locationLine), this.selectionHandler_,
-        this.directoryModel_, this.volumeManager_);
+        this.directoryModel_, this.volumeManager_,
+        /** @type {!A11yAnnounce} */ (this.ui_));
     this.emptyFolderController_ = new EmptyFolderController(
         this.ui_.emptyFolder, this.directoryModel_, this.ui_.alertDialog);
     this.actionsController_ = new ActionsController(
@@ -914,7 +915,8 @@
         this.dialogType == DialogType.FULL_PAGE);
     const grid = queryRequiredElement('.thumbnail-grid', dom);
     FileGrid.decorate(
-        grid, this.metadataModel_, this.volumeManager_, this.historyLoader_);
+        grid, this.metadataModel_, this.volumeManager_, this.historyLoader_,
+        /** @type {!A11yAnnounce} */ (this.ui_));
 
     this.addHistoryObserver_();
 
@@ -1575,6 +1577,9 @@
    * @param {VolumeInfo} volumeInfo Volume information currently selected.
    */
   showArcStorageToast_(volumeInfo) {
+    if (!util.isArcUsbStorageUIEnabled()) {
+      return;
+    }
     if (!volumeInfo ||
         volumeInfo.volumeType !== VolumeManagerCommon.VolumeType.REMOVABLE) {
       // The toast is for removable volumes.
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller_unittest.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller_unittest.js
index e11244c..92310a36 100644
--- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller_unittest.js
@@ -121,7 +121,7 @@
 
   // Setup FileGrid.
   const grid = /** @type {!FileGrid} */ (queryRequiredElement('#file-grid'));
-  FileGrid.decorate(grid, metadataModel, volumeManager, historyLoader);
+  FileGrid.decorate(grid, metadataModel, volumeManager, historyLoader, a11y);
 
   // Setup the ListContainer and its dependencies
   listContainer =
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/byte_reader.js b/ui/file_manager/file_manager/foreground/js/metadata/byte_reader.js
index 5cae08a..51fc6693 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/byte_reader.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/byte_reader.js
@@ -2,540 +2,533 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/**
- * @constructor
- * @param {ArrayBuffer} arrayBuffer An array of buffers to be read from.
- * @param {number=} opt_offset Offset to read bytes at.
- * @param {number=} opt_length Number of bytes to read.
- */
-function ByteReader(arrayBuffer, opt_offset, opt_length) {
-  opt_offset = opt_offset || 0;
-  opt_length = opt_length || (arrayBuffer.byteLength - opt_offset);
-  this.view_ = new DataView(arrayBuffer, opt_offset, opt_length);
-  this.pos_ = 0;
-  this.seekStack_ = [];
-  this.setByteOrder(ByteReader.BIG_ENDIAN);
-}
+/** @final */
+class ByteReader {
+  /**
+   * @param {ArrayBuffer} arrayBuffer An array of buffers to be read from.
+   * @param {number=} opt_offset Offset to read bytes at.
+   * @param {number=} opt_length Number of bytes to read.
+   */
+  constructor(arrayBuffer, opt_offset, opt_length) {
+    opt_offset = opt_offset || 0;
+    opt_length = opt_length || (arrayBuffer.byteLength - opt_offset);
+    /** @private @const {!DataView} */
+    this.view_ = new DataView(arrayBuffer, opt_offset, opt_length);
+    /** @private {number} */
+    this.pos_ = 0;
+    /** @private @const {!Array<number>} */
+    this.seekStack_ = [];
+    /** @private {boolean} */
+    this.littleEndian_ = false;
+  }
 
-// Static constants and methods.
+  /**
+   * Throw an error if (0 > pos >= end) or if (pos + size > end).
+   *
+   * Static utility function.
+   *
+   * @param {number} pos Position in the file.
+   * @param {number} size Number of bytes to read.
+   * @param {number} end Maximum position to read from.
+   */
+  static validateRead(pos, size, end) {
+    if (pos < 0 || pos >= end) {
+      throw new Error('Invalid read position');
+    }
+
+    if (pos + size > end) {
+      throw new Error('Read past end of buffer');
+    }
+  }
+
+  /**
+   * Read as a sequence of characters, returning them as a single string.
+   *
+   * This is a static utility function.  There is a member function with the
+   * same name which side-effects the current read position.
+   *
+   * @param {DataView} dataView Data view instance.
+   * @param {number} pos Position in bytes to read from.
+   * @param {number} size Number of bytes to read.
+   * @param {number=} opt_end Maximum position to read from.
+   * @return {string} Read string.
+   */
+  static readString(dataView, pos, size, opt_end) {
+    ByteReader.validateRead(pos, size, opt_end || dataView.byteLength);
+
+    const codes = [];
+
+    for (let i = 0; i < size; ++i) {
+      codes.push(dataView.getUint8(pos + i));
+    }
+
+    return String.fromCharCode.apply(null, codes);
+  }
+
+  /**
+   * Read as a sequence of characters, returning them as a single string.
+   *
+   * This is a static utility function.  There is a member function with the
+   * same name which side-effects the current read position.
+   *
+   * @param {DataView} dataView Data view instance.
+   * @param {number} pos Position in bytes to read from.
+   * @param {number} size Number of bytes to read.
+   * @param {number=} opt_end Maximum position to read from.
+   * @return {string} Read string.
+   */
+  static readNullTerminatedString(dataView, pos, size, opt_end) {
+    ByteReader.validateRead(pos, size, opt_end || dataView.byteLength);
+
+    const codes = [];
+
+    for (let i = 0; i < size; ++i) {
+      const code = dataView.getUint8(pos + i);
+      if (code == 0) {
+        break;
+      }
+      codes.push(code);
+    }
+
+    return String.fromCharCode.apply(null, codes);
+  }
+
+  /**
+   * Read as a sequence of UTF16 characters, returning them as a single string.
+   *
+   * This is a static utility function.  There is a member function with the
+   * same name which side-effects the current read position.
+   *
+   * @param {DataView} dataView Data view instance.
+   * @param {number} pos Position in bytes to read from.
+   * @param {boolean} bom True if BOM should be parsed.
+   * @param {number} size Number of bytes to read.
+   * @param {number=} opt_end Maximum position to read from.
+   * @return {string} Read string.
+   */
+  static readNullTerminatedStringUTF16(dataView, pos, bom, size, opt_end) {
+    ByteReader.validateRead(pos, size, opt_end || dataView.byteLength);
+
+    let littleEndian = false;
+    let start = 0;
+
+    if (bom) {
+      littleEndian = (dataView.getUint8(pos) == 0xFF);
+      start = 2;
+    }
+
+    const codes = [];
+
+    for (let i = start; i < size; i += 2) {
+      const code = dataView.getUint16(pos + i, littleEndian);
+      if (code == 0) {
+        break;
+      }
+      codes.push(code);
+    }
+
+    return String.fromCharCode.apply(null, codes);
+  }
+
+  /**
+   * Read as a sequence of bytes, returning them as a single base64 encoded
+   * string.
+   *
+   * This is a static utility function.  There is a member function with the
+   * same name which side-effects the current read position.
+   *
+   * @param {DataView} dataView Data view instance.
+   * @param {number} pos Position in bytes to read from.
+   * @param {number} size Number of bytes to read.
+   * @param {number=} opt_end Maximum position to read from.
+   * @return {string} Base 64 encoded value.
+   */
+  static readBase64(dataView, pos, size, opt_end) {
+    ByteReader.validateRead(pos, size, opt_end || dataView.byteLength);
+
+    const rv = [];
+    const chars = [];
+    let padding = 0;
+
+    for (let i = 0; i < size; /* incremented inside */) {
+      let bits = dataView.getUint8(pos + (i++)) << 16;
+
+      if (i < size) {
+        bits |= dataView.getUint8(pos + (i++)) << 8;
+
+        if (i < size) {
+          bits |= dataView.getUint8(pos + (i++));
+        } else {
+          padding = 1;
+        }
+      } else {
+        padding = 2;
+      }
+
+      chars[3] = ByteReader.base64Alphabet_[bits & 63];
+      chars[2] = ByteReader.base64Alphabet_[(bits >> 6) & 63];
+      chars[1] = ByteReader.base64Alphabet_[(bits >> 12) & 63];
+      chars[0] = ByteReader.base64Alphabet_[(bits >> 18) & 63];
+
+      rv.push.apply(rv, chars);
+    }
+
+    if (padding > 0) {
+      rv[rv.length - 1] = '=';
+    }
+    if (padding > 1) {
+      rv[rv.length - 2] = '=';
+    }
+
+    return rv.join('');
+  }
+
+  /**
+   * Read as an image encoded in a data url.
+   *
+   * This is a static utility function.  There is a member function with the
+   * same name which side-effects the current read position.
+   *
+   * @param {DataView} dataView Data view instance.
+   * @param {number} pos Position in bytes to read from.
+   * @param {number} size Number of bytes to read.
+   * @param {number=} opt_end Maximum position to read from.
+   * @return {string} Image as a data url.
+   */
+  static readImage(dataView, pos, size, opt_end) {
+    opt_end = opt_end || dataView.byteLength;
+    ByteReader.validateRead(pos, size, opt_end);
+
+    // Two bytes is enough to identify the mime type.
+    const prefixToMime = {
+      '\x89P': 'png',
+      '\xFF\xD8': 'jpeg',
+      'BM': 'bmp',
+      'GI': 'gif',
+    };
+
+    const prefix = ByteReader.readString(dataView, pos, 2, opt_end);
+    const mime = prefixToMime[prefix] ||
+        dataView.getUint16(pos, false).toString(16);  // For debugging.
+
+    const b64 = ByteReader.readBase64(dataView, pos, size, opt_end);
+    return 'data:image/' + mime + ';base64,' + b64;
+  }
+
+  /**
+   * Return true if the requested number of bytes can be read from the buffer.
+   *
+   * @param {number} size Number of bytes to read.
+   * @return {boolean} True if allowed, false otherwise.
+   */
+  canRead(size) {
+    return this.pos_ + size <= this.view_.byteLength;
+  }
+
+  /**
+   * Return true if the current position is past the end of the buffer.
+   * @return {boolean} True if EOF, otherwise false.
+   */
+  eof() {
+    return this.pos_ >= this.view_.byteLength;
+  }
+
+  /**
+   * Return true if the current position is before the beginning of the buffer.
+   * @return {boolean} True if BOF, otherwise false.
+   */
+  bof() {
+    return this.pos_ < 0;
+  }
+
+  /**
+   * Return true if the current position is outside the buffer.
+   * @return {boolean} True if outside, false if inside.
+   */
+  beof() {
+    return this.pos_ >= this.view_.byteLength || this.pos_ < 0;
+  }
+
+  /**
+   * Set the expected byte ordering for future reads.
+   * @param {number} order Byte order. Either LITTLE_ENDIAN or BIG_ENDIAN.
+   */
+  setByteOrder(order) {
+    this.littleEndian_ = order == ByteReader.LITTLE_ENDIAN;
+  }
+
+  /**
+   * Throw an error if the reader is at an invalid position, or if a read a read
+   * of |size| would put it in one.
+   *
+   * You may optionally pass opt_end to override what is considered to be the
+   * end of the buffer.
+   *
+   * @param {number} size Number of bytes to read.
+   * @param {number=} opt_end Maximum position to read from.
+   */
+  validateRead(size, opt_end) {
+    if (typeof opt_end == 'undefined') {
+      opt_end = this.view_.byteLength;
+    }
+
+    ByteReader.validateRead(this.pos_, size, opt_end);
+  }
+
+  /**
+   * @param {number} width Number of bytes to read.
+   * @param {boolean=} opt_signed True if signed, false otherwise.
+   * @param {number=} opt_end Maximum position to read from.
+   * @return {number} Scalar value.
+   */
+  readScalar(width, opt_signed, opt_end) {
+    let method = opt_signed ? 'getInt' : 'getUint';
+
+    switch (width) {
+      case 1:
+        method += '8';
+        break;
+
+      case 2:
+        method += '16';
+        break;
+
+      case 4:
+        method += '32';
+        break;
+
+      case 8:
+        method += '64';
+        break;
+
+      default:
+        throw new Error('Invalid width: ' + width);
+        break;
+    }
+
+    this.validateRead(width, opt_end);
+    const rv = this.view_[method](this.pos_, this.littleEndian_);
+    this.pos_ += width;
+    return rv;
+  }
+
+  /**
+   * Read as a sequence of characters, returning them as a single string.
+   *
+   * Adjusts the current position on success.  Throws an exception if the
+   * read would go past the end of the buffer.
+   *
+   * @param {number} size Number of bytes to read.
+   * @param {number=} opt_end Maximum position to read from.
+   * @return {string} String value.
+   */
+  readString(size, opt_end) {
+    const rv = ByteReader.readString(this.view_, this.pos_, size, opt_end);
+    this.pos_ += size;
+    return rv;
+  }
+
+  /**
+   * Read as a sequence of characters, returning them as a single string.
+   *
+   * Adjusts the current position on success.  Throws an exception if the
+   * read would go past the end of the buffer.
+   *
+   * @param {number} size Number of bytes to read.
+   * @param {number=} opt_end Maximum position to read from.
+   * @return {string} Null-terminated string value.
+   */
+  readNullTerminatedString(size, opt_end) {
+    const rv = ByteReader.readNullTerminatedString(
+        this.view_, this.pos_, size, opt_end);
+    this.pos_ += rv.length;
+
+    if (rv.length < size) {
+      // If we've stopped reading because we found '0' but didn't hit size limit
+      // then we should skip additional '0' character
+      this.pos_++;
+    }
+
+    return rv;
+  }
+
+  /**
+   * Read as a sequence of UTF16 characters, returning them as a single string.
+   *
+   * Adjusts the current position on success.  Throws an exception if the
+   * read would go past the end of the buffer.
+   *
+   * @param {boolean} bom True if BOM should be parsed.
+   * @param {number} size Number of bytes to read.
+   * @param {number=} opt_end Maximum position to read from.
+   * @return {string} Read string.
+   */
+  readNullTerminatedStringUTF16(bom, size, opt_end) {
+    const rv = ByteReader.readNullTerminatedStringUTF16(
+        this.view_, this.pos_, bom, size, opt_end);
+
+    if (bom) {
+      // If the BOM word was present advance the position.
+      this.pos_ += 2;
+    }
+
+    this.pos_ += rv.length;
+
+    if (rv.length < size) {
+      // If we've stopped reading because we found '0' but didn't hit size limit
+      // then we should skip additional '0' character
+      this.pos_ += 2;
+    }
+
+    return rv;
+  }
+
+  /**
+   * Read as an array of numbers.
+   *
+   * Adjusts the current position on success.  Throws an exception if the
+   * read would go past the end of the buffer.
+   *
+   * @param {number} size Number of bytes to read.
+   * @param {number=} opt_end Maximum position to read from.
+   * @param {function(new:Array<*>)=} opt_arrayConstructor Array constructor.
+   * @return {Array<*>} Array of bytes.
+   */
+  readSlice(size, opt_end, opt_arrayConstructor) {
+    this.validateRead(size, opt_end);
+
+    const arrayConstructor = opt_arrayConstructor || Uint8Array;
+    const slice = new arrayConstructor(
+        this.view_.buffer, this.view_.byteOffset + this.pos_, size);
+    this.pos_ += size;
+
+    return slice;
+  }
+
+  /**
+   * Read as a sequence of bytes, returning them as a single base64 encoded
+   * string.
+   *
+   * Adjusts the current position on success.  Throws an exception if the
+   * read would go past the end of the buffer.
+   *
+   * @param {number} size Number of bytes to read.
+   * @param {number=} opt_end Maximum position to read from.
+   * @return {string} Base 64 encoded value.
+   */
+  readBase64(size, opt_end) {
+    const rv = ByteReader.readBase64(this.view_, this.pos_, size, opt_end);
+    this.pos_ += size;
+    return rv;
+  }
+
+  /**
+   * Read an image returning it as a data url.
+   *
+   * Adjusts the current position on success.  Throws an exception if the
+   * read would go past the end of the buffer.
+   *
+   * @param {number} size Number of bytes to read.
+   * @param {number=} opt_end Maximum position to read from.
+   * @return {string} Image as a data url.
+   */
+  readImage(size, opt_end) {
+    const rv = ByteReader.readImage(this.view_, this.pos_, size, opt_end);
+    this.pos_ += size;
+    return rv;
+  }
+
+  /**
+   * Seek to a give position relative to opt_seekStart.
+   *
+   * @param {number} pos Position in bytes to seek to.
+   * @param {number=} opt_seekStart Relative position in bytes.
+   * @param {number=} opt_end Maximum position to seek to.
+   */
+  seek(pos, opt_seekStart, opt_end) {
+    opt_end = opt_end || this.view_.byteLength;
+
+    let newPos;
+    if (opt_seekStart == ByteReader.SEEK_CUR) {
+      newPos = this.pos_ + pos;
+    } else if (opt_seekStart == ByteReader.SEEK_END) {
+      newPos = opt_end + pos;
+    } else {
+      newPos = pos;
+    }
+
+    if (newPos < 0 || newPos > this.view_.byteLength) {
+      throw new Error('Seek outside of buffer: ' + (newPos - opt_end));
+    }
+
+    this.pos_ = newPos;
+  }
+
+  /**
+   * Seek to a given position relative to opt_seekStart, saving the current
+   * position.
+   *
+   * Recover the current position with a call to seekPop.
+   *
+   * @param {number} pos Position in bytes to seek to.
+   * @param {number=} opt_seekStart Relative position in bytes.
+   */
+  pushSeek(pos, opt_seekStart) {
+    const oldPos = this.pos_;
+    this.seek(pos, opt_seekStart);
+    // Alter the seekStack_ after the call to seek(), in case it throws.
+    this.seekStack_.push(oldPos);
+  }
+
+  /**
+   * Undo a previous seekPush.
+   */
+  popSeek() {
+    this.seek(this.seekStack_.pop());
+  }
+
+  /**
+   * Return the current read position.
+   * @return {number} Current position in bytes.
+   */
+  tell() {
+    return this.pos_;
+  }
+}
 
 /**
  * Intel, 0x1234 is [0x34, 0x12]
- * @const
- * @type {number}
+ * @const {number}
  */
 ByteReader.LITTLE_ENDIAN = 0;
+
 /**
  * Motorola, 0x1234 is [0x12, 0x34]
- * @const
- * @type {number}
+ * @const {number}
  */
 ByteReader.BIG_ENDIAN = 1;
 
 /**
  * Seek relative to the beginning of the buffer.
- * @const
- * @type {number}
+ * @const {number}
  */
 ByteReader.SEEK_BEG = 0;
+
 /**
  * Seek relative to the current position.
- * @const
- * @type {number}
+ * @const {number}
  */
 ByteReader.SEEK_CUR = 1;
+
 /**
  * Seek relative to the end of the buffer.
- * @const
- * @type {number}
+ * @const {number}
  */
 ByteReader.SEEK_END = 2;
 
 /**
- * Throw an error if (0 > pos >= end) or if (pos + size > end).
- *
- * Static utility function.
- *
- * @param {number} pos Position in the file.
- * @param {number} size Number of bytes to read.
- * @param {number} end Maximum position to read from.
- */
-ByteReader.validateRead = (pos, size, end) => {
-  if (pos < 0 || pos >= end) {
-    throw new Error('Invalid read position');
-  }
-
-  if (pos + size > end) {
-    throw new Error('Read past end of buffer');
-  }
-};
-
-/**
- * Read as a sequence of characters, returning them as a single string.
- *
- * This is a static utility function.  There is a member function with the
- * same name which side-effects the current read position.
- *
- * @param {DataView} dataView Data view instance.
- * @param {number} pos Position in bytes to read from.
- * @param {number} size Number of bytes to read.
- * @param {number=} opt_end Maximum position to read from.
- * @return {string} Read string.
- */
-ByteReader.readString = (dataView, pos, size, opt_end) => {
-  ByteReader.validateRead(pos, size, opt_end || dataView.byteLength);
-
-  const codes = [];
-
-  for (let i = 0; i < size; ++i) {
-    codes.push(dataView.getUint8(pos + i));
-  }
-
-  return String.fromCharCode.apply(null, codes);
-};
-
-/**
- * Read as a sequence of characters, returning them as a single string.
- *
- * This is a static utility function.  There is a member function with the
- * same name which side-effects the current read position.
- *
- * @param {DataView} dataView Data view instance.
- * @param {number} pos Position in bytes to read from.
- * @param {number} size Number of bytes to read.
- * @param {number=} opt_end Maximum position to read from.
- * @return {string} Read string.
- */
-ByteReader.readNullTerminatedString = (dataView, pos, size, opt_end) => {
-  ByteReader.validateRead(pos, size, opt_end || dataView.byteLength);
-
-  const codes = [];
-
-  for (let i = 0; i < size; ++i) {
-    const code = dataView.getUint8(pos + i);
-    if (code == 0) {
-      break;
-    }
-    codes.push(code);
-  }
-
-  return String.fromCharCode.apply(null, codes);
-};
-
-/**
- * Read as a sequence of UTF16 characters, returning them as a single string.
- *
- * This is a static utility function.  There is a member function with the
- * same name which side-effects the current read position.
- *
- * @param {DataView} dataView Data view instance.
- * @param {number} pos Position in bytes to read from.
- * @param {boolean} bom True if BOM should be parsed.
- * @param {number} size Number of bytes to read.
- * @param {number=} opt_end Maximum position to read from.
- * @return {string} Read string.
- */
-ByteReader.readNullTerminatedStringUTF16 =
-    (dataView, pos, bom, size, opt_end) => {
-      ByteReader.validateRead(pos, size, opt_end || dataView.byteLength);
-
-      let littleEndian = false;
-      let start = 0;
-
-      if (bom) {
-        littleEndian = (dataView.getUint8(pos) == 0xFF);
-        start = 2;
-      }
-
-      const codes = [];
-
-      for (let i = start; i < size; i += 2) {
-        const code = dataView.getUint16(pos + i, littleEndian);
-        if (code == 0) {
-          break;
-        }
-        codes.push(code);
-      }
-
-      return String.fromCharCode.apply(null, codes);
-    };
-
-/**
- * @const
- * @type {Array<string>}
- * @private
+ * @private @const {Array<string>}
  */
 ByteReader.base64Alphabet_ =
     ('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/')
         .split('');
-
-/**
- * Read as a sequence of bytes, returning them as a single base64 encoded
- * string.
- *
- * This is a static utility function.  There is a member function with the
- * same name which side-effects the current read position.
- *
- * @param {DataView} dataView Data view instance.
- * @param {number} pos Position in bytes to read from.
- * @param {number} size Number of bytes to read.
- * @param {number=} opt_end Maximum position to read from.
- * @return {string} Base 64 encoded value.
- */
-ByteReader.readBase64 = (dataView, pos, size, opt_end) => {
-  ByteReader.validateRead(pos, size, opt_end || dataView.byteLength);
-
-  const rv = [];
-  const chars = [];
-  let padding = 0;
-
-  for (let i = 0; i < size; /* incremented inside */) {
-    let bits = dataView.getUint8(pos + (i++)) << 16;
-
-    if (i < size) {
-      bits |= dataView.getUint8(pos + (i++)) << 8;
-
-      if (i < size) {
-        bits |= dataView.getUint8(pos + (i++));
-      } else {
-        padding = 1;
-      }
-    } else {
-      padding = 2;
-    }
-
-    chars[3] = ByteReader.base64Alphabet_[bits & 63];
-    chars[2] = ByteReader.base64Alphabet_[(bits >> 6) & 63];
-    chars[1] = ByteReader.base64Alphabet_[(bits >> 12) & 63];
-    chars[0] = ByteReader.base64Alphabet_[(bits >> 18) & 63];
-
-    rv.push.apply(rv, chars);
-  }
-
-  if (padding > 0) {
-    rv[rv.length - 1] = '=';
-  }
-  if (padding > 1) {
-    rv[rv.length - 2] = '=';
-  }
-
-  return rv.join('');
-};
-
-/**
- * Read as an image encoded in a data url.
- *
- * This is a static utility function.  There is a member function with the
- * same name which side-effects the current read position.
- *
- * @param {DataView} dataView Data view instance.
- * @param {number} pos Position in bytes to read from.
- * @param {number} size Number of bytes to read.
- * @param {number=} opt_end Maximum position to read from.
- * @return {string} Image as a data url.
- */
-ByteReader.readImage = (dataView, pos, size, opt_end) => {
-  opt_end = opt_end || dataView.byteLength;
-  ByteReader.validateRead(pos, size, opt_end);
-
-  // Two bytes is enough to identify the mime type.
-  const prefixToMime = {
-    '\x89P': 'png',
-    '\xFF\xD8': 'jpeg',
-    'BM': 'bmp',
-    'GI': 'gif',
-  };
-
-  const prefix = ByteReader.readString(dataView, pos, 2, opt_end);
-  const mime = prefixToMime[prefix] ||
-      dataView.getUint16(pos, false).toString(16);  // For debugging.
-
-  const b64 = ByteReader.readBase64(dataView, pos, size, opt_end);
-  return 'data:image/' + mime + ';base64,' + b64;
-};
-
-// Instance methods.
-
-/**
- * Return true if the requested number of bytes can be read from the buffer.
- *
- * @param {number} size Number of bytes to read.
- * @return {boolean} True if allowed, false otherwise.
- */
-ByteReader.prototype.canRead = function(size) {
-  return this.pos_ + size <= this.view_.byteLength;
-};
-
-/**
- * Return true if the current position is past the end of the buffer.
- * @return {boolean} True if EOF, otherwise false.
- */
-ByteReader.prototype.eof = function() {
-  return this.pos_ >= this.view_.byteLength;
-};
-
-/**
- * Return true if the current position is before the beginning of the buffer.
- * @return {boolean} True if BOF, otherwise false.
- */
-ByteReader.prototype.bof = function() {
-  return this.pos_ < 0;
-};
-
-/**
- * Return true if the current position is outside the buffer.
- * @return {boolean} True if outside, false if inside.
- */
-ByteReader.prototype.beof = function() {
-  return this.pos_ >= this.view_.byteLength || this.pos_ < 0;
-};
-
-/**
- * Set the expected byte ordering for future reads.
- * @param {number} order Byte order. Either LITTLE_ENDIAN or BIG_ENDIAN.
- */
-ByteReader.prototype.setByteOrder = function(order) {
-  this.littleEndian_ = order == ByteReader.LITTLE_ENDIAN;
-};
-
-/**
- * Throw an error if the reader is at an invalid position, or if a read a read
- * of |size| would put it in one.
- *
- * You may optionally pass opt_end to override what is considered to be the
- * end of the buffer.
- *
- * @param {number} size Number of bytes to read.
- * @param {number=} opt_end Maximum position to read from.
- */
-ByteReader.prototype.validateRead = function(size, opt_end) {
-  if (typeof opt_end == 'undefined') {
-    opt_end = this.view_.byteLength;
-  }
-
-  ByteReader.validateRead(this.pos_, size, opt_end);
-};
-
-/**
- * @param {number} width Number of bytes to read.
- * @param {boolean=} opt_signed True if signed, false otherwise.
- * @param {number=} opt_end Maximum position to read from.
- * @return {number} Scalar value.
- */
-ByteReader.prototype.readScalar = function(width, opt_signed, opt_end) {
-  let method = opt_signed ? 'getInt' : 'getUint';
-
-  switch (width) {
-    case 1:
-      method += '8';
-      break;
-
-    case 2:
-      method += '16';
-      break;
-
-    case 4:
-      method += '32';
-      break;
-
-    case 8:
-      method += '64';
-      break;
-
-    default:
-      throw new Error('Invalid width: ' + width);
-      break;
-  }
-
-  this.validateRead(width, opt_end);
-  const rv = this.view_[method](this.pos_, this.littleEndian_);
-  this.pos_ += width;
-  return rv;
-};
-
-/**
- * Read as a sequence of characters, returning them as a single string.
- *
- * Adjusts the current position on success.  Throws an exception if the
- * read would go past the end of the buffer.
- *
- * @param {number} size Number of bytes to read.
- * @param {number=} opt_end Maximum position to read from.
- * @return {string} String value.
- */
-ByteReader.prototype.readString = function(size, opt_end) {
-  const rv = ByteReader.readString(this.view_, this.pos_, size, opt_end);
-  this.pos_ += size;
-  return rv;
-};
-
-
-/**
- * Read as a sequence of characters, returning them as a single string.
- *
- * Adjusts the current position on success.  Throws an exception if the
- * read would go past the end of the buffer.
- *
- * @param {number} size Number of bytes to read.
- * @param {number=} opt_end Maximum position to read from.
- * @return {string} Null-terminated string value.
- */
-ByteReader.prototype.readNullTerminatedString = function(size, opt_end) {
-  const rv =
-      ByteReader.readNullTerminatedString(this.view_, this.pos_, size, opt_end);
-  this.pos_ += rv.length;
-
-  if (rv.length < size) {
-    // If we've stopped reading because we found '0' but didn't hit size limit
-    // then we should skip additional '0' character
-    this.pos_++;
-  }
-
-  return rv;
-};
-
-
-/**
- * Read as a sequence of UTF16 characters, returning them as a single string.
- *
- * Adjusts the current position on success.  Throws an exception if the
- * read would go past the end of the buffer.
- *
- * @param {boolean} bom True if BOM should be parsed.
- * @param {number} size Number of bytes to read.
- * @param {number=} opt_end Maximum position to read from.
- * @return {string} Read string.
- */
-ByteReader.prototype.readNullTerminatedStringUTF16 = function(
-    bom, size, opt_end) {
-  const rv = ByteReader.readNullTerminatedStringUTF16(
-      this.view_, this.pos_, bom, size, opt_end);
-
-  if (bom) {
-    // If the BOM word was present advance the position.
-    this.pos_ += 2;
-  }
-
-  this.pos_ += rv.length;
-
-  if (rv.length < size) {
-    // If we've stopped reading because we found '0' but didn't hit size limit
-    // then we should skip additional '0' character
-    this.pos_ += 2;
-  }
-
-  return rv;
-};
-
-
-/**
- * Read as an array of numbers.
- *
- * Adjusts the current position on success.  Throws an exception if the
- * read would go past the end of the buffer.
- *
- * @param {number} size Number of bytes to read.
- * @param {number=} opt_end Maximum position to read from.
- * @param {function(new:Array<*>)=} opt_arrayConstructor Array constructor.
- * @return {Array<*>} Array of bytes.
- */
-ByteReader.prototype.readSlice = function(size, opt_end, opt_arrayConstructor) {
-  this.validateRead(size, opt_end);
-
-  const arrayConstructor = opt_arrayConstructor || Uint8Array;
-  const slice = new arrayConstructor(
-      this.view_.buffer, this.view_.byteOffset + this.pos_, size);
-  this.pos_ += size;
-
-  return slice;
-};
-
-/**
- * Read as a sequence of bytes, returning them as a single base64 encoded
- * string.
- *
- * Adjusts the current position on success.  Throws an exception if the
- * read would go past the end of the buffer.
- *
- * @param {number} size Number of bytes to read.
- * @param {number=} opt_end Maximum position to read from.
- * @return {string} Base 64 encoded value.
- */
-ByteReader.prototype.readBase64 = function(size, opt_end) {
-  const rv = ByteReader.readBase64(this.view_, this.pos_, size, opt_end);
-  this.pos_ += size;
-  return rv;
-};
-
-/**
- * Read an image returning it as a data url.
- *
- * Adjusts the current position on success.  Throws an exception if the
- * read would go past the end of the buffer.
- *
- * @param {number} size Number of bytes to read.
- * @param {number=} opt_end Maximum position to read from.
- * @return {string} Image as a data url.
- */
-ByteReader.prototype.readImage = function(size, opt_end) {
-  const rv = ByteReader.readImage(this.view_, this.pos_, size, opt_end);
-  this.pos_ += size;
-  return rv;
-};
-
-/**
- * Seek to a give position relative to opt_seekStart.
- *
- * @param {number} pos Position in bytes to seek to.
- * @param {number=} opt_seekStart Relative position in bytes.
- * @param {number=} opt_end Maximum position to seek to.
- */
-ByteReader.prototype.seek = function(pos, opt_seekStart, opt_end) {
-  opt_end = opt_end || this.view_.byteLength;
-
-  let newPos;
-  if (opt_seekStart == ByteReader.SEEK_CUR) {
-    newPos = this.pos_ + pos;
-  } else if (opt_seekStart == ByteReader.SEEK_END) {
-    newPos = opt_end + pos;
-  } else {
-    newPos = pos;
-  }
-
-  if (newPos < 0 || newPos > this.view_.byteLength) {
-    throw new Error('Seek outside of buffer: ' + (newPos - opt_end));
-  }
-
-  this.pos_ = newPos;
-};
-
-/**
- * Seek to a given position relative to opt_seekStart, saving the current
- * position.
- *
- * Recover the current position with a call to seekPop.
- *
- * @param {number} pos Position in bytes to seek to.
- * @param {number=} opt_seekStart Relative position in bytes.
- */
-ByteReader.prototype.pushSeek = function(pos, opt_seekStart) {
-  const oldPos = this.pos_;
-  this.seek(pos, opt_seekStart);
-  // Alter the seekStack_ after the call to seek(), in case it throws.
-  this.seekStack_.push(oldPos);
-};
-
-/**
- * Undo a previous seekPush.
- */
-ByteReader.prototype.popSeek = function() {
-  this.seek(this.seekStack_.pop());
-};
-
-/**
- * Return the current read position.
- * @return {number} Current position in bytes.
- */
-ByteReader.prototype.tell = function() {
-  return this.pos_;
-};
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider.js b/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider.js
index ad86d9b..0579a5d 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider.js
@@ -2,52 +2,365 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/**
- * @param {!MessagePort=} opt_messagePort Message port overriding the default
- *     worker port.
- * @extends {MetadataProvider}
- * @constructor
- * @struct
- */
-function ContentMetadataProvider(opt_messagePort) {
-  MetadataProvider.call(this, ContentMetadataProvider.PROPERTY_NAMES);
+/** @final */
+class ContentMetadataProvider extends MetadataProvider {
+  /**
+   * @param {!MessagePort=} opt_messagePort Message port overriding the default
+   *     worker port.
+   */
+  constructor(opt_messagePort) {
+    super(ContentMetadataProvider.PROPERTY_NAMES);
+
+    /**
+     * Pass all URLs to the metadata reader until we have a correct filter.
+     * @private {RegExp}
+     */
+    this.urlFilter_ = /.*/;
+
+    /** @private @const {!MessagePort} */
+    this.dispatcher_ = opt_messagePort ?
+        opt_messagePort :
+        new SharedWorker(ContentMetadataProvider.WORKER_SCRIPT).port;
+    this.dispatcher_.onmessage = this.onMessage_.bind(this);
+    this.dispatcher_.postMessage({verb: 'init'});
+    this.dispatcher_.start();
+
+    /**
+     * Initialization is not complete until the Worker sends back the
+     * 'initialized' message.  See below.
+     * @private {boolean}
+     */
+    this.initialized_ = false;
+
+    /**
+     * Map from Entry.toURL() to callback.
+     * Note that simultaneous requests for same url are handled in
+     * MetadataCache.
+     * @private @const {!Object<!string, !Array<function(!MetadataItem)>>}
+     */
+    this.callbacks_ = {};
+  }
 
   /**
-   * Pass all URLs to the metadata reader until we have a correct filter.
-   * @private {RegExp}
+   * Converts content metadata from parsers to the internal format.
+   * @param {Object} metadata The content metadata.
+   * @return {!MetadataItem} Converted metadata.
    */
-  this.urlFilter_ = /.*/;
+  static convertContentMetadata(metadata) {
+    const item = new MetadataItem();
+    item.contentImageTransform = metadata['imageTransform'];
+    item.contentThumbnailTransform = metadata['thumbnailTransform'];
+    item.contentThumbnailUrl = metadata['thumbnailURL'];
+    item.exifLittleEndian = metadata['littleEndian'];
+    item.ifd = metadata['ifd'];
+    item.imageHeight = metadata['height'];
+    item.imageWidth = metadata['width'];
+    item.mediaMimeType = metadata['mimeType'];
+    return item;
+  }
+
+  /** @override */
+  get(requests) {
+    if (!requests.length) {
+      return Promise.resolve([]);
+    }
+
+    const promises = [];
+    for (let i = 0; i < requests.length; i++) {
+      promises.push(new Promise(((request, fulfill) => {
+                                  this.getImpl_(
+                                      request.entry, request.names, fulfill);
+                                }).bind(null, requests[i])));
+    }
+    return Promise.all(promises);
+  }
 
   /**
-   * @private {!MessagePort}
-   * @const
+   * Fetches the metadata.
+   * @param {!Entry} entry File entry.
+   * @param {!Array<string>} names Requested metadata type.
+   * @param {function(!MetadataItem)} callback Callback expects metadata value.
+   *     This callback is called asynchronously.
+   * @private
    */
-  this.dispatcher_ = opt_messagePort ?
-      opt_messagePort :
-      new SharedWorker(ContentMetadataProvider.WORKER_SCRIPT).port;
-  this.dispatcher_.onmessage = this.onMessage_.bind(this);
-  this.dispatcher_.postMessage({verb: 'init'});
-  this.dispatcher_.start();
+  getImpl_(entry, names, callback) {
+    if (entry.isDirectory) {
+      setTimeout(
+          callback.bind(
+              null,
+              this.createError_(
+                  entry.toURL(), 'get',
+                  'we don\'t generate thumbnails for directory')),
+          0);
+      return;
+    }
+    // TODO(ryoh): mediaGalleries API does not handle
+    // image metadata correctly.
+    // We parse it in our pure js parser.
+    // chrome/browser/media_galleries/fileapi/supported_image_type_validator.cc
+    const type = FileType.getType(entry);
+    if (type && type.type === 'image') {
+      const url = entry.toURL();
+      if (this.callbacks_[url]) {
+        this.callbacks_[url].push(callback);
+      } else {
+        this.callbacks_[url] = [callback];
+        this.dispatcher_.postMessage({verb: 'request', arguments: [url]});
+      }
+      return;
+    }
+    this.getFromMediaGalleries_(entry, names).then(callback);
+  }
 
   /**
-   * Initialization is not complete until the Worker sends back the
-   * 'initialized' message.  See below.
-   * @private {boolean}
+   * Gets a metadata from mediaGalleries API
+   *
+   * @param {!Entry} entry File entry.
+   * @param {!Array<string>} names Requested metadata type.
+   * @return {!Promise<!MetadataItem>}  Promise that resolves with the metadata
+   *     of
+   *    the entry.
+   * @private
    */
-  this.initialized_ = false;
+  getFromMediaGalleries_(entry, names) {
+    const self = this;
+    return new Promise((resolve, reject) => {
+      entry.file(
+          blob => {
+            let metadataType = 'mimeTypeOnly';
+            if (names.indexOf('mediaArtist') !== -1 ||
+                names.indexOf('mediaTitle') !== -1 ||
+                names.indexOf('mediaTrack') !== -1 ||
+                names.indexOf('mediaYearRecorded') !== -1) {
+              metadataType = 'mimeTypeAndTags';
+            }
+            if (names.indexOf('contentThumbnailUrl') !== -1) {
+              metadataType = 'all';
+            }
+            chrome.mediaGalleries.getMetadata(
+                blob, {metadataType: metadataType}, metadata => {
+                  if (chrome.runtime.lastError) {
+                    resolve(self.createError_(
+                        entry.toURL(), 'resolving metadata',
+                        chrome.runtime.lastError.toString()));
+                  } else {
+                    self.convertMediaMetadataToMetadataItem_(entry, metadata)
+                        .then(resolve, reject);
+                  }
+                });
+          },
+          err => {
+            resolve(self.createError_(
+                entry.toURL(), 'loading file entry',
+                'failed to open file entry'));
+          });
+    });
+  }
 
   /**
-   * Map from Entry.toURL() to callback.
-   * Note that simultaneous requests for same url are handled in MetadataCache.
-   * @private {!Object<!string, !Array<function(!MetadataItem)>>}
-   * @const
+   * Dispatches a message from a metadata reader to the appropriate on* method.
+   * @param {Object} event The event.
+   * @private
    */
-  this.callbacks_ = {};
+  onMessage_(event) {
+    const data = event.data;
+    switch (data.verb) {
+      case 'initialized':
+        this.onInitialized_(data.arguments[0]);
+        break;
+      case 'result':
+        this.onResult_(
+            data.arguments[0],
+            data.arguments[1] ? ContentMetadataProvider.convertContentMetadata(
+                                    data.arguments[1]) :
+                                new MetadataItem());
+        break;
+      case 'error':
+        const error = this.createError_(
+            data.arguments[0], data.arguments[1], data.arguments[2]);
+        this.onResult_(data.arguments[0], error);
+        break;
+      case 'log':
+        this.onLog_(data.arguments[0]);
+        break;
+      default:
+        assertNotReached();
+        break;
+    }
+  }
+
+  /**
+   * Handles the 'initialized' message from the metadata reader Worker.
+   * @param {RegExp} regexp Regexp of supported urls.
+   * @private
+   */
+  onInitialized_(regexp) {
+    this.urlFilter_ = regexp;
+
+    // Tests can monitor for this state with
+    // ExtensionTestMessageListener listener("worker-initialized");
+    // ASSERT_TRUE(listener.WaitUntilSatisfied());
+    // Automated tests need to wait for this, otherwise we crash in
+    // browser_test cleanup because the worker process still has
+    // URL requests in-flight.
+    util.testSendMessage('worker-initialized');
+    this.initialized_ = true;
+  }
+
+  /**
+   * Handles the 'result' message from the worker.
+   * @param {string} url File url.
+   * @param {!MetadataItem} metadataItem The metadata item.
+   * @private
+   */
+  onResult_(url, metadataItem) {
+    const callbacks = this.callbacks_[url];
+    delete this.callbacks_[url];
+    for (let i = 0; i < callbacks.length; i++) {
+      callbacks[i](metadataItem);
+    }
+  }
+
+  /**
+   * Handles the 'log' message from the worker.
+   * @param {Array<*>} arglist Log arguments.
+   * @private
+   */
+  onLog_(arglist) {
+    console.log.apply(
+        console, ['ContentMetadataProvider log:'].concat(arglist));
+  }
+
+  /**
+   * Dispatches a message from MediaGalleries API to the appropriate on* method.
+   * @param {!Entry} entry File entry.
+   * @param {!Object} metadata The metadata from MediaGalleries API.
+   * @return {!Promise<!MetadataItem>}  Promise that resolves with
+   *    converted metadata item.
+   * @private
+   */
+  convertMediaMetadataToMetadataItem_(entry, metadata) {
+    return new Promise((resolve, reject) => {
+      if (!metadata) {
+        resolve(this.createError_(
+            entry.toURL(), 'Reading a thumbnail image',
+            'Failed to parse metadata'));
+        return;
+      }
+      const item = new MetadataItem();
+      const mimeType = metadata['mimeType'];
+      item.contentMimeType = mimeType;
+      item.mediaMimeType = mimeType;
+      const trans = {scaleX: 1, scaleY: 1, rotate90: 0};
+      if (metadata.rotation) {
+        switch (metadata.rotation) {
+          case 0:
+            break;
+          case 90:
+            trans.rotate90 = 1;
+            break;
+          case 180:
+            trans.scaleX *= -1;
+            trans.scaleY *= -1;
+            break;
+          case 270:
+            trans.rotate90 = 1;
+            trans.scaleX *= -1;
+            trans.scaleY *= -1;
+            break;
+          default:
+            console.error('Unknown rotation angle: ', metadata.rotation);
+        }
+      }
+      if (metadata.rotation) {
+        item.contentImageTransform = item.contentThumbnailTransform = trans;
+      }
+      item.imageHeight = metadata['height'];
+      item.imageWidth = metadata['width'];
+      item.mediaAlbum = metadata['album'];
+      item.mediaArtist = metadata['artist'];
+      item.mediaDuration = metadata['duration'];
+      item.mediaGenre = metadata['genre'];
+      item.mediaTitle = metadata['title'];
+      if (metadata['track']) {
+        item.mediaTrack = '' + metadata['track'];
+      }
+      if (metadata.rawTags) {
+        metadata.rawTags.forEach(entry => {
+          if (entry.type === 'mp3') {
+            if (entry.tags['date']) {
+              item.mediaYearRecorded = entry.tags['date'];
+            }
+            // It is possible that metadata['track'] is undefined but this is
+            // defined.
+            if (entry.tags['track']) {
+              item.mediaTrack = entry.tags['track'];
+            }
+          }
+        });
+      }
+      if (metadata.attachedImages && metadata.attachedImages.length > 0) {
+        const reader = new FileReader();
+        reader.onload = e => {
+          item.contentThumbnailUrl = e.target.result;
+          resolve(item);
+        };
+        reader.onerror = e => {
+          resolve(this.createError_(
+              entry.toURL(), 'Reading a thumbnail image',
+              reader.error.toString()));
+        };
+        reader.readAsDataURL(metadata.attachedImages[0]);
+      } else {
+        resolve(item);
+      }
+    });
+  }
+
+  /**
+   * Handles the 'error' message from the worker.
+   * @param {string} url File entry.
+   * @param {string} step Step failed.
+   * @param {string} errorDescription Error description.
+   * @return {!MetadataItem} Error metadata
+   * @private
+   */
+  createError_(url, step, errorDescription) {
+    // For error case, fill all fields with error object.
+    const error =
+        new ContentMetadataProvider.Error(url, step, errorDescription);
+    const item = new MetadataItem();
+    item.contentImageTransformError = error;
+    item.contentThumbnailTransformError = error;
+    item.contentThumbnailUrlError = error;
+    return item;
+  }
 }
 
 /**
- * @const {!Array<string>}
+ * Content metadata provider error.
  */
+ContentMetadataProvider.Error = class extends Error {
+  /**
+   * @param {string} url File Entry.
+   * @param {string} step Step failed.
+   * @param {string} errorDescription Error description.
+   */
+  constructor(url, step, errorDescription) {
+    super(errorDescription);
+
+    /** @public @const {string} */
+    this.url = url;
+
+    /** @public @const {string} */
+    this.step = step;
+
+    /** @public @const {string} */
+    this.errorDescription = errorDescription;
+  }
+};
+
+/** @public @const {!Array<string>} */
 ContentMetadataProvider.PROPERTY_NAMES = [
   'contentImageTransform',
   'contentThumbnailTransform',
@@ -68,336 +381,8 @@
 
 /**
  * Path of a worker script.
- * @public {string}
+ * @public @const {string}
  */
 ContentMetadataProvider.WORKER_SCRIPT =
     'chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/' +
     'foreground/js/metadata/metadata_dispatcher.js';
-
-/**
- * Converts content metadata from parsers to the internal format.
- * @param {Object} metadata The content metadata.
- * @return {!MetadataItem} Converted metadata.
- */
-ContentMetadataProvider.convertContentMetadata = metadata => {
-  const item = new MetadataItem();
-  item.contentImageTransform = metadata['imageTransform'];
-  item.contentThumbnailTransform = metadata['thumbnailTransform'];
-  item.contentThumbnailUrl = metadata['thumbnailURL'];
-  item.exifLittleEndian = metadata['littleEndian'];
-  item.ifd = metadata['ifd'];
-  item.imageHeight = metadata['height'];
-  item.imageWidth = metadata['width'];
-  item.mediaMimeType = metadata['mimeType'];
-  return item;
-};
-
-ContentMetadataProvider.prototype.__proto__ = MetadataProvider.prototype;
-
-/**
- * @override
- */
-ContentMetadataProvider.prototype.get = function(requests) {
-  if (!requests.length) {
-    return Promise.resolve([]);
-  }
-
-  const promises = [];
-  for (let i = 0; i < requests.length; i++) {
-    promises.push(new Promise(((request, fulfill) => {
-                                this.getImpl_(
-                                    request.entry, request.names, fulfill);
-                              }).bind(null, requests[i])));
-  }
-  return Promise.all(promises);
-};
-
-/**
- * Fetches the metadata.
- * @param {!Entry} entry File entry.
- * @param {!Array<string>} names Requested metadata type.
- * @param {function(!MetadataItem)} callback Callback expects metadata value.
- *     This callback is called asynchronously.
- * @private
- */
-ContentMetadataProvider.prototype.getImpl_ = function(entry, names, callback) {
-  if (entry.isDirectory) {
-    setTimeout(
-        callback.bind(
-            null,
-            this.createError_(
-                entry.toURL(), 'get',
-                'we don\'t generate thumbnails for directory')),
-        0);
-    return;
-  }
-  // TODO(ryoh): mediaGalleries API does not handle
-  // image metadata correctly.
-  // We parse it in our pure js parser.
-  // chrome/browser/media_galleries/fileapi/supported_image_type_validator.cc
-  const type = FileType.getType(entry);
-  if (type && type.type === 'image') {
-    const url = entry.toURL();
-    if (this.callbacks_[url]) {
-      this.callbacks_[url].push(callback);
-    } else {
-      this.callbacks_[url] = [callback];
-      this.dispatcher_.postMessage({verb: 'request', arguments: [url]});
-    }
-    return;
-  }
-  this.getFromMediaGalleries_(entry, names).then(callback);
-};
-
-/**
- * Gets a metadata from mediaGalleries API
- *
- * @param {!Entry} entry File entry.
- * @param {!Array<string>} names Requested metadata type.
- * @return {!Promise<!MetadataItem>}  Promise that resolves with the metadata of
- *    the entry.
- * @private
- */
-ContentMetadataProvider.prototype.getFromMediaGalleries_ = function(
-    entry, names) {
-  const self = this;
-  return new Promise((resolve, reject) => {
-    entry.file(
-        blob => {
-          let metadataType = 'mimeTypeOnly';
-          if (names.indexOf('mediaArtist') !== -1 ||
-              names.indexOf('mediaTitle') !== -1 ||
-              names.indexOf('mediaTrack') !== -1 ||
-              names.indexOf('mediaYearRecorded') !== -1) {
-            metadataType = 'mimeTypeAndTags';
-          }
-          if (names.indexOf('contentThumbnailUrl') !== -1) {
-            metadataType = 'all';
-          }
-          chrome.mediaGalleries.getMetadata(
-              blob, {metadataType: metadataType}, metadata => {
-                if (chrome.runtime.lastError) {
-                  resolve(self.createError_(
-                      entry.toURL(), 'resolving metadata',
-                      chrome.runtime.lastError.toString()));
-                } else {
-                  self.convertMediaMetadataToMetadataItem_(entry, metadata)
-                      .then(resolve, reject);
-                }
-              });
-        },
-        err => {
-          resolve(self.createError_(
-              entry.toURL(), 'loading file entry',
-              'failed to open file entry'));
-        });
-  });
-};
-
-/**
- * Dispatches a message from a metadata reader to the appropriate on* method.
- * @param {Object} event The event.
- * @private
- */
-ContentMetadataProvider.prototype.onMessage_ = function(event) {
-  const data = event.data;
-  switch (data.verb) {
-    case 'initialized':
-      this.onInitialized_(data.arguments[0]);
-      break;
-    case 'result':
-      this.onResult_(
-          data.arguments[0],
-          data.arguments[1] ? ContentMetadataProvider.convertContentMetadata(
-                                  data.arguments[1]) :
-                              new MetadataItem());
-      break;
-    case 'error':
-      const error = this.createError_(
-          data.arguments[0], data.arguments[1], data.arguments[2]);
-      this.onResult_(data.arguments[0], error);
-      break;
-    case 'log':
-      this.onLog_(data.arguments[0]);
-      break;
-    default:
-      assertNotReached();
-      break;
-  }
-};
-
-/**
- * Handles the 'initialized' message from the metadata reader Worker.
- * @param {RegExp} regexp Regexp of supported urls.
- * @private
- */
-ContentMetadataProvider.prototype.onInitialized_ = function(regexp) {
-  this.urlFilter_ = regexp;
-
-  // Tests can monitor for this state with
-  // ExtensionTestMessageListener listener("worker-initialized");
-  // ASSERT_TRUE(listener.WaitUntilSatisfied());
-  // Automated tests need to wait for this, otherwise we crash in
-  // browser_test cleanup because the worker process still has
-  // URL requests in-flight.
-  util.testSendMessage('worker-initialized');
-  this.initialized_ = true;
-};
-
-/**
- * Handles the 'result' message from the worker.
- * @param {string} url File url.
- * @param {!MetadataItem} metadataItem The metadata item.
- * @private
- */
-ContentMetadataProvider.prototype.onResult_ = function(url, metadataItem) {
-  const callbacks = this.callbacks_[url];
-  delete this.callbacks_[url];
-  for (let i = 0; i < callbacks.length; i++) {
-    callbacks[i](metadataItem);
-  }
-};
-
-/**
- * Handles the 'log' message from the worker.
- * @param {Array<*>} arglist Log arguments.
- * @private
- */
-ContentMetadataProvider.prototype.onLog_ = arglist => {
-  console.log.apply(console, ['ContentMetadataProvider log:'].concat(arglist));
-};
-
-/**
- * Dispatches a message from MediaGalleries API to the appropriate on* method.
- * @param {!Entry} entry File entry.
- * @param {!Object} metadata The metadata from MediaGalleries API.
- * @return {!Promise<!MetadataItem>}  Promise that resolves with
- *    converted metadata item.
- * @private
- */
-ContentMetadataProvider.prototype.convertMediaMetadataToMetadataItem_ =
-    function(entry, metadata) {
-  return new Promise((resolve, reject) => {
-    if (!metadata) {
-      resolve(this.createError_(
-          entry.toURL(), 'Reading a thumbnail image',
-          'Failed to parse metadata'));
-      return;
-    }
-    const item = new MetadataItem();
-    const mimeType = metadata['mimeType'];
-    item.contentMimeType = mimeType;
-    item.mediaMimeType = mimeType;
-    const trans = {scaleX: 1, scaleY: 1, rotate90: 0};
-    if (metadata.rotation) {
-      switch (metadata.rotation) {
-        case 0:
-          break;
-        case 90:
-          trans.rotate90 = 1;
-          break;
-        case 180:
-          trans.scaleX *= -1;
-          trans.scaleY *= -1;
-          break;
-        case 270:
-          trans.rotate90 = 1;
-          trans.scaleX *= -1;
-          trans.scaleY *= -1;
-          break;
-        default:
-          console.error('Unknown rotation angle: ', metadata.rotation);
-      }
-    }
-    if (metadata.rotation) {
-      item.contentImageTransform = item.contentThumbnailTransform = trans;
-    }
-    item.imageHeight = metadata['height'];
-    item.imageWidth = metadata['width'];
-    item.mediaAlbum = metadata['album'];
-    item.mediaArtist = metadata['artist'];
-    item.mediaDuration = metadata['duration'];
-    item.mediaGenre = metadata['genre'];
-    item.mediaTitle = metadata['title'];
-    if (metadata['track']) {
-      item.mediaTrack = '' + metadata['track'];
-    }
-    if (metadata.rawTags) {
-      metadata.rawTags.forEach(entry => {
-        if (entry.type === 'mp3') {
-          if (entry.tags['date']) {
-            item.mediaYearRecorded = entry.tags['date'];
-          }
-          // It is possible that metadata['track'] is undefined but this is
-          // defined.
-          if (entry.tags['track']) {
-            item.mediaTrack = entry.tags['track'];
-          }
-        }
-      });
-    }
-    if (metadata.attachedImages && metadata.attachedImages.length > 0) {
-      const reader = new FileReader();
-      reader.onload = e => {
-        item.contentThumbnailUrl = e.target.result;
-        resolve(item);
-      };
-      reader.onerror = e => {
-        resolve(this.createError_(
-            entry.toURL(), 'Reading a thumbnail image',
-            reader.error.toString()));
-      };
-      reader.readAsDataURL(metadata.attachedImages[0]);
-    } else {
-      resolve(item);
-    }
-  });
-};
-
-/**
- * Handles the 'error' message from the worker.
- * @param {string} url File entry.
- * @param {string} step Step failed.
- * @param {string} errorDescription Error description.
- * @return {!MetadataItem} Error metadata
- * @private
- */
-ContentMetadataProvider.prototype.createError_ =
-    (url, step, errorDescription) => {
-      // For error case, fill all fields with error object.
-      const error =
-          new ContentMetadataProvider.Error(url, step, errorDescription);
-      const item = new MetadataItem();
-      item.contentImageTransformError = error;
-      item.contentThumbnailTransformError = error;
-      item.contentThumbnailUrlError = error;
-      return item;
-    };
-
-/**
- * Content metadata provider error.
- * @param {string} url File Entry.
- * @param {string} step Step failed.
- * @param {string} errorDescription Error description.
- * @constructor
- * @struct
- * @extends {Error}
- */
-ContentMetadataProvider.Error = function(url, step, errorDescription) {
-  /**
-   * @public {string}
-   */
-  this.url = url;
-
-  /**
-   * @public {string}
-   */
-  this.step = step;
-
-  /**
-   * @public {string}
-   */
-  this.errorDescription = errorDescription;
-};
-
-ContentMetadataProvider.Error.prototype.__proto__ = Error.prototype;
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/exif_parser_unittest.js b/ui/file_manager/file_manager/foreground/js/metadata/exif_parser_unittest.js
index 8fd5385..2f66c58 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/exif_parser_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/exif_parser_unittest.js
@@ -65,18 +65,34 @@
 }
 
 /**
+ * @implements {MetadataParserLogger}
+ * @final
+ */
+class ConsoleLogger {
+  constructor() {
+    this.verbose = true;
+  }
+
+  error(arg) {
+    console.error(arg);
+  }
+
+  log(arg) {
+    console.log(arg);
+  }
+
+  vlog(arg) {
+    console.log(arg);
+  }
+}
+
+/**
  * Parses exif data bytes (with logging) and returns the parsed tags.
  * @param {!TypedArray} bytes Bytes to be read.
  * @return {!Object<!Exif.Tag, !ExifEntry>} Tags.
  */
 function parseExifData_(bytes) {
-  let exifParser = new ExifParser(this);
-  exifParser.log = arg => {
-    console.log(arg);
-  };
-  exifParser.vlog = arg => {
-    console.log(arg);
-  };
+  const exifParser = new ExifParser(new ConsoleLogger());
 
   let tags = {};
   let byteReader = new ByteReader(bytes.buffer);
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider.js b/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider.js
index 8f89ae17..da7d0e88 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider.js
@@ -5,18 +5,148 @@
 /**
  * Metadata provider for FileEntry#getMetadata.
  * TODO(hirono): Rename thumbnailUrl with externalThumbnailUrl.
- *
- * @constructor
- * @extends {MetadataProvider}
- * @struct
+ * @final
  */
-function ExternalMetadataProvider() {
-  MetadataProvider.call(this, ExternalMetadataProvider.PROPERTY_NAMES);
+class ExternalMetadataProvider extends MetadataProvider {
+  constructor() {
+    super(ExternalMetadataProvider.PROPERTY_NAMES);
+  }
+
+  /** @override */
+  get(requests) {
+    if (!requests.length) {
+      return Promise.resolve([]);
+    }
+    return new Promise(fulfill => {
+      const entries = requests.map(request => {
+        return request.entry;
+      });
+      const nameMap = {};
+      for (let i = 0; i < requests.length; i++) {
+        for (let j = 0; j < requests[i].names.length; j++) {
+          nameMap[requests[i].names[j]] = true;
+        }
+      }
+      chrome.fileManagerPrivate.getEntryProperties(
+          entries, Object.keys(nameMap), results => {
+            if (!chrome.runtime.lastError) {
+              fulfill(this.convertResults_(requests, nameMap, assert(results)));
+            } else {
+              fulfill(requests.map(() => {
+                return new MetadataItem();
+              }));
+            }
+          });
+    });
+  }
+
+  /**
+   * @param {!Array<!MetadataRequest>} requests
+   * @param {!Object<boolean>} nameMap A map of property names that will be used
+   *     to copy the value from |propertiesList|.
+   * @param {!Array<!chrome.fileManagerPrivate.EntryProperties>} propertiesList
+   * @return {!Array<!MetadataItem>}
+   */
+  convertResults_(requests, nameMap, propertiesList) {
+    const results = [];
+    for (let i = 0; i < propertiesList.length; i++) {
+      const prop = propertiesList[i];
+      const item = new MetadataItem();
+      if (prop.alternateUrl !== undefined || nameMap['alternateUrl']) {
+        item.alternateUrl = prop.alternateUrl;
+      }
+      if (prop.availableOffline !== undefined || nameMap['availableOffline']) {
+        item.availableOffline = prop.availableOffline;
+      }
+      if (prop.availableWhenMetered !== undefined ||
+          nameMap['availableWhenMetered']) {
+        item.availableWhenMetered = prop.availableWhenMetered;
+      }
+      if (prop.contentMimeType !== undefined || nameMap['contentMimeType']) {
+        item.contentMimeType = prop.contentMimeType || '';
+      }
+      if (prop.croppedThumbnailUrl !== undefined ||
+          nameMap['croppedThumbnailUrl']) {
+        item.croppedThumbnailUrl = prop.croppedThumbnailUrl;
+      }
+      if (prop.customIconUrl !== undefined || nameMap['customIconUrl']) {
+        item.customIconUrl = prop.customIconUrl || '';
+      }
+      if (prop.dirty !== undefined || nameMap['dirty']) {
+        item.dirty = prop.dirty;
+      }
+      if (prop.externalFileUrl !== undefined || nameMap['externalFileUrl']) {
+        item.externalFileUrl = prop.externalFileUrl;
+      }
+      if (prop.hosted !== undefined || nameMap['hosted']) {
+        item.hosted = prop.hosted;
+      }
+      if (prop.imageHeight !== undefined || nameMap['imageHeight']) {
+        item.imageHeight = prop.imageHeight;
+      }
+      if (prop.imageRotation !== undefined || nameMap['imageRotation']) {
+        item.imageRotation = prop.imageRotation;
+      }
+      if (prop.imageWidth !== undefined || nameMap['imageWidth']) {
+        item.imageWidth = prop.imageWidth;
+      }
+      if (prop.modificationTime !== undefined || nameMap['modificationTime']) {
+        item.modificationTime = new Date(prop.modificationTime);
+      }
+      if (prop.modificationByMeTime !== undefined ||
+          nameMap['modificationByMeTime']) {
+        item.modificationByMeTime = new Date(prop.modificationByMeTime);
+      }
+      if (prop.pinned !== undefined || nameMap['pinned']) {
+        item.pinned = prop.pinned;
+      }
+      if (prop.present !== undefined || nameMap['present']) {
+        item.present = prop.present;
+      }
+      if (prop.shared !== undefined || nameMap['shared']) {
+        item.shared = prop.shared;
+      }
+      if (prop.sharedWithMe !== undefined || nameMap['sharedWithMe']) {
+        item.sharedWithMe = prop.sharedWithMe;
+      }
+      if (prop.size !== undefined || nameMap['size']) {
+        item.size = requests[i].entry.isFile ? (prop.size || 0) : -1;
+      }
+      if (prop.thumbnailUrl !== undefined || nameMap['thumbnailUrl']) {
+        item.thumbnailUrl = prop.thumbnailUrl;
+      }
+      if (prop.canCopy !== undefined || nameMap['canCopy']) {
+        item.canCopy = prop.canCopy;
+      }
+      if (prop.canDelete !== undefined || nameMap['canDelete']) {
+        item.canDelete = prop.canDelete;
+      }
+      if (prop.canRename !== undefined || nameMap['canRename']) {
+        item.canRename = prop.canRename;
+      }
+      if (prop.canAddChildren !== undefined || nameMap['canAddChildren']) {
+        item.canAddChildren = prop.canAddChildren;
+      }
+      if (prop.canShare !== undefined || nameMap['canShare']) {
+        item.canShare = prop.canShare;
+      }
+      if (prop.isMachineRoot !== undefined || nameMap['isMachineRoot']) {
+        item.isMachineRoot = prop.isMachineRoot;
+      }
+      if (prop.isExternalMedia !== undefined || nameMap['isExternalMedia']) {
+        item.isExternalMedia = prop.isExternalMedia;
+      }
+      if (prop.isArbitrarySyncFolder !== undefined ||
+          nameMap['isArbitrarySyncFolder']) {
+        item.isArbitrarySyncFolder = prop.isArbitrarySyncFolder;
+      }
+      results.push(item);
+    }
+    return results;
+  }
 }
 
-/**
- * @const {!Array<string>}
- */
+/** @const {!Array<string>} */
 ExternalMetadataProvider.PROPERTY_NAMES = [
   'alternateUrl',
   'availableOffline',
@@ -47,143 +177,3 @@
   'isExternalMedia',
   'isArbitrarySyncFolder',
 ];
-
-ExternalMetadataProvider.prototype.__proto__ = MetadataProvider.prototype;
-
-/**
- * @override
- */
-ExternalMetadataProvider.prototype.get = function(requests) {
-  if (!requests.length) {
-    return Promise.resolve([]);
-  }
-  return new Promise(fulfill => {
-    const entries = requests.map(request => {
-      return request.entry;
-    });
-    const nameMap = {};
-    for (let i = 0; i < requests.length; i++) {
-      for (let j = 0; j < requests[i].names.length; j++) {
-        nameMap[requests[i].names[j]] = true;
-      }
-    }
-    chrome.fileManagerPrivate.getEntryProperties(
-        entries, Object.keys(nameMap), results => {
-          if (!chrome.runtime.lastError) {
-            fulfill(this.convertResults_(requests, nameMap, assert(results)));
-          } else {
-            fulfill(requests.map(() => {
-              return new MetadataItem();
-            }));
-          }
-        });
-  });
-};
-
-/**
- * @param {!Array<!MetadataRequest>} requests
- * @param {!Object<boolean>} nameMap A map of property names that will be used
- *     to copy the value from |propertiesList|.
- * @param {!Array<!chrome.fileManagerPrivate.EntryProperties>} propertiesList
- * @return {!Array<!MetadataItem>}
- */
-ExternalMetadataProvider.prototype.convertResults_ =
-    (requests, nameMap, propertiesList) => {
-      const results = [];
-      for (let i = 0; i < propertiesList.length; i++) {
-        const prop = propertiesList[i];
-        const item = new MetadataItem();
-        if (prop.alternateUrl !== undefined || nameMap['alternateUrl']) {
-          item.alternateUrl = prop.alternateUrl;
-        }
-        if (prop.availableOffline !== undefined ||
-            nameMap['availableOffline']) {
-          item.availableOffline = prop.availableOffline;
-        }
-        if (prop.availableWhenMetered !== undefined ||
-            nameMap['availableWhenMetered']) {
-          item.availableWhenMetered = prop.availableWhenMetered;
-        }
-        if (prop.contentMimeType !== undefined || nameMap['contentMimeType']) {
-          item.contentMimeType = prop.contentMimeType || '';
-        }
-        if (prop.croppedThumbnailUrl !== undefined ||
-            nameMap['croppedThumbnailUrl']) {
-          item.croppedThumbnailUrl = prop.croppedThumbnailUrl;
-        }
-        if (prop.customIconUrl !== undefined || nameMap['customIconUrl']) {
-          item.customIconUrl = prop.customIconUrl || '';
-        }
-        if (prop.dirty !== undefined || nameMap['dirty']) {
-          item.dirty = prop.dirty;
-        }
-        if (prop.externalFileUrl !== undefined || nameMap['externalFileUrl']) {
-          item.externalFileUrl = prop.externalFileUrl;
-        }
-        if (prop.hosted !== undefined || nameMap['hosted']) {
-          item.hosted = prop.hosted;
-        }
-        if (prop.imageHeight !== undefined || nameMap['imageHeight']) {
-          item.imageHeight = prop.imageHeight;
-        }
-        if (prop.imageRotation !== undefined || nameMap['imageRotation']) {
-          item.imageRotation = prop.imageRotation;
-        }
-        if (prop.imageWidth !== undefined || nameMap['imageWidth']) {
-          item.imageWidth = prop.imageWidth;
-        }
-        if (prop.modificationTime !== undefined ||
-            nameMap['modificationTime']) {
-          item.modificationTime = new Date(prop.modificationTime);
-        }
-        if (prop.modificationByMeTime !== undefined ||
-            nameMap['modificationByMeTime']) {
-          item.modificationByMeTime = new Date(prop.modificationByMeTime);
-        }
-        if (prop.pinned !== undefined || nameMap['pinned']) {
-          item.pinned = prop.pinned;
-        }
-        if (prop.present !== undefined || nameMap['present']) {
-          item.present = prop.present;
-        }
-        if (prop.shared !== undefined || nameMap['shared']) {
-          item.shared = prop.shared;
-        }
-        if (prop.sharedWithMe !== undefined || nameMap['sharedWithMe']) {
-          item.sharedWithMe = prop.sharedWithMe;
-        }
-        if (prop.size !== undefined || nameMap['size']) {
-          item.size = requests[i].entry.isFile ? (prop.size || 0) : -1;
-        }
-        if (prop.thumbnailUrl !== undefined || nameMap['thumbnailUrl']) {
-          item.thumbnailUrl = prop.thumbnailUrl;
-        }
-        if (prop.canCopy !== undefined || nameMap['canCopy']) {
-          item.canCopy = prop.canCopy;
-        }
-        if (prop.canDelete !== undefined || nameMap['canDelete']) {
-          item.canDelete = prop.canDelete;
-        }
-        if (prop.canRename !== undefined || nameMap['canRename']) {
-          item.canRename = prop.canRename;
-        }
-        if (prop.canAddChildren !== undefined || nameMap['canAddChildren']) {
-          item.canAddChildren = prop.canAddChildren;
-        }
-        if (prop.canShare !== undefined || nameMap['canShare']) {
-          item.canShare = prop.canShare;
-        }
-        if (prop.isMachineRoot !== undefined || nameMap['isMachineRoot']) {
-          item.isMachineRoot = prop.isMachineRoot;
-        }
-        if (prop.isExternalMedia !== undefined || nameMap['isExternalMedia']) {
-          item.isExternalMedia = prop.isExternalMedia;
-        }
-        if (prop.isArbitrarySyncFolder !== undefined ||
-            nameMap['isArbitrarySyncFolder']) {
-          item.isArbitrarySyncFolder = prop.isArbitrarySyncFolder;
-        }
-        results.push(item);
-      }
-      return results;
-    };
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider.js b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider.js
index b4e07324..a97509a 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider.js
@@ -4,50 +4,43 @@
 
 /**
  * Metadata provider for FileEntry#getMetadata.
- *
- * @constructor
- * @extends {MetadataProvider}
- * @struct
+ * @final
  */
-function FileSystemMetadataProvider() {
-  MetadataProvider.call(this, FileSystemMetadataProvider.PROPERTY_NAMES);
+class FileSystemMetadataProvider extends MetadataProvider {
+  constructor() {
+    super(FileSystemMetadataProvider.PROPERTY_NAMES);
+  }
+
+  /** @override */
+  get(requests) {
+    if (!requests.length) {
+      return Promise.resolve([]);
+    }
+    return Promise.all(requests.map(request => {
+      return new Promise((fulfill, reject) => {
+               request.entry.getMetadata(fulfill, reject);
+             })
+          .then(
+              result => {
+                const item = new MetadataItem();
+                item.modificationTime = result.modificationTime;
+                item.size = request.entry.isDirectory ? -1 : result.size;
+                item.present = true;
+                item.availableOffline = true;
+                return item;
+              },
+              error => {
+                // Can't use console.error because some tests hit this line and
+                // console.error causes them to fail because of JSErrorCount.
+                // This error is an acceptable condition.
+                console.warn(
+                    'getMetadata failure for: ' + request.entry.toURL(), error);
+                return new MetadataItem();
+              });
+    }));
+  }
 }
 
-/**
- * @const {!Array<string>}
- */
+/** @const {!Array<string>} */
 FileSystemMetadataProvider.PROPERTY_NAMES =
     ['modificationTime', 'size', 'present', 'availableOffline'];
-
-FileSystemMetadataProvider.prototype.__proto__ = MetadataProvider.prototype;
-
-/**
- * @override
- */
-FileSystemMetadataProvider.prototype.get = requests => {
-  if (!requests.length) {
-    return Promise.resolve([]);
-  }
-  return Promise.all(requests.map(request => {
-    return new Promise((fulfill, reject) => {
-             request.entry.getMetadata(fulfill, reject);
-           })
-        .then(
-            result => {
-              const item = new MetadataItem();
-              item.modificationTime = result.modificationTime;
-              item.size = request.entry.isDirectory ? -1 : result.size;
-              item.present = true;
-              item.availableOffline = true;
-              return item;
-            },
-            error => {
-              // Can't use console.error because some tests hit this line and
-              // console.error causes them to fail because of JSErrorCount. This
-              // error is an acceptable condition.
-              console.warn(
-                  'getMetadata failure for: ' + request.entry.toURL(), error);
-              return new MetadataItem();
-            });
-  }));
-};
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/id3_parser.js b/ui/file_manager/file_manager/foreground/js/metadata/id3_parser.js
index 089429d..d44a6704 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/id3_parser.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/id3_parser.js
@@ -25,414 +25,411 @@
 
 /**
  * ID3 parser.
- *
- * @param {MetadataParserLogger} parent A metadata dispatcher.
- * @constructor
- * @struct
- * @extends {MetadataParser}
+ * @final
  */
-function Id3Parser(parent) {
-  MetadataParser.call(this, parent, 'id3', /\.(mp3)$/i);
-}
-
-Id3Parser.prototype.__proto__ = MetadataParser.prototype;
-
-/**
- * Reads synchsafe integer.
- * 'SynchSafe' term is taken from id3 documentation.
- *
- * @param {ByteReader} reader Reader to use.
- * @param {number} length Rytes to read.
- * @return {number} Synchsafe value.
- * @private
- */
-Id3Parser.readSynchSafe_ = (reader, length) => {
-  let rv = 0;
-
-  switch (length) {
-    case 4:
-      rv = reader.readScalar(1, false) << 21;
-    case 3:
-      rv |= reader.readScalar(1, false) << 14;
-    case 2:
-      rv |= reader.readScalar(1, false) << 7;
-    case 1:
-      rv |= reader.readScalar(1, false);
+class Id3Parser extends MetadataParser {
+  /**
+   * @param {!MetadataParserLogger} parent A metadata dispatcher.
+   */
+  constructor(parent) {
+    super(parent, 'id3', /\.(mp3)$/i);
   }
 
-  return rv;
-};
+  /**
+   * Reads synchsafe integer.
+   * 'SynchSafe' term is taken from id3 documentation.
+   *
+   * @param {ByteReader} reader Reader to use.
+   * @param {number} length Rytes to read.
+   * @return {number} Synchsafe value.
+   * @private
+   */
+  static readSynchSafe_(reader, length) {
+    let rv = 0;
 
-/**
- * Reads 3bytes integer.
- *
- * @param {ByteReader} reader Reader to use.
- * @return {number} Uint24 value.
- * @private
- */
-Id3Parser.readUInt24_ = reader => {
-  return reader.readScalar(2, false) << 16 | reader.readScalar(1, false);
-};
+    switch (length) {
+      case 4:
+        rv = reader.readScalar(1, false) << 21;
+      case 3:
+        rv |= reader.readScalar(1, false) << 14;
+      case 2:
+        rv |= reader.readScalar(1, false) << 7;
+      case 1:
+        rv |= reader.readScalar(1, false);
+    }
 
-/**
- * Reads string from reader with specified encoding
- *
- * @param {ByteReader} reader Reader to use.
- * @param {number} encoding String encoding.
- * @param {number} size Maximum string size. Actual result may be shorter.
- * @return {string} String value.
- * @private
- */
-Id3Parser.prototype.readString_ = function(reader, encoding, size) {
-  switch (encoding) {
-    case Id3Parser.v2.ENCODING.ISO_8859_1:
-      return reader.readNullTerminatedString(size);
+    return rv;
+  }
 
-    case Id3Parser.v2.ENCODING.UTF_16:
-      return reader.readNullTerminatedStringUTF16(true, size);
+  /**
+   * Reads 3bytes integer.
+   *
+   * @param {ByteReader} reader Reader to use.
+   * @return {number} Uint24 value.
+   * @private
+   */
+  static readUInt24_(reader) {
+    return reader.readScalar(2, false) << 16 | reader.readScalar(1, false);
+  }
 
-    case Id3Parser.v2.ENCODING.UTF_16BE:
-      return reader.readNullTerminatedStringUTF16(false, size);
+  /**
+   * Reads string from reader with specified encoding
+   *
+   * @param {ByteReader} reader Reader to use.
+   * @param {number} encoding String encoding.
+   * @param {number} size Maximum string size. Actual result may be shorter.
+   * @return {string} String value.
+   * @private
+   */
+  readString_(reader, encoding, size) {
+    switch (encoding) {
+      case Id3Parser.v2.ENCODING.ISO_8859_1:
+        return reader.readNullTerminatedString(size);
 
-    case Id3Parser.v2.ENCODING.UTF_8:
-      // TODO: implement UTF_8.
-      this.log('UTF8 encoding not supported, used ISO_8859_1 instead');
-      return reader.readNullTerminatedString(size);
+      case Id3Parser.v2.ENCODING.UTF_16:
+        return reader.readNullTerminatedStringUTF16(true, size);
 
-    default: {
-      this.log('Unsupported encoding in ID3 tag: ' + encoding);
-      return '';
+      case Id3Parser.v2.ENCODING.UTF_16BE:
+        return reader.readNullTerminatedStringUTF16(false, size);
+
+      case Id3Parser.v2.ENCODING.UTF_8:
+        // TODO: implement UTF_8.
+        this.log('UTF8 encoding not supported, used ISO_8859_1 instead');
+        return reader.readNullTerminatedString(size);
+
+      default: {
+        this.log('Unsupported encoding in ID3 tag: ' + encoding);
+        return '';
+      }
     }
   }
-};
 
-/**
- * Reads text frame from reader.
- *
- * @param {ByteReader} reader Reader to use.
- * @param {number} majorVersion Major id3 version to use.
- * @param {Object} frame Frame so store data at.
- * @param {number} end Frame end position in reader.
- * @private
- */
-Id3Parser.prototype.readTextFrame_ = function(
-    reader, majorVersion, frame, end) {
-  frame.encoding = reader.readScalar(1, false, end);
-  frame.value = this.readString_(reader, frame.encoding, end - reader.tell());
-};
-
-/**
- * Reads user defined text frame from reader.
- *
- * @param {ByteReader} reader Reader to use.
- * @param {number} majorVersion Major id3 version to use.
- * @param {Object} frame Frame so store data at.
- * @param {number} end Frame end position in reader.
- * @private
- */
-Id3Parser.prototype.readUserDefinedTextFrame_ = function(
-    reader, majorVersion, frame, end) {
-  frame.encoding = reader.readScalar(1, false, end);
-
-  frame.description =
-      this.readString_(reader, frame.encoding, end - reader.tell());
-
-  frame.value = this.readString_(reader, frame.encoding, end - reader.tell());
-};
-
-/**
- * @param {ByteReader} reader Reader to use.
- * @param {number} majorVersion Major id3 version to use.
- * @param {Object} frame Frame so store data at.
- * @param {number} end Frame end position in reader.
- * @private
- */
-Id3Parser.prototype.readPIC_ = function(reader, majorVersion, frame, end) {
-  frame.encoding = reader.readScalar(1, false, end);
-  frame.format = reader.readNullTerminatedString(3, end - reader.tell());
-  frame.pictureType = reader.readScalar(1, false, end);
-  frame.description =
-      this.readString_(reader, frame.encoding, end - reader.tell());
-
-
-  if (frame.format == '-->') {
-    frame.imageUrl = reader.readNullTerminatedString(end - reader.tell());
-  } else {
-    frame.imageUrl = reader.readImage(end - reader.tell());
-  }
-};
-
-/**
- * @param {ByteReader} reader Reader to use.
- * @param {number} majorVersion Major id3 version to use.
- * @param {Object} frame Frame so store data at.
- * @param {number} end Frame end position in reader.
- * @private
- */
-Id3Parser.prototype.readAPIC_ = function(reader, majorVersion, frame, end) {
-  this.vlog('Extracting picture');
-  frame.encoding = reader.readScalar(1, false, end);
-  frame.mime = reader.readNullTerminatedString(end - reader.tell());
-  frame.pictureType = reader.readScalar(1, false, end);
-  frame.description =
-      this.readString_(reader, frame.encoding, end - reader.tell());
-
-  if (frame.mime == '-->') {
-    frame.imageUrl = reader.readNullTerminatedString(end - reader.tell());
-  } else {
-    frame.imageUrl = reader.readImage(end - reader.tell());
-  }
-};
-
-/**
- * Reads string from reader with specified encoding
- *
- * @param {ByteReader} reader Reader to use.
- * @param {number} majorVersion Major id3 version to use.
- * @return {Object} Frame read.
- * @private
- */
-Id3Parser.prototype.readFrame_ = function(reader, majorVersion) {
-  if (reader.eof()) {
-    return null;
+  /**
+   * Reads text frame from reader.
+   *
+   * @param {ByteReader} reader Reader to use.
+   * @param {number} majorVersion Major id3 version to use.
+   * @param {Object} frame Frame so store data at.
+   * @param {number} end Frame end position in reader.
+   * @private
+   */
+  readTextFrame_(reader, majorVersion, frame, end) {
+    frame.encoding = reader.readScalar(1, false, end);
+    frame.value = this.readString_(reader, frame.encoding, end - reader.tell());
   }
 
-  const frame = {};
+  /**
+   * Reads user defined text frame from reader.
+   *
+   * @param {ByteReader} reader Reader to use.
+   * @param {number} majorVersion Major id3 version to use.
+   * @param {Object} frame Frame so store data at.
+   * @param {number} end Frame end position in reader.
+   * @private
+   */
+  readUserDefinedTextFrame_(reader, majorVersion, frame, end) {
+    frame.encoding = reader.readScalar(1, false, end);
 
-  reader.pushSeek(reader.tell(), ByteReader.SEEK_BEG);
+    frame.description =
+        this.readString_(reader, frame.encoding, end - reader.tell());
 
-  const position = reader.tell();
-
-  frame.name = (majorVersion == 2) ? reader.readNullTerminatedString(3) :
-                                     reader.readNullTerminatedString(4);
-
-  if (frame.name == '') {
-    return null;
+    frame.value = this.readString_(reader, frame.encoding, end - reader.tell());
   }
 
-  this.vlog('Found frame ' + (frame.name) + ' at position ' + position);
+  /**
+   * @param {ByteReader} reader Reader to use.
+   * @param {number} majorVersion Major id3 version to use.
+   * @param {Object} frame Frame so store data at.
+   * @param {number} end Frame end position in reader.
+   * @private
+   */
+  readPIC_(reader, majorVersion, frame, end) {
+    frame.encoding = reader.readScalar(1, false, end);
+    frame.format = reader.readNullTerminatedString(3, end - reader.tell());
+    frame.pictureType = reader.readScalar(1, false, end);
+    frame.description =
+        this.readString_(reader, frame.encoding, end - reader.tell());
 
-  switch (majorVersion) {
-    case 2:
-      frame.size = Id3Parser.readUInt24_(reader);
-      frame.headerSize = 6;
-      break;
-    case 3:
-      frame.size = reader.readScalar(4, false);
-      frame.headerSize = 10;
-      frame.flags = reader.readScalar(2, false);
-      break;
-    case 4:
-      frame.size = Id3Parser.readSynchSafe_(reader, 4);
-      frame.headerSize = 10;
-      frame.flags = reader.readScalar(2, false);
-      break;
+
+    if (frame.format == '-->') {
+      frame.imageUrl = reader.readNullTerminatedString(end - reader.tell());
+    } else {
+      frame.imageUrl = reader.readImage(end - reader.tell());
+    }
   }
 
-  this.vlog('Found frame [' + frame.name + '] with size [' + frame.size + ']');
+  /**
+   * @param {ByteReader} reader Reader to use.
+   * @param {number} majorVersion Major id3 version to use.
+   * @param {Object} frame Frame so store data at.
+   * @param {number} end Frame end position in reader.
+   * @private
+   */
+  readAPIC_(reader, majorVersion, frame, end) {
+    this.vlog('Extracting picture');
+    frame.encoding = reader.readScalar(1, false, end);
+    frame.mime = reader.readNullTerminatedString(end - reader.tell());
+    frame.pictureType = reader.readScalar(1, false, end);
+    frame.description =
+        this.readString_(reader, frame.encoding, end - reader.tell());
 
-  if (Id3Parser.v2.HANDLERS[frame.name]) {
-    Id3Parser.v2.HANDLERS[frame.name].call(
-        this, reader, majorVersion, frame, reader.tell() + frame.size);
-  } else if (frame.name.charAt(0) == 'T' || frame.name.charAt(0) == 'W') {
-    this.readTextFrame_(
-        reader, majorVersion, frame, reader.tell() + frame.size);
+    if (frame.mime == '-->') {
+      frame.imageUrl = reader.readNullTerminatedString(end - reader.tell());
+    } else {
+      frame.imageUrl = reader.readImage(end - reader.tell());
+    }
   }
 
-  reader.popSeek();
+  /**
+   * Reads string from reader with specified encoding
+   *
+   * @param {ByteReader} reader Reader to use.
+   * @param {number} majorVersion Major id3 version to use.
+   * @return {Object} Frame read.
+   * @private
+   */
+  readFrame_(reader, majorVersion) {
+    if (reader.eof()) {
+      return null;
+    }
 
-  reader.seek(frame.size + frame.headerSize, ByteReader.SEEK_CUR);
+    const frame = {};
 
-  return frame;
-};
+    reader.pushSeek(reader.tell(), ByteReader.SEEK_BEG);
 
-/**
- * @param {File} file File object to parse.
- * @param {Object} metadata Metadata object of the file.
- * @param {function(Object)} callback Success callback.
- * @param {function(string)} onError Error callback.
- */
-Id3Parser.prototype.parse = function(file, metadata, callback, onError) {
-  const self = this;
+    const position = reader.tell();
 
-  this.log('Starting id3 parser for ' + file.name);
+    frame.name = (majorVersion == 2) ? reader.readNullTerminatedString(3) :
+                                       reader.readNullTerminatedString(4);
 
-  const id3v1Parser = new FunctionSequence(
-      'id3v1parser', [
-        /**
-         * Reads last 128 bytes of file in bytebuffer,
-         * which passes further.
-         * In last 128 bytes should be placed ID3v1 tag if available.
-         * @param {File} file File which bytes to read.
-         */
-        function readTail(file) {
-          MetadataParser.readFileBytes(
-              file, file.size - 128, file.size, this.nextStep, this.onError);
-        },
+    if (frame.name == '') {
+      return null;
+    }
 
-        /**
-         * Attempts to extract ID3v1 tag from 128 bytes long ByteBuffer
-         * @param {File} file File which tags are being extracted. Could be used
-         *     for logging purposes.
-         * @param {ByteReader} reader ByteReader of 128 bytes.
-         */
-        function extractId3v1(file, reader) {
-          if (reader.readString(3) == 'TAG') {
-            this.logger.vlog('id3v1 found');
-            const id3v1 = metadata.id3v1 = {};
+    this.vlog('Found frame ' + (frame.name) + ' at position ' + position);
 
-            const title = reader.readNullTerminatedString(30).trim();
+    switch (majorVersion) {
+      case 2:
+        frame.size = Id3Parser.readUInt24_(reader);
+        frame.headerSize = 6;
+        break;
+      case 3:
+        frame.size = reader.readScalar(4, false);
+        frame.headerSize = 10;
+        frame.flags = reader.readScalar(2, false);
+        break;
+      case 4:
+        frame.size = Id3Parser.readSynchSafe_(reader, 4);
+        frame.headerSize = 10;
+        frame.flags = reader.readScalar(2, false);
+        break;
+    }
 
-            if (title.length > 0) {
-              metadata.title = title;
-            }
+    this.vlog(
+        'Found frame [' + frame.name + '] with size [' + frame.size + ']');
 
-            reader.seek(3 + 30, ByteReader.SEEK_BEG);
+    if (Id3Parser.v2.HANDLERS[frame.name]) {
+      Id3Parser.v2.HANDLERS[frame.name].call(
+          this, reader, majorVersion, frame, reader.tell() + frame.size);
+    } else if (frame.name.charAt(0) == 'T' || frame.name.charAt(0) == 'W') {
+      this.readTextFrame_(
+          reader, majorVersion, frame, reader.tell() + frame.size);
+    }
 
-            const artist = reader.readNullTerminatedString(30).trim();
-            if (artist.length > 0) {
-              metadata.artist = artist;
-            }
+    reader.popSeek();
 
-            reader.seek(3 + 30 + 30, ByteReader.SEEK_BEG);
+    reader.seek(frame.size + frame.headerSize, ByteReader.SEEK_CUR);
 
-            const album = reader.readNullTerminatedString(30).trim();
-            if (album.length > 0) {
-              metadata.album = album;
-            }
-          }
-          this.nextStep();
-        }
-      ],
-      this, () => {}, error => {});
+    return frame;
+  }
 
-  const id3v2Parser = new FunctionSequence(
-      'id3v2parser', [
-        function readHead(file) {
-          MetadataParser.readFileBytes(
-              file, 0, 10, this.nextStep, this.onError);
-        },
+  /**
+   * @param {File} file File object to parse.
+   * @param {Object} metadata Metadata object of the file.
+   * @param {function(Object)} callback Success callback.
+   * @param {function(string)} onError Error callback.
+   */
+  parse(file, metadata, callback, onError) {
+    const self = this;
 
-        /**
-         * Check if passed array of 10 bytes contains ID3 header.
-         * @param {File} file File to check and continue reading if ID3
-         *     metadata found.
-         * @param {ByteReader} reader Reader to fill with stream bytes.
-         */
-        function checkId3v2(file, reader) {
-          if (reader.readString(3) == 'ID3') {
-            this.logger.vlog('id3v2 found');
-            const id3v2 = metadata.id3v2 = {};
-            id3v2.major = reader.readScalar(1, false);
-            id3v2.minor = reader.readScalar(1, false);
-            id3v2.flags = reader.readScalar(1, false);
-            id3v2.size = Id3Parser.readSynchSafe_(reader, 4);
+    this.log('Starting id3 parser for ' + file.name);
 
+    const id3v1Parser = new FunctionSequence(
+        'id3v1parser', [
+          /**
+           * Reads last 128 bytes of file in bytebuffer,
+           * which passes further.
+           * In last 128 bytes should be placed ID3v1 tag if available.
+           * @param {File} file File which bytes to read.
+           */
+          function readTail(file) {
             MetadataParser.readFileBytes(
-                file, 10, 10 + id3v2.size, this.nextStep, this.onError);
-          } else {
-            this.finish();
-          }
-        },
-
-        /**
-         * Extracts all ID3v2 frames from given bytebuffer.
-         * @param {File} file File being parsed.
-         * @param {ByteReader} reader Reader to use for metadata extraction.
-         */
-        function extractFrames(file, reader) {
-          const id3v2 = metadata.id3v2;
-
-          if ((id3v2.major > 2) &&
-              (id3v2.flags & Id3Parser.v2.FLAG_EXTENDED_HEADER != 0)) {
-            // Skip extended header if found
-            if (id3v2.major == 3) {
-              reader.seek(reader.readScalar(4, false) - 4);
-            } else if (id3v2.major == 4) {
-              reader.seek(Id3Parser.readSynchSafe_(reader, 4) - 4);
-            }
-          }
-
-          let frame;
-
-          while (frame = self.readFrame_(reader, id3v2.major)) {
-            metadata.id3v2[frame.name] = frame;
-          }
-
-          this.nextStep();
-        },
-
-        /**
-         * Adds 'description' object to metadata.
-         * 'description' used to unify different parsers and make
-         * metadata parser-aware.
-         * Description is array if value-type pairs. Type should be used
-         * to properly format value before displaying to user.
-         */
-        function prepareDescription() {
-          const id3v2 = metadata.id3v2;
-
-          if (id3v2['APIC']) {
-            metadata.thumbnailURL = id3v2['APIC'].imageUrl;
-          } else if (id3v2['PIC']) {
-            metadata.thumbnailURL = id3v2['PIC'].imageUrl;
-          }
-
-          metadata.description = [];
-
-          for (const key in id3v2) {
-            if (typeof (Id3Parser.v2.MAPPERS[key]) != 'undefined' &&
-                id3v2[key].value.trim().length > 0) {
-              metadata.description.push({
-                key: Id3Parser.v2.MAPPERS[key],
-                value: id3v2[key].value.trim()
-              });
-            }
-          }
+                file, file.size - 128, file.size, this.nextStep, this.onError);
+          },
 
           /**
-           * @param {string} propName
-           * @param {...string} tags
+           * Attempts to extract ID3v1 tag from 128 bytes long ByteBuffer
+           * @param {File} file File which tags are being extracted. Could be
+           *     used for logging purposes.
+           * @param {ByteReader} reader ByteReader of 128 bytes.
            */
-          function extract(propName, tags) {
-            for (let i = 1; i != arguments.length; i++) {
-              const tag = id3v2[arguments[i]];
-              if (tag && tag.value) {
-                metadata[propName] = tag.value;
-                break;
+          function extractId3v1(file, reader) {
+            if (reader.readString(3) == 'TAG') {
+              this.logger.vlog('id3v1 found');
+              const id3v1 = metadata.id3v1 = {};
+
+              const title = reader.readNullTerminatedString(30).trim();
+
+              if (title.length > 0) {
+                metadata.title = title;
+              }
+
+              reader.seek(3 + 30, ByteReader.SEEK_BEG);
+
+              const artist = reader.readNullTerminatedString(30).trim();
+              if (artist.length > 0) {
+                metadata.artist = artist;
+              }
+
+              reader.seek(3 + 30 + 30, ByteReader.SEEK_BEG);
+
+              const album = reader.readNullTerminatedString(30).trim();
+              if (album.length > 0) {
+                metadata.album = album;
               }
             }
+            this.nextStep();
           }
+        ],
+        this, () => {}, error => {});
 
-          extract('album', 'TALB', 'TAL');
-          extract('title', 'TIT2', 'TT2');
-          extract('artist', 'TPE1', 'TP1');
+    const id3v2Parser = new FunctionSequence(
+        'id3v2parser', [
+          function readHead(file) {
+            MetadataParser.readFileBytes(
+                file, 0, 10, this.nextStep, this.onError);
+          },
 
-          metadata.description.sort((a, b) => {
-            return Id3Parser.METADATA_ORDER.indexOf(a.key) -
-                Id3Parser.METADATA_ORDER.indexOf(b.key);
-          });
-          this.nextStep();
-        }
-      ],
-      this, () => {}, error => {});
+          /**
+           * Check if passed array of 10 bytes contains ID3 header.
+           * @param {File} file File to check and continue reading if ID3
+           *     metadata found.
+           * @param {ByteReader} reader Reader to fill with stream bytes.
+           */
+          function checkId3v2(file, reader) {
+            if (reader.readString(3) == 'ID3') {
+              this.logger.vlog('id3v2 found');
+              const id3v2 = metadata.id3v2 = {};
+              id3v2.major = reader.readScalar(1, false);
+              id3v2.minor = reader.readScalar(1, false);
+              id3v2.flags = reader.readScalar(1, false);
+              id3v2.size = Id3Parser.readSynchSafe_(reader, 4);
 
-  const metadataParser = new FunctionParallel(
-      'mp3metadataParser', [id3v1Parser, id3v2Parser], this, () => {
-        callback.call(null, metadata);
-      }, onError);
+              MetadataParser.readFileBytes(
+                  file, 10, 10 + id3v2.size, this.nextStep, this.onError);
+            } else {
+              this.finish();
+            }
+          },
 
-  id3v1Parser.setCallback(metadataParser.nextStep);
-  id3v2Parser.setCallback(metadataParser.nextStep);
+          /**
+           * Extracts all ID3v2 frames from given bytebuffer.
+           * @param {File} file File being parsed.
+           * @param {ByteReader} reader Reader to use for metadata extraction.
+           */
+          function extractFrames(file, reader) {
+            const id3v2 = metadata.id3v2;
 
-  id3v1Parser.setFailureCallback(metadataParser.onError);
-  id3v2Parser.setFailureCallback(metadataParser.onError);
+            if ((id3v2.major > 2) &&
+                (id3v2.flags & Id3Parser.v2.FLAG_EXTENDED_HEADER != 0)) {
+              // Skip extended header if found
+              if (id3v2.major == 3) {
+                reader.seek(reader.readScalar(4, false) - 4);
+              } else if (id3v2.major == 4) {
+                reader.seek(Id3Parser.readSynchSafe_(reader, 4) - 4);
+              }
+            }
 
-  this.vlog('Passed argument : ' + file);
+            let frame;
 
-  metadataParser.start(file);
-};
+            while (frame = self.readFrame_(reader, id3v2.major)) {
+              metadata.id3v2[frame.name] = frame;
+            }
 
+            this.nextStep();
+          },
+
+          /**
+           * Adds 'description' object to metadata.
+           * 'description' used to unify different parsers and make
+           * metadata parser-aware.
+           * Description is array if value-type pairs. Type should be used
+           * to properly format value before displaying to user.
+           */
+          function prepareDescription() {
+            const id3v2 = metadata.id3v2;
+
+            if (id3v2['APIC']) {
+              metadata.thumbnailURL = id3v2['APIC'].imageUrl;
+            } else if (id3v2['PIC']) {
+              metadata.thumbnailURL = id3v2['PIC'].imageUrl;
+            }
+
+            metadata.description = [];
+
+            for (const key in id3v2) {
+              if (typeof (Id3Parser.v2.MAPPERS[key]) != 'undefined' &&
+                  id3v2[key].value.trim().length > 0) {
+                metadata.description.push({
+                  key: Id3Parser.v2.MAPPERS[key],
+                  value: id3v2[key].value.trim()
+                });
+              }
+            }
+
+            /**
+             * @param {string} propName
+             * @param {...string} tags
+             */
+            function extract(propName, tags) {
+              for (let i = 1; i != arguments.length; i++) {
+                const tag = id3v2[arguments[i]];
+                if (tag && tag.value) {
+                  metadata[propName] = tag.value;
+                  break;
+                }
+              }
+            }
+
+            extract('album', 'TALB', 'TAL');
+            extract('title', 'TIT2', 'TT2');
+            extract('artist', 'TPE1', 'TP1');
+
+            metadata.description.sort((a, b) => {
+              return Id3Parser.METADATA_ORDER.indexOf(a.key) -
+                  Id3Parser.METADATA_ORDER.indexOf(b.key);
+            });
+            this.nextStep();
+          }
+        ],
+        this, () => {}, error => {});
+
+    const metadataParser = new FunctionParallel(
+        'mp3metadataParser', [id3v1Parser, id3v2Parser], this, () => {
+          callback.call(null, metadata);
+        }, onError);
+
+    id3v1Parser.setCallback(metadataParser.nextStep);
+    id3v2Parser.setCallback(metadataParser.nextStep);
+
+    id3v1Parser.setFailureCallback(metadataParser.onError);
+    id3v2Parser.setFailureCallback(metadataParser.onError);
+
+    this.vlog('Passed argument : ' + file);
+
+    metadataParser.start(file);
+  }
+}
 
 /**
  * Metadata order to use for metadata generation
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/image_orientation.js b/ui/file_manager/file_manager/foreground/js/metadata/image_orientation.js
index 62a6f07..034b7b3e 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/image_orientation.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/image_orientation.js
@@ -11,158 +11,147 @@
 
 /**
  * Class representing image orientation.
- * The constructor takes 2x2 matrix value that cancels the image orientation:
- * |a, c|
- * |b, d|
- * @param {number} a
- * @param {number} b
- * @param {number} c
- * @param {number} d
- * @struct
- * @constructor
+ * @final
  */
-function ImageOrientation(a, b, c, d) {
+class ImageOrientation {
   /**
-   * @public {number}
-   * @const
+   * The constructor takes 2x2 matrix value that cancels the image orientation:
+   * |a, c|
+   * |b, d|
+   * @param {number} a
+   * @param {number} b
+   * @param {number} c
+   * @param {number} d
    */
-  this.a = a;
+  constructor(a, b, c, d) {
+    /** @public @const {number} */
+    this.a = a;
+
+    /** @public @const {number} */
+    this.b = b;
+
+    /** @public @const {number} */
+    this.c = c;
+
+    /** @public @const {number} */
+    this.d = d;
+  }
 
   /**
-   * @public {number}
-   * @const
+   * @param {number} orientation 1-based orientation number defined by EXIF.
+   * @return {!ImageOrientation}
    */
-  this.b = b;
+  static fromExifOrientation(orientation) {
+    switch (~~orientation) {
+      case 1:
+        return new ImageOrientation(1, 0, 0, 1);
+      case 2:
+        return new ImageOrientation(-1, 0, 0, 1);
+      case 3:
+        return new ImageOrientation(-1, 0, 0, -1);
+      case 4:
+        return new ImageOrientation(1, 0, 0, -1);
+      case 5:
+        return new ImageOrientation(0, 1, 1, 0);
+      case 6:
+        return new ImageOrientation(0, 1, -1, 0);
+      case 7:
+        return new ImageOrientation(0, -1, -1, 0);
+      case 8:
+        return new ImageOrientation(0, -1, 1, 0);
+      default:
+        console.error('Invalid orientation number.');
+        return new ImageOrientation(1, 0, 0, 1);
+    }
+  }
 
   /**
-   * @public {number}
-   * @const
+   * @param {number} rotation90 Clockwise degrees / 90.
+   * @return {!ImageOrientation}
    */
-  this.c = c;
+  static fromClockwiseRotation(rotation90) {
+    switch (~~(rotation90 % 4)) {
+      case 0:
+        return new ImageOrientation(1, 0, 0, 1);
+      case 1:
+      case -3:
+        return new ImageOrientation(0, 1, -1, 0);
+      case 2:
+      case -2:
+        return new ImageOrientation(-1, 0, 0, -1);
+      case 3:
+      case -1:
+        return new ImageOrientation(0, -1, 1, 0);
+      default:
+        console.error('Invalid orientation number.');
+        return new ImageOrientation(1, 0, 0, 1);
+    }
+  }
 
   /**
-   * @public {number}
-   * @const
+   * Builds a transformation matrix from the image transform parameters.
+   * @param {!ImageTransformParam} transform
+   * @return {!ImageOrientation}
    */
-  this.d = d;
+  static fromRotationAndScale(transform) {
+    const scaleX = transform.scaleX;
+    const scaleY = transform.scaleY;
+    const rotate90 = transform.rotate90;
+
+    const orientation = ImageOrientation.fromClockwiseRotation(rotate90);
+
+    // Flip X and Y.
+    // In the Files app., CSS transformations are applied like
+    // "transform: rotate(90deg) scaleX(-1)".
+    // Since the image is scaled based on the X,Y axes pinned to the original,
+    // it is equivalent to scale first and then rotate.
+    // |a c| |s_x 0 | |x|   |a*s_x c*s_y| |x|
+    // |b d| | 0 s_y| |y| = |b*s_x d*s_y| |y|
+    return new ImageOrientation(
+        orientation.a * scaleX, orientation.b * scaleX, orientation.c * scaleY,
+        orientation.d * scaleY);
+  }
+
+  /**
+   * Obtains the image size after cancelling its orientation.
+   * @param {number} imageWidth
+   * @param {number} imageHeight
+   * @return {{width:number, height:number}}
+   */
+  getSizeAfterCancelling(imageWidth, imageHeight) {
+    const projectedX = this.a * imageWidth + this.c * imageHeight;
+    const projectedY = this.b * imageWidth + this.d * imageHeight;
+    return {
+      width: Math.abs(projectedX),
+      height: Math.abs(projectedY),
+    };
+  }
+
+  /**
+   * Applies the transformation that cancels the image orientation to the given
+   * context.
+   * @param {!CanvasRenderingContext2D} context
+   * @param {number} imageWidth
+   * @param {number} imageHeight
+   */
+  cancelImageOrientation(context, imageWidth, imageHeight) {
+    // Calculate where to project the point of (imageWidth, imageHeight).
+    const projectedX = this.a * imageWidth + this.c * imageHeight;
+    const projectedY = this.b * imageWidth + this.d * imageHeight;
+
+    // If the projected point coordinates are negative, add offset to cancel it.
+    const offsetX = projectedX < 0 ? -projectedX : 0;
+    const offsetY = projectedY < 0 ? -projectedY : 0;
+
+    // Apply the transform.
+    context.setTransform(this.a, this.b, this.c, this.d, offsetX, offsetY);
+  }
+
+  /**
+   * Checks if the orientation represents identity transformation or not.
+   * @return {boolean}
+   */
+  isIdentity() {
+    return this.a === 1 && this.b === 0 && this.c === 0 && this.d === 1;
+  }
 }
-
-/**
- * @param {number} orientation 1-based orientation number defined by EXIF.
- * @return {!ImageOrientation}
- */
-ImageOrientation.fromExifOrientation = orientation => {
-  switch (~~orientation) {
-    case 1:
-      return new ImageOrientation(1, 0, 0, 1);
-    case 2:
-      return new ImageOrientation(-1, 0, 0, 1);
-    case 3:
-      return new ImageOrientation(-1, 0, 0, -1);
-    case 4:
-      return new ImageOrientation(1, 0, 0, -1);
-    case 5:
-      return new ImageOrientation(0, 1, 1, 0);
-    case 6:
-      return new ImageOrientation(0, 1, -1, 0);
-    case 7:
-      return new ImageOrientation(0, -1, -1, 0);
-    case 8:
-      return new ImageOrientation(0, -1, 1, 0);
-    default:
-      console.error('Invalid orientation number.');
-      return new ImageOrientation(1, 0, 0, 1);
-  }
-};
-
-/**
- * @param {number} rotation90 Clockwise degrees / 90.
- * @return {!ImageOrientation}
- */
-ImageOrientation.fromClockwiseRotation = rotation90 => {
-  switch (~~(rotation90 % 4)) {
-    case 0:
-      return new ImageOrientation(1, 0, 0, 1);
-    case 1:
-    case -3:
-      return new ImageOrientation(0, 1, -1, 0);
-    case 2:
-    case -2:
-      return new ImageOrientation(-1, 0, 0, -1);
-    case 3:
-    case -1:
-      return new ImageOrientation(0, -1, 1, 0);
-    default:
-      console.error('Invalid orientation number.');
-      return new ImageOrientation(1, 0, 0, 1);
-  }
-};
-
-/**
- * Builds a transformation matrix from the image transform parameters.
- * @param {ImageTransformParam} transform
- * @return {!ImageOrientation}
- */
-ImageOrientation.fromRotationAndScale = transform => {
-  const scaleX = transform.scaleX;
-  const scaleY = transform.scaleY;
-  const rotate90 = transform.rotate90;
-
-  const orientation = ImageOrientation.fromClockwiseRotation(rotate90);
-
-  // Flip X and Y.
-  // In the Files app., CSS transformations are applied like
-  // "transform: rotate(90deg) scaleX(-1)".
-  // Since the image is scaled based on the X,Y axes pinned to the original,
-  // it is equivalent to scale first and then rotate.
-  // |a c| |s_x 0 | |x|   |a*s_x c*s_y| |x|
-  // |b d| | 0 s_y| |y| = |b*s_x d*s_y| |y|
-  return new ImageOrientation(
-      orientation.a * scaleX, orientation.b * scaleX, orientation.c * scaleY,
-      orientation.d * scaleY);
-};
-
-/**
- * Obtains the image size after cancelling its orientation.
- * @param {number} imageWidth
- * @param {number} imageHeight
- * @return {{width:number, height:number}}
- */
-ImageOrientation.prototype.getSizeAfterCancelling = function(
-    imageWidth, imageHeight) {
-  const projectedX = this.a * imageWidth + this.c * imageHeight;
-  const projectedY = this.b * imageWidth + this.d * imageHeight;
-  return {
-    width: Math.abs(projectedX),
-    height: Math.abs(projectedY),
-  };
-};
-
-/**
- * Applies the transformation that cancels the image orientation to the given
- * context.
- * @param {!CanvasRenderingContext2D} context
- * @param {number} imageWidth
- * @param {number} imageHeight
- */
-ImageOrientation.prototype.cancelImageOrientation = function(
-    context, imageWidth, imageHeight) {
-  // Calculate where to project the point of (imageWidth, imageHeight).
-  const projectedX = this.a * imageWidth + this.c * imageHeight;
-  const projectedY = this.b * imageWidth + this.d * imageHeight;
-
-  // If the projected point coordinates are negative, add offset to cancel it.
-  const offsetX = projectedX < 0 ? -projectedX : 0;
-  const offsetY = projectedY < 0 ? -projectedY : 0;
-
-  // Apply the transform.
-  context.setTransform(this.a, this.b, this.c, this.d, offsetX, offsetY);
-};
-
-/**
- * Checks if the orientation represents identity transformation or not.
- * @return {boolean}
- */
-ImageOrientation.prototype.isIdentity = function() {
-  return this.a === 1 && this.b === 0 && this.c === 0 && this.d === 1;
-};
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/image_parsers.js b/ui/file_manager/file_manager/foreground/js/metadata/image_parsers.js
index 1a3c1813..255c29c7 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/image_parsers.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/image_parsers.js
@@ -5,253 +5,245 @@
 /**
  * Base class for image metadata parsers that only need to look at a short
  * fragment at the start of the file.
- * @param {MetadataParserLogger} parent Parent object.
- * @param {string} type Image type.
- * @param {RegExp} urlFilter RegExp to match URLs.
- * @param {number} headerSize Size of header.
- * @constructor
- * @struct
- * @extends {ImageParser}
+ * @abstract
  */
-function SimpleImageParser(parent, type, urlFilter, headerSize) {
-  ImageParser.call(this, parent, type, urlFilter);
-  this.headerSize = headerSize;
+class SimpleImageParser extends ImageParser {
+  /**
+   * @param {!MetadataParserLogger} parent Parent object.
+   * @param {string} type Image type.
+   * @param {!RegExp} urlFilter RegExp to match URLs.
+   * @param {number} headerSize Size of header.
+   */
+  constructor(parent, type, urlFilter, headerSize) {
+    super(parent, type, urlFilter);
+    /** @public @const {number} */
+    this.headerSize = headerSize;
+  }
+
+  /**
+   * @param {File} file File to be parses.
+   * @param {Object} metadata Metadata object of the file.
+   * @param {function(Object)} callback Success callback.
+   * @param {function(string)} errorCallback Error callback.
+   */
+  parse(file, metadata, callback, errorCallback) {
+    const self = this;
+    MetadataParser.readFileBytes(file, 0, this.headerSize, (file, br) => {
+      try {
+        self.parseHeader(metadata, br);
+        callback(metadata);
+      } catch (e) {
+        errorCallback(e.toString());
+      }
+    }, errorCallback);
+  }
+
+  /**
+   * Parse header of an image. Inherited class must implement this.
+   * @abstract
+   * @param {Object} metadata Dictionary to store the parsed metadata.
+   * @param {ByteReader} byteReader Reader for header binary data.
+   */
+  parseHeader(metadata, byteReader) {}
 }
 
-SimpleImageParser.prototype.__proto__ = ImageParser.prototype;
-
-/**
- * @param {File} file File to be parses.
- * @param {Object} metadata Metadata object of the file.
- * @param {function(Object)} callback Success callback.
- * @param {function(string)} errorCallback Error callback.
- */
-SimpleImageParser.prototype.parse = function(
-    file, metadata, callback, errorCallback) {
-  const self = this;
-  MetadataParser.readFileBytes(file, 0, this.headerSize, (file, br) => {
-    try {
-      self.parseHeader(metadata, br);
-      callback(metadata);
-    } catch (e) {
-      errorCallback(e.toString());
-    }
-  }, errorCallback);
-};
-
-/**
- * Parse header of an image. Inherited class must implement this.
- * @param {Object} metadata Dictionary to store the parsed metadata.
- * @param {ByteReader} byteReader Reader for header binary data.
- */
-SimpleImageParser.prototype.parseHeader = (metadata, byteReader) => {};
-
 /**
  * Parser for the header of png files.
- * @param {MetadataParserLogger} parent Parent object.
- * @extends {SimpleImageParser}
- * @constructor
- * @struct
+ * @final
  */
-function PngParser(parent) {
-  SimpleImageParser.call(this, parent, 'png', /\.png$/i, 24);
+class PngParser extends SimpleImageParser {
+  /**
+   * @param {!MetadataParserLogger} parent Parent object.
+   */
+  constructor(parent) {
+    super(parent, 'png', /\.png$/i, 24);
+  }
+
+  /**
+   * @override
+   */
+  parseHeader(metadata, br) {
+    br.setByteOrder(ByteReader.BIG_ENDIAN);
+
+    const signature = br.readString(8);
+    if (signature != '\x89PNG\x0D\x0A\x1A\x0A') {
+      throw new Error('Invalid PNG signature: ' + signature);
+    }
+
+    br.seek(12);
+    const ihdr = br.readString(4);
+    if (ihdr != 'IHDR') {
+      throw new Error('Missing IHDR chunk');
+    }
+
+    metadata.width = br.readScalar(4);
+    metadata.height = br.readScalar(4);
+  }
 }
 
-PngParser.prototype = {
-  __proto__: SimpleImageParser.prototype
-};
-
-/**
- * @override
- */
-PngParser.prototype.parseHeader = (metadata, br) => {
-  br.setByteOrder(ByteReader.BIG_ENDIAN);
-
-  const signature = br.readString(8);
-  if (signature != '\x89PNG\x0D\x0A\x1A\x0A') {
-    throw new Error('Invalid PNG signature: ' + signature);
-  }
-
-  br.seek(12);
-  const ihdr = br.readString(4);
-  if (ihdr != 'IHDR') {
-    throw new Error('Missing IHDR chunk');
-  }
-
-  metadata.width = br.readScalar(4);
-  metadata.height = br.readScalar(4);
-};
-
 registerParserClass(PngParser);
 
 /**
  * Parser for the header of bmp files.
- * @param {MetadataParserLogger} parent Parent object.
- * @constructor
- * @extends {SimpleImageParser}
- * @struct
+ * @final
  */
-function BmpParser(parent) {
-  SimpleImageParser.call(this, parent, 'bmp', /\.bmp$/i, 28);
-}
-
-BmpParser.prototype = {
-  __proto__: SimpleImageParser.prototype
-};
-
-/**
- * @override
- */
-BmpParser.prototype.parseHeader = (metadata, br) => {
-  br.setByteOrder(ByteReader.LITTLE_ENDIAN);
-
-  const signature = br.readString(2);
-  if (signature != 'BM') {
-    throw new Error('Invalid BMP signature: ' + signature);
+class BmpParser extends SimpleImageParser {
+  /**
+   * @param {!MetadataParserLogger} parent Parent object.
+   */
+  constructor(parent) {
+    super(parent, 'bmp', /\.bmp$/i, 28);
   }
 
-  br.seek(18);
-  metadata.width = br.readScalar(4);
-  metadata.height = br.readScalar(4);
-};
+  /**
+   * @override
+   */
+  parseHeader(metadata, br) {
+    br.setByteOrder(ByteReader.LITTLE_ENDIAN);
+
+    const signature = br.readString(2);
+    if (signature != 'BM') {
+      throw new Error('Invalid BMP signature: ' + signature);
+    }
+
+    br.seek(18);
+    metadata.width = br.readScalar(4);
+    metadata.height = br.readScalar(4);
+  }
+}
 
 registerParserClass(BmpParser);
 
 /**
  * Parser for the header of gif files.
- * @param {MetadataParserLogger} parent Parent object.
- * @constructor
- * @extends {SimpleImageParser}
- * @struct
+ * @final
  */
-function GifParser(parent) {
-  SimpleImageParser.call(this, parent, 'gif', /\.Gif$/i, 10);
-}
-
-GifParser.prototype = {
-  __proto__: SimpleImageParser.prototype
-};
-
-/**
- * @override
- */
-GifParser.prototype.parseHeader = (metadata, br) => {
-  br.setByteOrder(ByteReader.LITTLE_ENDIAN);
-
-  const signature = br.readString(6);
-  if (!signature.match(/GIF8(7|9)a/)) {
-    throw new Error('Invalid GIF signature: ' + signature);
+class GifParser extends SimpleImageParser {
+  /**
+   * @param {!MetadataParserLogger} parent Parent object.
+   */
+  constructor(parent) {
+    super(parent, 'gif', /\.Gif$/i, 10);
   }
 
-  metadata.width = br.readScalar(2);
-  metadata.height = br.readScalar(2);
-};
+  /**
+   * @override
+   */
+  parseHeader(metadata, br) {
+    br.setByteOrder(ByteReader.LITTLE_ENDIAN);
+
+    const signature = br.readString(6);
+    if (!signature.match(/GIF8(7|9)a/)) {
+      throw new Error('Invalid GIF signature: ' + signature);
+    }
+
+    metadata.width = br.readScalar(2);
+    metadata.height = br.readScalar(2);
+  }
+}
 
 registerParserClass(GifParser);
 
 /**
  * Parser for the header of webp files.
- * @param {MetadataParserLogger} parent Parent object.
- * @constructor
- * @extends {SimpleImageParser}
- * @struct
+ * @final
  */
-function WebpParser(parent) {
-  SimpleImageParser.call(this, parent, 'webp', /\.webp$/i, 30);
+class WebpParser extends SimpleImageParser {
+  /**
+   * @param {!MetadataParserLogger} parent Parent object.
+   */
+  constructor(parent) {
+    super(parent, 'webp', /\.webp$/i, 30);
+  }
+
+  /**
+   * @override
+   */
+  parseHeader(metadata, br) {
+    br.setByteOrder(ByteReader.LITTLE_ENDIAN);
+
+    const riffSignature = br.readString(4);
+    if (riffSignature != 'RIFF') {
+      throw new Error('Invalid RIFF signature: ' + riffSignature);
+    }
+
+    br.seek(8);
+    const webpSignature = br.readString(4);
+    if (webpSignature != 'WEBP') {
+      throw new Error('Invalid WEBP signature: ' + webpSignature);
+    }
+
+    const chunkFormat = br.readString(4);
+    switch (chunkFormat) {
+      // VP8 lossy bitstream format.
+      case 'VP8 ':
+        br.seek(23);
+        const lossySignature = br.readScalar(2) | (br.readScalar(1) << 16);
+        if (lossySignature != 0x2a019d) {
+          throw new Error(
+              'Invalid VP8 lossy bitstream signature: ' + lossySignature);
+        }
+        var dimensionBits = br.readScalar(4);
+        metadata.width = dimensionBits & 0x3fff;
+        metadata.height = (dimensionBits >> 16) & 0x3fff;
+        break;
+
+      // VP8 lossless bitstream format.
+      case 'VP8L':
+        br.seek(20);
+        const losslessSignature = br.readScalar(1);
+        if (losslessSignature != 0x2f) {
+          throw new Error(
+              'Invalid VP8 lossless bitstream signature: ' + losslessSignature);
+        }
+        var dimensionBits = br.readScalar(4);
+        metadata.width = (dimensionBits & 0x3fff) + 1;
+        metadata.height = ((dimensionBits >> 14) & 0x3fff) + 1;
+        break;
+
+      // VP8 extended file format.
+      case 'VP8X':
+        br.seek(20);
+        // Read 24-bit value. ECMAScript assures left-to-right evaluation order.
+        metadata.width = (br.readScalar(2) | (br.readScalar(1) << 16)) + 1;
+        metadata.height = (br.readScalar(2) | (br.readScalar(1) << 16)) + 1;
+        break;
+
+      default:
+        throw new Error('Invalid chunk format: ' + chunkFormat);
+    }
+  }
 }
 
-WebpParser.prototype = {
-  __proto__: SimpleImageParser.prototype
-};
-
-/**
- * @override
- */
-WebpParser.prototype.parseHeader = (metadata, br) => {
-  br.setByteOrder(ByteReader.LITTLE_ENDIAN);
-
-  const riffSignature = br.readString(4);
-  if (riffSignature != 'RIFF') {
-    throw new Error('Invalid RIFF signature: ' + riffSignature);
-  }
-
-  br.seek(8);
-  const webpSignature = br.readString(4);
-  if (webpSignature != 'WEBP') {
-    throw new Error('Invalid WEBP signature: ' + webpSignature);
-  }
-
-  const chunkFormat = br.readString(4);
-  switch (chunkFormat) {
-    // VP8 lossy bitstream format.
-    case 'VP8 ':
-      br.seek(23);
-      const lossySignature = br.readScalar(2) | (br.readScalar(1) << 16);
-      if (lossySignature != 0x2a019d) {
-        throw new Error(
-            'Invalid VP8 lossy bitstream signature: ' + lossySignature);
-      }
-      var dimensionBits = br.readScalar(4);
-      metadata.width = dimensionBits & 0x3fff;
-      metadata.height = (dimensionBits >> 16) & 0x3fff;
-      break;
-
-    // VP8 lossless bitstream format.
-    case 'VP8L':
-      br.seek(20);
-      const losslessSignature = br.readScalar(1);
-      if (losslessSignature != 0x2f) {
-        throw new Error(
-            'Invalid VP8 lossless bitstream signature: ' + losslessSignature);
-      }
-      var dimensionBits = br.readScalar(4);
-      metadata.width = (dimensionBits & 0x3fff) + 1;
-      metadata.height = ((dimensionBits >> 14) & 0x3fff) + 1;
-      break;
-
-    // VP8 extended file format.
-    case 'VP8X':
-      br.seek(20);
-      // Read 24-bit value. ECMAScript assures left-to-right evaluation order.
-      metadata.width = (br.readScalar(2) | (br.readScalar(1) << 16)) + 1;
-      metadata.height = (br.readScalar(2) | (br.readScalar(1) << 16)) + 1;
-      break;
-
-    default:
-      throw new Error('Invalid chunk format: ' + chunkFormat);
-  }
-};
-
 registerParserClass(WebpParser);
 
 /**
  * Parser for the header of .ico icon files.
- * @param {MetadataParserLogger} parent Parent metadata dispatcher object.
- * @constructor
- * @extends {SimpleImageParser}
+ * @final
  */
-function IcoParser(parent) {
-  SimpleImageParser.call(this, parent, 'ico', /\.ico$/i, 8);
-}
-
-IcoParser.prototype = {
-  __proto__: SimpleImageParser.prototype
-};
-
-/**
- * @override
- */
-IcoParser.prototype.parseHeader = (metadata, byteReader) => {
-  byteReader.setByteOrder(ByteReader.LITTLE_ENDIAN);
-
-  const signature = byteReader.readString(4);
-  if (signature !== '\x00\x00\x00\x01') {
-    throw new Error('Invalid ICO signature: ' + signature);
+class IcoParser extends SimpleImageParser {
+  /**
+   * @param {!MetadataParserLogger} parent Parent metadata dispatcher object.
+   */
+  constructor(parent) {
+    super(parent, 'ico', /\.ico$/i, 8);
   }
 
-  byteReader.seek(2);
-  metadata.width = byteReader.readScalar(1);
-  metadata.height = byteReader.readScalar(1);
-};
+  /**
+   * @override
+   */
+  parseHeader(metadata, byteReader) {
+    byteReader.setByteOrder(ByteReader.LITTLE_ENDIAN);
+
+    const signature = byteReader.readString(4);
+    if (signature !== '\x00\x00\x00\x01') {
+      throw new Error('Invalid ICO signature: ' + signature);
+    }
+
+    byteReader.seek(2);
+    metadata.width = byteReader.readScalar(1);
+    metadata.height = byteReader.readScalar(1);
+  }
+}
 
 registerParserClass(IcoParser);
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/metadata_item.js b/ui/file_manager/file_manager/foreground/js/metadata/metadata_item.js
index f321f77..f1cc079e 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/metadata_item.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/metadata_item.js
@@ -259,4 +259,24 @@
    * @public {boolean|undefined}
    */
   this.canShare;
+
+  /**
+   * @public {string|undefined}
+   */
+  this.alternateUrl;
+
+  /**
+   * @public {boolean|undefined}
+   */
+  this.isMachineRoot;
+
+  /**
+   * @public {boolean|undefined}
+   */
+  this.isArbitrarySyncFolder;
+
+  /**
+   * @public {boolean|undefined}
+   */
+  this.isExternalMedia;
 }
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/metadata_model.js b/ui/file_manager/file_manager/foreground/js/metadata/metadata_model.js
index dcc79e6..6f0f25b 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/metadata_model.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/metadata_model.js
@@ -4,6 +4,7 @@
 
 /**
  * Stats collected about Metadata handling for tests.
+ * @final
  */
 class MetadataStats {
   constructor() {
@@ -29,29 +30,20 @@
    * @param {!MetadataProvider} rawProvider
    */
   constructor(rawProvider) {
-    /**
-     * @private {!MetadataProvider}
-     * @const
-     */
+    /** @private @const {!MetadataProvider} */
     this.rawProvider_ = rawProvider;
 
-    /**
-     * @private {!MetadataProviderCache}
-     * @const
-     */
+    /** @private @const {!MetadataProviderCache} */
     this.cache_ = new MetadataProviderCache();
 
-    /**
-     * @private {!Array<!MetadataProviderCallbackRequest<T>>}
-     * @const
-     */
+    /** @private @const {!Array<!MetadataProviderCallbackRequest<T>>} */
     this.callbackRequests_ = [];
 
-    /** @private {?MetadataStats} record stats about Metadata when in tests. */
-    this.stats_ = null;
-    if (window.IN_TEST) {
-      this.stats_ = new MetadataStats();
-    }
+    /**
+     * @private @const {?MetadataStats} record stats about Metadata when in
+     *     tests.
+     */
+    this.stats_ = window.IN_TEST ? new MetadataStats() : null;
   }
 
   /**
@@ -222,6 +214,7 @@
   }
 }
 
+/** @final */
 class MetadataProviderCallbackRequest {
   /**
    * @param {!Array<!Entry>} entries
@@ -275,6 +268,7 @@
 
 /**
  * Helper wrapper for LRUCache.
+ * @final
  */
 class MetadataProviderCache extends MetadataCacheSet {
   constructor() {
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/metadata_model_unittest.js b/ui/file_manager/file_manager/foreground/js/metadata/metadata_model_unittest.js
index dadeafc..e98c56d 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/metadata_model_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/metadata_model_unittest.js
@@ -2,66 +2,57 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/**
- * TestMetadataProvider
- * @constructor
- * @extends {MetadataProvider}
- */
-function TestMetadataProvider() {
-  MetadataProvider.call(this, ['property', 'propertyA', 'propertyB']);
-  this.requestCount = 0;
+/** @final */
+class TestMetadataProvider extends MetadataProvider {
+  constructor() {
+    super(['property', 'propertyA', 'propertyB']);
+    this.requestCount = 0;
+  }
+
+  /** @override */
+  get(requests) {
+    this.requestCount++;
+    return Promise.resolve(requests.map(request => {
+      const entry = request.entry;
+      const names = request.names;
+      const result = {};
+      for (let i = 0; i < names.length; i++) {
+        result[names[i]] = entry.toURL() + ':' + names[i];
+      }
+      return result;
+    }));
+  }
 }
 
-TestMetadataProvider.prototype.__proto__ = MetadataProvider.prototype;
+/** @final */
+class TestEmptyMetadataProvider extends MetadataProvider {
+  constructor() {
+    super(['property']);
+  }
 
-TestMetadataProvider.prototype.get = function(requests) {
-  this.requestCount++;
-  return Promise.resolve(requests.map(request => {
-    const entry = request.entry;
-    const names = request.names;
-    const result = {};
-    for (let i = 0; i < names.length; i++) {
-      result[names[i]] = entry.toURL() + ':' + names[i];
-    }
-    return result;
-  }));
-};
-
-/**
- * TestEmptyMetadataProvider
- * @constructor
- * @extends {MetadataProvider}
- */
-function TestEmptyMetadataProvider() {
-  MetadataProvider.call(this, ['property']);
+  /** @override */
+  get(requests) {
+    return Promise.resolve(requests.map(() => {
+      return {};
+    }));
+  }
 }
 
-TestEmptyMetadataProvider.prototype.__proto__ = MetadataProvider.prototype;
+/** @final */
+class ManualTestMetadataProvider extends MetadataProvider {
+  constructor() {
+    super(['propertyA', 'propertyB', 'propertyC']);
+    this.callback = [];
+  }
 
-TestEmptyMetadataProvider.prototype.get = requests => {
-  return Promise.resolve(requests.map(() => {
-    return {};
-  }));
-};
-
-/**
- * ManualTestMetadataProvider
- * @constructor
- * @extends {MetadataProvider}
- */
-function ManualTestMetadataProvider() {
-  MetadataProvider.call(this, ['propertyA', 'propertyB', 'propertyC']);
-  this.callback = [];
+  /** @override */
+  get(requests) {
+    return new Promise(fulfill => {
+      this.callback.push(fulfill);
+    });
+  }
 }
 
-ManualTestMetadataProvider.prototype.__proto__ = MetadataProvider.prototype;
-
-ManualTestMetadataProvider.prototype.get = function(requests) {
-  return new Promise(fulfill => {
-    this.callback.push(fulfill);
-  });
-};
-
 /** @type {!Entry} */
 const entryA = /** @type {!Entry} */ ({
   toURL: function() {
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/mpeg_parser.js b/ui/file_manager/file_manager/foreground/js/metadata/mpeg_parser.js
index 6d06258..9bc77b4 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/mpeg_parser.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/mpeg_parser.js
@@ -2,322 +2,318 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/**
- * @param {MetadataParserLogger} parent Parent object.
- * @constructor
- * @extends {MetadataParser}
- * @struct
- */
-function MpegParser(parent) {
-  MetadataParser.call(this, parent, 'mpeg', /\.(mp4|m4v|m4a|mpe?g4?)$/i);
-  this.mimeType = 'video/mpeg';
-}
+/** @final */
+class MpegParser extends MetadataParser {
+  /**
+   * @param {!MetadataParserLogger} parent Parent object.
+   */
+  constructor(parent) {
+    super(parent, 'mpeg', /\.(mp4|m4v|m4a|mpe?g4?)$/i);
+    this.mimeType = 'video/mpeg';
+  }
 
-MpegParser.prototype = {
-  __proto__: MetadataParser.prototype
-};
+  /**
+   * @param {ByteReader} br ByteReader instance.
+   * @param {number=} opt_end End of atom position.
+   * @return {number} Atom size.
+   */
+  static readAtomSize(br, opt_end) {
+    const pos = br.tell();
+
+    if (opt_end) {
+      // Assert that opt_end <= buffer end.
+      // When supplied, opt_end is the end of the enclosing atom and is used to
+      // check the correct nesting.
+      br.validateRead(opt_end - pos);
+    }
+
+    const size = br.readScalar(4, false, opt_end);
+
+    if (size < MpegParser.HEADER_SIZE) {
+      throw 'atom too short (' + size + ') @' + pos;
+    }
+
+    if (opt_end && pos + size > opt_end) {
+      throw 'atom too long (' + size + '>' + (opt_end - pos) + ') @' + pos;
+    }
+
+    return size;
+  }
+
+  /**
+   * @param {ByteReader} br ByteReader instance.
+   * @param {number=} opt_end End of atom position.
+   * @return {string} Atom name.
+   */
+  static readAtomName(br, opt_end) {
+    return br.readString(4, opt_end).toLowerCase();
+  }
+
+  /**
+   * @param {Object} metadata Metadata object.
+   * @return {Object} Root of the parser tree.
+   */
+  static createRootParser(metadata) {
+    function findParentAtom(atom, name) {
+      for (;;) {
+        atom = atom.parent;
+        if (!atom) {
+          return null;
+        }
+        if (atom.name == name) {
+          return atom;
+        }
+      }
+    }
+
+    function parseFtyp(br, atom) {
+      metadata.brand = br.readString(4, atom.end);
+    }
+
+    function parseMvhd(br, atom) {
+      const version = br.readScalar(4, false, atom.end);
+      const offset = (version == 0) ? 8 : 16;
+      br.seek(offset, ByteReader.SEEK_CUR);
+      const timescale = br.readScalar(4, false, atom.end);
+      const duration = br.readScalar(4, false, atom.end);
+      metadata.duration = duration / timescale;
+    }
+
+    function parseHdlr(br, atom) {
+      br.seek(8, ByteReader.SEEK_CUR);
+      findParentAtom(atom, 'trak').trackType = br.readString(4, atom.end);
+    }
+
+    function parseStsd(br, atom) {
+      const track = findParentAtom(atom, 'trak');
+      if (track && track.trackType == 'vide') {
+        br.seek(40, ByteReader.SEEK_CUR);
+        metadata.width = br.readScalar(2, false, atom.end);
+        metadata.height = br.readScalar(2, false, atom.end);
+      }
+    }
+
+    function parseDataString(name, br, atom) {
+      br.seek(8, ByteReader.SEEK_CUR);
+      metadata[name] = br.readString(atom.end - br.tell(), atom.end);
+    }
+
+    function parseCovr(br, atom) {
+      br.seek(8, ByteReader.SEEK_CUR);
+      metadata.thumbnailURL = br.readImage(atom.end - br.tell(), atom.end);
+    }
+
+    // 'meta' atom can occur at one of the several places in the file structure.
+    const parseMeta = {
+      ilst: {
+        '©nam': {data: parseDataString.bind(null, 'title')},
+        '©alb': {data: parseDataString.bind(null, 'album')},
+        '©art': {data: parseDataString.bind(null, 'artist')},
+        'covr': {data: parseCovr}
+      },
+      versioned: true
+    };
+
+    // main parser for the entire file structure.
+    return {
+      ftyp: parseFtyp,
+      moov: {
+        mvhd: parseMvhd,
+        trak: {
+          mdia: {
+            hdlr: parseHdlr,
+            minf: {
+              stbl: {stsd: parseStsd},
+            },
+          },
+          meta: parseMeta
+        },
+        udta: {
+          meta: parseMeta,
+        },
+        meta: parseMeta
+      },
+      meta: parseMeta
+    };
+  }
+
+  /**
+   * @param {File} file File.
+   * @param {Object} metadata Metadata.
+   * @param {function(Object)} callback Success callback.
+   * @param {function((ProgressEvent|string))} onError Error callback.
+   */
+  parse(file, metadata, callback, onError) {
+    const rootParser = MpegParser.createRootParser(metadata);
+
+    // Kick off the processing by reading the first atom's header.
+    this.requestRead(
+        rootParser, file, 0, MpegParser.HEADER_SIZE, null, onError,
+        callback.bind(null, metadata));
+  }
+
+  /**
+   * @param {function(ByteReader, Object)|Object} parser Parser tree node.
+   * @param {ByteReader} br ByteReader instance.
+   * @param {Object} atom Atom descriptor.
+   * @param {number} filePos File position of the atom start.
+   */
+  applyParser(parser, br, atom, filePos) {
+    if (this.verbose) {
+      let path = atom.name;
+      for (let p = atom.parent; p && p.name; p = p.parent) {
+        path = p.name + '.' + path;
+      }
+
+      let action;
+      if (!parser) {
+        action = 'skipping ';
+      } else if (parser instanceof Function) {
+        action = 'parsing  ';
+      } else {
+        action = 'recursing';
+      }
+
+      const start = atom.start - MpegParser.HEADER_SIZE;
+      this.vlog(
+          path + ': ' +
+              '@' + (filePos + start) + ':' + (atom.end - start),
+          action);
+    }
+
+    if (parser) {
+      if (parser instanceof Function) {
+        br.pushSeek(atom.start);
+        parser(br, atom);
+        br.popSeek();
+      } else {
+        if (parser.versioned) {
+          atom.start += 4;
+        }
+        this.parseMpegAtomsInRange(parser, br, atom, filePos);
+      }
+    }
+  }
+
+  /**
+   * @param {function(ByteReader, Object)|Object} parser Parser tree node.
+   * @param {ByteReader} br ByteReader instance.
+   * @param {Object} parentAtom Parent atom descriptor.
+   * @param {number} filePos File position of the atom start.
+   */
+  parseMpegAtomsInRange(parser, br, parentAtom, filePos) {
+    let count = 0;
+    for (let offset = parentAtom.start; offset != parentAtom.end;) {
+      if (count++ > 100) {
+        // Most likely we are looping through a corrupt file.
+        throw 'too many child atoms in ' + parentAtom.name + ' @' + offset;
+      }
+
+      br.seek(offset);
+      const size = MpegParser.readAtomSize(br, parentAtom.end);
+      const name = MpegParser.readAtomName(br, parentAtom.end);
+
+      this.applyParser(
+          parser[name], br, {
+            start: offset + MpegParser.HEADER_SIZE,
+            end: offset + size,
+            name: name,
+            parent: parentAtom
+          },
+          filePos);
+
+      offset += size;
+    }
+  }
+
+  /**
+   * @param {Object} rootParser Parser definition.
+   * @param {File} file File.
+   * @param {number} filePos Start position in the file.
+   * @param {number} size Atom size.
+   * @param {string?} name Atom name.
+   * @param {function((ProgressEvent|string))} onError Error callback.
+   * @param {function()} onSuccess Success callback.
+   */
+  requestRead(rootParser, file, filePos, size, name, onError, onSuccess) {
+    const self = this;
+    const reader = new FileReader();
+    reader.onerror = onError;
+    reader.onload = event => {
+      self.processTopLevelAtom(
+          /** @type {ArrayBuffer} */ (reader.result), rootParser, file, filePos,
+          size, name, onError, onSuccess);
+    };
+    this.vlog('reading @' + filePos + ':' + size);
+    reader.readAsArrayBuffer(file.slice(filePos, filePos + size));
+  }
+
+  /**
+   * @param {ArrayBuffer} buf Data buffer.
+   * @param {Object} rootParser Parser definition.
+   * @param {File} file File.
+   * @param {number} filePos Start position in the file.
+   * @param {number} size Atom size.
+   * @param {string?} name Atom name.
+   * @param {function((ProgressEvent|string))} onError Error callback.
+   * @param {function()} onSuccess Success callback.
+   */
+  processTopLevelAtom(
+      buf, rootParser, file, filePos, size, name, onError, onSuccess) {
+    try {
+      const br = new ByteReader(buf);
+
+      // the header has already been read.
+      const atomEnd = size - MpegParser.HEADER_SIZE;
+
+      const bufLength = buf.byteLength;
+
+      // Check the available data size. It should be either exactly
+      // what we requested or HEADER_SIZE bytes less (for the last atom).
+      if (bufLength != atomEnd && bufLength != size) {
+        throw 'Read failure @' + filePos + ', ' +
+            'requested ' + size + ', read ' + bufLength;
+      }
+
+      // Process the top level atom.
+      if (name) {  // name is null only the first time.
+        this.applyParser(
+            rootParser[name], br, {start: 0, end: atomEnd, name: name},
+            filePos);
+      }
+
+      filePos += bufLength;
+      if (bufLength == size) {
+        // The previous read returned everything we asked for, including
+        // the next atom header at the end of the buffer.
+        // Parse this header and schedule the next read.
+        br.seek(-MpegParser.HEADER_SIZE, ByteReader.SEEK_END);
+        let nextSize = MpegParser.readAtomSize(br);
+        const nextName = MpegParser.readAtomName(br);
+
+        // If we do not have a parser for the next atom, skip the content and
+        // read only the header (the one after the next).
+        if (!rootParser[nextName]) {
+          filePos += nextSize - MpegParser.HEADER_SIZE;
+          nextSize = MpegParser.HEADER_SIZE;
+        }
+
+        this.requestRead(
+            rootParser, file, filePos, nextSize, nextName, onError, onSuccess);
+      } else {
+        // The previous read did not return the next atom header, EOF reached.
+        this.vlog('EOF @' + filePos);
+        onSuccess();
+      }
+    } catch (e) {
+      onError(e.toString());
+    }
+  }
+}
 
 /**
  * Size of the atom header.
  */
 MpegParser.HEADER_SIZE = 8;
 
-/**
- * @param {ByteReader} br ByteReader instance.
- * @param {number=} opt_end End of atom position.
- * @return {number} Atom size.
- */
-MpegParser.readAtomSize = (br, opt_end) => {
-  const pos = br.tell();
-
-  if (opt_end) {
-    // Assert that opt_end <= buffer end.
-    // When supplied, opt_end is the end of the enclosing atom and is used to
-    // check the correct nesting.
-    br.validateRead(opt_end - pos);
-  }
-
-  const size = br.readScalar(4, false, opt_end);
-
-  if (size < MpegParser.HEADER_SIZE) {
-    throw 'atom too short (' + size + ') @' + pos;
-  }
-
-  if (opt_end && pos + size > opt_end) {
-    throw 'atom too long (' + size + '>' + (opt_end - pos) + ') @' + pos;
-  }
-
-  return size;
-};
-
-/**
- * @param {ByteReader} br ByteReader instance.
- * @param {number=} opt_end End of atom position.
- * @return {string} Atom name.
- */
-MpegParser.readAtomName = (br, opt_end) => {
-  return br.readString(4, opt_end).toLowerCase();
-};
-
-/**
- * @param {Object} metadata Metadata object.
- * @return {Object} Root of the parser tree.
- */
-MpegParser.createRootParser = metadata => {
-  function findParentAtom(atom, name) {
-    for (;;) {
-      atom = atom.parent;
-      if (!atom) {
-        return null;
-      }
-      if (atom.name == name) {
-        return atom;
-      }
-    }
-  }
-
-  function parseFtyp(br, atom) {
-    metadata.brand = br.readString(4, atom.end);
-  }
-
-  function parseMvhd(br, atom) {
-    const version = br.readScalar(4, false, atom.end);
-    const offset = (version == 0) ? 8 : 16;
-    br.seek(offset, ByteReader.SEEK_CUR);
-    const timescale = br.readScalar(4, false, atom.end);
-    const duration = br.readScalar(4, false, atom.end);
-    metadata.duration = duration / timescale;
-  }
-
-  function parseHdlr(br, atom) {
-    br.seek(8, ByteReader.SEEK_CUR);
-    findParentAtom(atom, 'trak').trackType = br.readString(4, atom.end);
-  }
-
-  function parseStsd(br, atom) {
-    const track = findParentAtom(atom, 'trak');
-    if (track && track.trackType == 'vide') {
-      br.seek(40, ByteReader.SEEK_CUR);
-      metadata.width = br.readScalar(2, false, atom.end);
-      metadata.height = br.readScalar(2, false, atom.end);
-    }
-  }
-
-  function parseDataString(name, br, atom) {
-    br.seek(8, ByteReader.SEEK_CUR);
-    metadata[name] = br.readString(atom.end - br.tell(), atom.end);
-  }
-
-  function parseCovr(br, atom) {
-    br.seek(8, ByteReader.SEEK_CUR);
-    metadata.thumbnailURL = br.readImage(atom.end - br.tell(), atom.end);
-  }
-
-  // 'meta' atom can occur at one of the several places in the file structure.
-  const parseMeta = {
-    ilst: {
-      '©nam': {data: parseDataString.bind(null, 'title')},
-      '©alb': {data: parseDataString.bind(null, 'album')},
-      '©art': {data: parseDataString.bind(null, 'artist')},
-      'covr': {data: parseCovr}
-    },
-    versioned: true
-  };
-
-  // main parser for the entire file structure.
-  return {
-    ftyp: parseFtyp,
-    moov: {
-      mvhd: parseMvhd,
-      trak: {
-        mdia: {
-          hdlr: parseHdlr,
-          minf: {
-            stbl: {stsd: parseStsd},
-          },
-        },
-        meta: parseMeta
-      },
-      udta: {
-        meta: parseMeta,
-      },
-      meta: parseMeta
-    },
-    meta: parseMeta
-  };
-};
-
-/**
- * @param {File} file File.
- * @param {Object} metadata Metadata.
- * @param {function(Object)} callback Success callback.
- * @param {function((ProgressEvent|string))} onError Error callback.
- */
-MpegParser.prototype.parse = function(file, metadata, callback, onError) {
-  const rootParser = MpegParser.createRootParser(metadata);
-
-  // Kick off the processing by reading the first atom's header.
-  this.requestRead(
-      rootParser, file, 0, MpegParser.HEADER_SIZE, null, onError,
-      callback.bind(null, metadata));
-};
-
-/**
- * @param {function(ByteReader, Object)|Object} parser Parser tree node.
- * @param {ByteReader} br ByteReader instance.
- * @param {Object} atom Atom descriptor.
- * @param {number} filePos File position of the atom start.
- */
-MpegParser.prototype.applyParser = function(parser, br, atom, filePos) {
-  if (this.verbose) {
-    let path = atom.name;
-    for (let p = atom.parent; p && p.name; p = p.parent) {
-      path = p.name + '.' + path;
-    }
-
-    let action;
-    if (!parser) {
-      action = 'skipping ';
-    } else if (parser instanceof Function) {
-      action = 'parsing  ';
-    } else {
-      action = 'recursing';
-    }
-
-    const start = atom.start - MpegParser.HEADER_SIZE;
-    this.vlog(
-        path + ': ' +
-            '@' + (filePos + start) + ':' + (atom.end - start),
-        action);
-  }
-
-  if (parser) {
-    if (parser instanceof Function) {
-      br.pushSeek(atom.start);
-      parser(br, atom);
-      br.popSeek();
-    } else {
-      if (parser.versioned) {
-        atom.start += 4;
-      }
-      this.parseMpegAtomsInRange(parser, br, atom, filePos);
-    }
-  }
-};
-
-/**
- * @param {function(ByteReader, Object)|Object} parser Parser tree node.
- * @param {ByteReader} br ByteReader instance.
- * @param {Object} parentAtom Parent atom descriptor.
- * @param {number} filePos File position of the atom start.
- */
-MpegParser.prototype.parseMpegAtomsInRange = function(
-    parser, br, parentAtom, filePos) {
-  let count = 0;
-  for (let offset = parentAtom.start; offset != parentAtom.end;) {
-    if (count++ > 100) {  // Most likely we are looping through a corrupt file.
-      throw 'too many child atoms in ' + parentAtom.name + ' @' + offset;
-    }
-
-    br.seek(offset);
-    const size = MpegParser.readAtomSize(br, parentAtom.end);
-    const name = MpegParser.readAtomName(br, parentAtom.end);
-
-    this.applyParser(
-        parser[name], br, {
-          start: offset + MpegParser.HEADER_SIZE,
-          end: offset + size,
-          name: name,
-          parent: parentAtom
-        },
-        filePos);
-
-    offset += size;
-  }
-};
-
-/**
- * @param {Object} rootParser Parser definition.
- * @param {File} file File.
- * @param {number} filePos Start position in the file.
- * @param {number} size Atom size.
- * @param {string?} name Atom name.
- * @param {function((ProgressEvent|string))} onError Error callback.
- * @param {function()} onSuccess Success callback.
- */
-MpegParser.prototype.requestRead = function(
-    rootParser, file, filePos, size, name, onError, onSuccess) {
-  const self = this;
-  const reader = new FileReader();
-  reader.onerror = onError;
-  reader.onload = event => {
-    self.processTopLevelAtom(
-        /** @type {ArrayBuffer} */ (reader.result), rootParser, file, filePos,
-        size, name, onError, onSuccess);
-  };
-  this.vlog('reading @' + filePos + ':' + size);
-  reader.readAsArrayBuffer(file.slice(filePos, filePos + size));
-};
-
-/**
- * @param {ArrayBuffer} buf Data buffer.
- * @param {Object} rootParser Parser definition.
- * @param {File} file File.
- * @param {number} filePos Start position in the file.
- * @param {number} size Atom size.
- * @param {string?} name Atom name.
- * @param {function((ProgressEvent|string))} onError Error callback.
- * @param {function()} onSuccess Success callback.
- */
-MpegParser.prototype.processTopLevelAtom = function(
-    buf, rootParser, file, filePos, size, name, onError, onSuccess) {
-  try {
-    const br = new ByteReader(buf);
-
-    // the header has already been read.
-    const atomEnd = size - MpegParser.HEADER_SIZE;
-
-    const bufLength = buf.byteLength;
-
-    // Check the available data size. It should be either exactly
-    // what we requested or HEADER_SIZE bytes less (for the last atom).
-    if (bufLength != atomEnd && bufLength != size) {
-      throw 'Read failure @' + filePos + ', ' +
-          'requested ' + size + ', read ' + bufLength;
-    }
-
-    // Process the top level atom.
-    if (name) {  // name is null only the first time.
-      this.applyParser(
-          rootParser[name], br, {start: 0, end: atomEnd, name: name}, filePos);
-    }
-
-    filePos += bufLength;
-    if (bufLength == size) {
-      // The previous read returned everything we asked for, including
-      // the next atom header at the end of the buffer.
-      // Parse this header and schedule the next read.
-      br.seek(-MpegParser.HEADER_SIZE, ByteReader.SEEK_END);
-      let nextSize = MpegParser.readAtomSize(br);
-      const nextName = MpegParser.readAtomName(br);
-
-      // If we do not have a parser for the next atom, skip the content and
-      // read only the header (the one after the next).
-      if (!rootParser[nextName]) {
-        filePos += nextSize - MpegParser.HEADER_SIZE;
-        nextSize = MpegParser.HEADER_SIZE;
-      }
-
-      this.requestRead(
-          rootParser, file, filePos, nextSize, nextName, onError, onSuccess);
-    } else {
-      // The previous read did not return the next atom header, EOF reached.
-      this.vlog('EOF @' + filePos);
-      onSuccess();
-    }
-  } catch (e) {
-    onError(e.toString());
-  }
-};
-
 registerParserClass(MpegParser);
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/multi_metadata_provider.js b/ui/file_manager/file_manager/foreground/js/metadata/multi_metadata_provider.js
index 6e3c8426..9be61fe 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/multi_metadata_provider.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/multi_metadata_provider.js
@@ -2,51 +2,176 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/**
- * @param {!FileSystemMetadataProvider} fileSystemMetadataProvider
- * @param {!ExternalMetadataProvider} externalMetadataProvider
- * @param {!ContentMetadataProvider} contentMetadataProvider
- * @param {!VolumeManager} volumeManager
- * @constructor
- * @extends {MetadataProvider}
- * @struct
- */
-function MultiMetadataProvider(
-    fileSystemMetadataProvider, externalMetadataProvider,
-    contentMetadataProvider, volumeManager) {
-  MetadataProvider.call(
-      this,
-      FileSystemMetadataProvider.PROPERTY_NAMES
-          .concat(ExternalMetadataProvider.PROPERTY_NAMES)
-          .concat(ContentMetadataProvider.PROPERTY_NAMES));
+/** @final */
+class MultiMetadataProvider extends MetadataProvider {
+  /**
+   * @param {!FileSystemMetadataProvider} fileSystemMetadataProvider
+   * @param {!ExternalMetadataProvider} externalMetadataProvider
+   * @param {!ContentMetadataProvider} contentMetadataProvider
+   * @param {!VolumeManager} volumeManager
+   */
+  constructor(
+      fileSystemMetadataProvider, externalMetadataProvider,
+      contentMetadataProvider, volumeManager) {
+    super(FileSystemMetadataProvider.PROPERTY_NAMES
+              .concat(ExternalMetadataProvider.PROPERTY_NAMES)
+              .concat(ContentMetadataProvider.PROPERTY_NAMES));
+
+    /** @private @const {!FileSystemMetadataProvider} */
+    this.fileSystemMetadataProvider_ = fileSystemMetadataProvider;
+
+    /** @private @const {!ExternalMetadataProvider} */
+    this.externalMetadataProvider_ = externalMetadataProvider;
+
+    /** @private @const {!ContentMetadataProvider} */
+    this.contentMetadataProvider_ = contentMetadataProvider;
+
+    /** @private @const {!VolumeManager} */
+    this.volumeManager_ = volumeManager;
+  }
 
   /**
-   * @private {!FileSystemMetadataProvider}
-   * @const
+   * Obtains metadata for entries.
+   * @param {!Array<!MetadataRequest>} requests
+   * @return {!Promise<!Array<!MetadataItem>>}
    */
-  this.fileSystemMetadataProvider_ = fileSystemMetadataProvider;
+  get(requests) {
+    const fileSystemRequests = [];
+    const externalRequests = [];
+    const contentRequests = [];
+    const fallbackContentRequests = [];
+    requests.forEach(request => {
+      // Group property names.
+      const fileSystemPropertyNames = [];
+      const externalPropertyNames = [];
+      const contentPropertyNames = [];
+      const fallbackContentPropertyNames = [];
+      for (let i = 0; i < request.names.length; i++) {
+        const name = request.names[i];
+        const isFileSystemProperty =
+            FileSystemMetadataProvider.PROPERTY_NAMES.indexOf(name) !== -1;
+        const isExternalProperty =
+            ExternalMetadataProvider.PROPERTY_NAMES.indexOf(name) !== -1;
+        const isContentProperty =
+            ContentMetadataProvider.PROPERTY_NAMES.indexOf(name) !== -1;
+        assert(isFileSystemProperty || isExternalProperty || isContentProperty);
+        assert(!(isFileSystemProperty && isContentProperty));
+        // If the property can be obtained both from ExternalProvider and from
+        // ContentProvider, we can obtain the property from ExternalProvider
+        // without fetching file content. On the other hand, the values from
+        // ExternalProvider may be out of sync if the file is 'dirty'. Thus we
+        // fallback to ContentProvider if the file is dirty. See below.
+        if (isExternalProperty && isContentProperty) {
+          externalPropertyNames.push(name);
+          fallbackContentPropertyNames.push(name);
+          continue;
+        }
+        if (isFileSystemProperty) {
+          fileSystemPropertyNames.push(name);
+        }
+        if (isExternalProperty) {
+          externalPropertyNames.push(name);
+        }
+        if (isContentProperty) {
+          contentPropertyNames.push(name);
+        }
+      }
+      const volumeInfo = this.volumeManager_.getVolumeInfo(request.entry);
+      const addRequests = (list, names) => {
+        if (names.length) {
+          list.push(new MetadataRequest(request.entry, names));
+        }
+      };
+      if (volumeInfo &&
+          (volumeInfo.volumeType === VolumeManagerCommon.VolumeType.DRIVE ||
+           volumeInfo.volumeType === VolumeManagerCommon.VolumeType.PROVIDED)) {
+        // Because properties can be out of sync just after sync completion
+        // even if 'dirty' is false, it refers 'present' here to switch the
+        // content and the external providers.
+        if (fallbackContentPropertyNames.length &&
+            externalPropertyNames.indexOf('present') === -1) {
+          externalPropertyNames.push('present');
+        }
+        addRequests(externalRequests, externalPropertyNames);
+        addRequests(contentRequests, contentPropertyNames);
+        addRequests(fallbackContentRequests, fallbackContentPropertyNames);
+      } else if (
+          volumeInfo &&
+          volumeInfo.volumeType ===
+              VolumeManagerCommon.VolumeType.DOCUMENTS_PROVIDER) {
+        // We need to discard content requests when using a documents provider
+        // since the content sniffing code can't resolve the file path in the
+        // MediaGallery API. See crbug.com/942417
+        addRequests(fileSystemRequests, fileSystemPropertyNames);
+        addRequests(
+            externalRequests,
+            MultiMetadataProvider.DOCUMENTS_PROVIDER_EXTERNAL_PROPERTY_NAMES);
+      } else {
+        addRequests(fileSystemRequests, fileSystemPropertyNames);
+        addRequests(
+            contentRequests,
+            contentPropertyNames.concat(fallbackContentPropertyNames));
+      }
+    });
 
-  /**
-   * @private {!ExternalMetadataProvider}
-   * @const
-   */
-  this.externalMetadataProvider_ = externalMetadataProvider;
+    const get = (provider, inRequests) => {
+      return provider.get(inRequests).then(results => {
+        return {
+          requests: inRequests,
+          results: results,
+        };
+      });
+    };
+    const fileSystemPromise =
+        get(this.fileSystemMetadataProvider_, fileSystemRequests);
+    const externalPromise =
+        get(this.externalMetadataProvider_, externalRequests);
+    const contentPromise = get(this.contentMetadataProvider_, contentRequests);
+    const fallbackContentPromise = externalPromise.then(requestsAndResults => {
+      const requests = requestsAndResults.requests;
+      const results = requestsAndResults.results;
+      const dirtyMap = [];
+      for (let i = 0; i < results.length; i++) {
+        dirtyMap[requests[i].entry.toURL()] = results[i].present;
+      }
+      return get(
+          this.contentMetadataProvider_,
+          fallbackContentRequests.filter(request => {
+            return dirtyMap[request.entry.toURL()];
+          }));
+    });
 
-  /**
-   * @private {!ContentMetadataProvider}
-   * @const
-   */
-  this.contentMetadataProvider_ = contentMetadataProvider;
-
-  /**
-   * @private {!VolumeManager}
-   * @const
-   */
-  this.volumeManager_ = volumeManager;
+    // Merge results.
+    return Promise
+        .all([
+          fileSystemPromise,
+          externalPromise,
+          contentPromise,
+          fallbackContentPromise,
+        ])
+        .then(resultsList => {
+          const integratedResults = {};
+          for (let i = 0; i < resultsList.length; i++) {
+            const inRequests = resultsList[i].requests;
+            const results = resultsList[i].results;
+            assert(inRequests.length === results.length);
+            for (let j = 0; j < results.length; j++) {
+              const url = inRequests[j].entry.toURL();
+              integratedResults[url] =
+                  integratedResults[url] || new MetadataItem();
+              for (const name in results[j]) {
+                integratedResults[url][name] = results[j][name];
+              }
+            }
+          }
+          return requests.map(request => {
+            return integratedResults[request.entry.toURL()] ||
+                new MetadataItem();
+          });
+        });
+  }
 }
 
-MultiMetadataProvider.prototype.__proto__ = MetadataProvider.prototype;
-
 /**
  * Property names of documents-provider files which we should get from
  * ExternalMetadataProvider.
@@ -66,142 +191,3 @@
   'canRename',
   'canAddChildren',
 ];
-
-/**
- * Obtains metadata for entries.
- * @param {!Array<!MetadataRequest>} requests
- * @return {!Promise<!Array<!MetadataItem>>}
- */
-MultiMetadataProvider.prototype.get = function(requests) {
-  const fileSystemRequests = [];
-  const externalRequests = [];
-  const contentRequests = [];
-  const fallbackContentRequests = [];
-  requests.forEach(request => {
-    // Group property names.
-    const fileSystemPropertyNames = [];
-    const externalPropertyNames = [];
-    const contentPropertyNames = [];
-    const fallbackContentPropertyNames = [];
-    for (let i = 0; i < request.names.length; i++) {
-      const name = request.names[i];
-      const isFileSystemProperty =
-          FileSystemMetadataProvider.PROPERTY_NAMES.indexOf(name) !== -1;
-      const isExternalProperty =
-          ExternalMetadataProvider.PROPERTY_NAMES.indexOf(name) !== -1;
-      const isContentProperty =
-          ContentMetadataProvider.PROPERTY_NAMES.indexOf(name) !== -1;
-      assert(isFileSystemProperty || isExternalProperty || isContentProperty);
-      assert(!(isFileSystemProperty && isContentProperty));
-      // If the property can be obtained both from ExternalProvider and from
-      // ContentProvider, we can obtain the property from ExternalProvider
-      // without fetching file content. On the other hand, the values from
-      // ExternalProvider may be out of sync if the file is 'dirty'. Thus we
-      // fallback to ContentProvider if the file is dirty. See below.
-      if (isExternalProperty && isContentProperty) {
-        externalPropertyNames.push(name);
-        fallbackContentPropertyNames.push(name);
-        continue;
-      }
-      if (isFileSystemProperty) {
-        fileSystemPropertyNames.push(name);
-      }
-      if (isExternalProperty) {
-        externalPropertyNames.push(name);
-      }
-      if (isContentProperty) {
-        contentPropertyNames.push(name);
-      }
-    }
-    const volumeInfo = this.volumeManager_.getVolumeInfo(request.entry);
-    const addRequests = (list, names) => {
-      if (names.length) {
-        list.push(new MetadataRequest(request.entry, names));
-      }
-    };
-    if (volumeInfo &&
-        (volumeInfo.volumeType === VolumeManagerCommon.VolumeType.DRIVE ||
-         volumeInfo.volumeType === VolumeManagerCommon.VolumeType.PROVIDED)) {
-      // Because properties can be out of sync just after sync completion
-      // even if 'dirty' is false, it refers 'present' here to switch the
-      // content and the external providers.
-      if (fallbackContentPropertyNames.length &&
-          externalPropertyNames.indexOf('present') === -1) {
-        externalPropertyNames.push('present');
-      }
-      addRequests(externalRequests, externalPropertyNames);
-      addRequests(contentRequests, contentPropertyNames);
-      addRequests(fallbackContentRequests, fallbackContentPropertyNames);
-    } else if (
-        volumeInfo &&
-        volumeInfo.volumeType ===
-            VolumeManagerCommon.VolumeType.DOCUMENTS_PROVIDER) {
-      // We need to discard content requests when using a documents provider
-      // since the content sniffing code can't resolve the file path in the
-      // MediaGallery API. See crbug.com/942417
-      addRequests(fileSystemRequests, fileSystemPropertyNames);
-      addRequests(
-          externalRequests,
-          MultiMetadataProvider.DOCUMENTS_PROVIDER_EXTERNAL_PROPERTY_NAMES);
-    } else {
-      addRequests(fileSystemRequests, fileSystemPropertyNames);
-      addRequests(
-          contentRequests,
-          contentPropertyNames.concat(fallbackContentPropertyNames));
-    }
-  });
-
-  const get = (provider, inRequests) => {
-    return provider.get(inRequests).then(results => {
-      return {
-        requests: inRequests,
-        results: results,
-      };
-    });
-  };
-  const fileSystemPromise =
-      get(this.fileSystemMetadataProvider_, fileSystemRequests);
-  const externalPromise = get(this.externalMetadataProvider_, externalRequests);
-  const contentPromise = get(this.contentMetadataProvider_, contentRequests);
-  const fallbackContentPromise = externalPromise.then(requestsAndResults => {
-    const requests = requestsAndResults.requests;
-    const results = requestsAndResults.results;
-    const dirtyMap = [];
-    for (let i = 0; i < results.length; i++) {
-      dirtyMap[requests[i].entry.toURL()] = results[i].present;
-    }
-    return get(
-        this.contentMetadataProvider_,
-        fallbackContentRequests.filter(request => {
-          return dirtyMap[request.entry.toURL()];
-        }));
-  });
-
-  // Merge results.
-  return Promise
-      .all([
-        fileSystemPromise,
-        externalPromise,
-        contentPromise,
-        fallbackContentPromise,
-      ])
-      .then(resultsList => {
-        const integratedResults = {};
-        for (let i = 0; i < resultsList.length; i++) {
-          const inRequests = resultsList[i].requests;
-          const results = resultsList[i].results;
-          assert(inRequests.length === results.length);
-          for (let j = 0; j < results.length; j++) {
-            const url = inRequests[j].entry.toURL();
-            integratedResults[url] =
-                integratedResults[url] || new MetadataItem();
-            for (const name in results[j]) {
-              integratedResults[url][name] = results[j][name];
-            }
-          }
-        }
-        return requests.map(request => {
-          return integratedResults[request.entry.toURL()] || new MetadataItem();
-        });
-      });
-};
diff --git a/ui/file_manager/file_manager/foreground/js/toolbar_controller.js b/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
index bc1ab7b..2fc32aa 100644
--- a/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
@@ -18,10 +18,11 @@
    * @param {!FileSelectionHandler} selectionHandler
    * @param {!DirectoryModel} directoryModel
    * @param {!VolumeManager} volumeManager
+   * @param {!A11yAnnounce} a11y
    */
   constructor(
       toolbar, navigationList, listContainer, locationLine, selectionHandler,
-      directoryModel, volumeManager) {
+      directoryModel, volumeManager, a11y) {
     /**
      * @private {!HTMLElement}
      * @const
@@ -107,6 +108,12 @@
      */
     this.volumeManager_ = volumeManager;
 
+    /**
+     * @private {!A11yAnnounce}
+     * @const
+     */
+    this.a11y_ = a11y;
+
     this.selectionHandler_.addEventListener(
         FileSelectionHandler.EventType.CHANGE,
         this.onSelectionChanged_.bind(this));
@@ -209,6 +216,7 @@
    */
   onCancelSelectionButtonClicked_() {
     this.directoryModel_.selectEntries([]);
+    this.a11y_.speakA11yMessage(str('SELECTION_CANCELLATION'));
   }
 
   /**
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_grid.js b/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
index 4412e5f4..1b4c0017 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
@@ -70,6 +70,9 @@
     /** @private {?ObjectPropertyDescriptor|undefined} */
     this.dataModelDescriptor_ = null;
 
+    /** @public {?A11yAnnounce} */
+    this.a11y = null;
+
     throw new Error('Use FileGrid.decorate');
   }
 
@@ -102,14 +105,16 @@
    * @param {!MetadataModel} metadataModel File system metadata.
    * @param {!VolumeManager} volumeManager Volume manager instance.
    * @param {!importer.HistoryLoader} historyLoader
+   * @param {!A11yAnnounce} a11y
    */
-  static decorate(element, metadataModel, volumeManager, historyLoader) {
+  static decorate(element, metadataModel, volumeManager, historyLoader, a11y) {
     cr.ui.Grid.decorate(element);
     const self = /** @type {!FileGrid} */ (element);
     self.__proto__ = FileGrid.prototype;
     self.metadataModel_ = metadataModel;
     self.volumeManager_ = volumeManager;
     self.historyLoader_ = historyLoader;
+    self.a11y = a11y;
 
     self.listThumbnailLoader_ = null;
     self.beginIndex_ = 0;
@@ -135,6 +140,25 @@
   }
 
   /**
+   * @param {number} index Index of the list item.
+   * @return {string}
+   */
+  getItemLabel(index) {
+    if (index === -1) {
+      return '';
+    }
+
+    /** @type {Entry|FilesAppEntry} */
+    const entry = this.dataModel.item(index);
+    if (!entry) {
+      return '';
+    }
+
+    const locationInfo = this.volumeManager_.getLocationInfo(entry);
+    return util.getEntryLabel(locationInfo, entry);
+  }
+
+  /**
    * Sets list thumbnail loader.
    * @param {ListThumbnailLoader} listThumbnailLoader A list thumbnail loader.
    */
@@ -621,6 +645,7 @@
     bottom.appendChild(
         filelist.renderFileNameLabel(li.ownerDocument, entry, locationInfo));
     frame.appendChild(bottom);
+    li.setAttribute('file-name', util.getEntryLabel(locationInfo, entry));
 
     this.updateSharedStatus_(li, entry);
   }
@@ -1011,6 +1036,11 @@
     filelist.handleKeyDown.call(this, e);
   }
 
+  /** @return {!FileGrid} */
+  get filesView() {
+    return /** @type {!FileGrid} */ (this.grid_);
+  }
+
   /** @override */
   getIndexBelow(index) {
     if (this.isAccessibilityEnabled()) {
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_table.js b/ui/file_manager/file_manager/foreground/js/ui/file_table.js
index 1d80429..0f90659 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_table.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_table.js
@@ -405,8 +405,8 @@
     /** @private {?function(!Event)} */
     this.onThumbnailLoadedBound_ = null;
 
-    /** @private {?A11yAnnounce} */
-    this.a11y_ = null;
+    /** @public {?A11yAnnounce} */
+    this.a11y = null;
 
     throw new Error('Designed to decorate elements');
   }
@@ -431,7 +431,7 @@
     self.metadataModel_ = metadataModel;
     self.volumeManager_ = volumeManager;
     self.historyLoader_ = historyLoader;
-    self.a11y_ = a11y;
+    self.a11y = a11y;
 
     /** @private {ListThumbnailLoader} */
     self.listThumbnailLoader_ = null;
@@ -588,7 +588,7 @@
 
     // Delegate to parent to sort.
     super.sort(index);
-    this.a11y_.speakA11yMessage(msg);
+    this.a11y.speakA11yMessage(msg);
   }
 
   /**
@@ -861,6 +861,25 @@
   }
 
   /**
+   * @param {number} index Index of the list item.
+   * @return {string}
+   */
+  getItemLabel(index) {
+    if (index === -1) {
+      return '';
+    }
+
+    /** @type {Entry|FilesAppEntry} */
+    const entry = this.dataModel.item(index);
+    if (!entry) {
+      return '';
+    }
+
+    const locationInfo = this.volumeManager_.getLocationInfo(entry);
+    return util.getEntryLabel(locationInfo, entry);
+  }
+
+  /**
    * Render the Size column of the detail table.
    *
    * @param {Entry} entry The Entry object to render.
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_table_list.js b/ui/file_manager/file_manager/foreground/js/ui/file_table_list.js
index 957c187..8bed1cf 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_table_list.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_table_list.js
@@ -33,7 +33,7 @@
 
   /** @override */
   mergeItems(beginIndex, endIndex) {
-    cr.ui.table.TableList.prototype.mergeItems.call(this, beginIndex, endIndex);
+    super.mergeItems(beginIndex, endIndex);
 
     // Make sure that list item's selected attribute is updated just after the
     // mergeItems operation is done. This prevents checkmarks on selected items
@@ -44,7 +44,7 @@
         continue;
       }
       const isSelected = this.selectionModel.getIndexSelected(i);
-      if (item.selected != isSelected) {
+      if (item.selected !== isSelected) {
         item.selected = isSelected;
       }
     }
@@ -56,7 +56,20 @@
 
   /** @override */
   createSelectionController(sm) {
-    return new FileListSelectionController(assert(sm));
+    return new FileListSelectionController(assert(sm), this);
+  }
+
+  /** @return {A11yAnnounce} */
+  get a11y() {
+    return this.table.a11y;
+  }
+
+  /**
+   * @param {number} index Index of the list item.
+   * @return {string}
+   */
+  getItemLabel(index) {
+    return this.table.getItemLabel(index);
   }
 }
 
@@ -77,15 +90,16 @@
   /**
    * @param {!cr.ui.ListSelectionModel} selectionModel The selection model to
    *     interact with.
+   * @param {!FileTableList} tableList
    */
-  constructor(selectionModel) {
+  constructor(selectionModel, tableList) {
     super(selectionModel);
 
-    /**
-     * @type {!FileTapHandler}
-     * @const
-     */
+    /** @const @private {!FileTapHandler} */
     this.tapHandler_ = new FileTapHandler();
+
+    /** @const @private {!FileTableList} */
+    this.tableList_ = tableList;
   }
 
   /** @override */
@@ -108,6 +122,11 @@
   handleKeyDown(e) {
     filelist.handleKeyDown.call(this, e);
   }
+
+  /** @return {!FileTableList} */
+  get filesView() {
+    return this.tableList_;
+  }
 }
 
 /**
@@ -245,22 +264,25 @@
  * @param {!FileTapHandler.TapEvent} eventType
  * @return True if conducted any action. False when if did nothing special for
  *     tap.
- * @this {cr.ui.ListSelectionController}
+ * @this {cr.ui.ListSelectionController} either FileListSelectionController or
+ *     FileGridSelectionController.
  */
 filelist.handleTap = function(e, index, eventType) {
   const sm = /**
                 @type {!FileListSelectionModel|!FileListSingleSelectionModel}
                   */
       (this.selectionModel);
-  if (eventType == FileTapHandler.TapEvent.TWO_FINGER_TAP) {
+
+  if (eventType === FileTapHandler.TapEvent.TWO_FINGER_TAP) {
     // Prepare to open the context menu in the same manner as the right click.
     // If the target is any of the selected files, open a one for those files.
     // If the target is a non-selected file, cancel current selection and open
     // context menu for the single file.
     // Otherwise (when the target is the background), for the current folder.
-    if (index == -1) {
+    if (index === -1) {
       // Two-finger tap outside the list should be handled here because it does
       // not produce mousedown/click events.
+      this.filesView.a11y.speakA11yMessage(str('SELECTION_ALL_ENTRIES'));
       sm.unselectAll();
     } else {
       const indexSelected = sm.getIndexSelected(index);
@@ -274,6 +296,9 @@
         sm.beginChange();
         sm.selectedIndex = index;
         sm.endChange();
+        const name = this.filesView.getItemLabel(index);
+        this.filesView.a11y.speakA11yMessage(
+            strf('SELECTION_SINGLE_ENTRY', name));
       }
     }
 
@@ -281,35 +306,49 @@
     // 'contextmenu' event.
     return false;
   }
-  if (index == -1) {
+
+  if (index === -1) {
     return false;
   }
-  const isTap = eventType == FileTapHandler.TapEvent.TAP ||
-      eventType == FileTapHandler.TapEvent.LONG_TAP;
+
+  // Single finger tap.
+  const isTap = eventType === FileTapHandler.TapEvent.TAP ||
+      eventType === FileTapHandler.TapEvent.LONG_TAP;
   // Revert to click handling for single tap on checkbox or tap during rename.
   // Single tap on the checkbox in the list view mode should toggle select.
   // Single tap on input for rename should focus on input.
   const isCheckbox = e.target.classList.contains('detail-checkmark');
-  const isRename = e.target.localName == 'input';
-  if (eventType == FileTapHandler.TapEvent.TAP && (isCheckbox || isRename)) {
+  const isRename = e.target.localName === 'input';
+  if (eventType === FileTapHandler.TapEvent.TAP && (isCheckbox || isRename)) {
     return false;
   }
+
   if (sm.multiple && sm.getCheckSelectMode() && isTap && !e.shiftKey) {
     // toggle item selection. Equivalent to mouse click on checkbox.
     sm.beginChange();
+
+    const name = this.filesView.getItemLabel(index);
+    const msgId = sm.getIndexSelected(index) ? 'SELECTION_ADD_SINGLE_ENTRY' :
+                                               'SELECTION_REMOVE_SINGLE_ENTRY';
+    this.filesView.a11y.speakA11yMessage(strf(msgId, name));
+
     sm.setIndexSelected(index, !sm.getIndexSelected(index));
     // Toggle the current one and make it anchor index.
     sm.leadIndex = index;
     sm.anchorIndex = index;
     sm.endChange();
     return true;
-  } else if (sm.multiple && (eventType == FileTapHandler.TapEvent.LONG_PRESS)) {
+  } else if (
+      sm.multiple && (eventType === FileTapHandler.TapEvent.LONG_PRESS)) {
     sm.beginChange();
     if (!sm.getCheckSelectMode()) {
       // Make sure to unselect the leading item that was not the touch target.
       sm.unselectAll();
       sm.setCheckSelectMode(true);
     }
+    const name = this.filesView.getItemLabel(index);
+    this.filesView.a11y.speakA11yMessage(
+        strf('SELECTION_ADD_SINGLE_ENTRY', name));
     sm.setIndexSelected(index, true);
     sm.leadIndex = index;
     sm.anchorIndex = index;
@@ -317,11 +356,13 @@
     return true;
     // Do not toggle selection yet, so as to avoid unselecting before drag.
   } else if (
-      eventType == FileTapHandler.TapEvent.TAP && !sm.getCheckSelectMode()) {
+      eventType === FileTapHandler.TapEvent.TAP && !sm.getCheckSelectMode()) {
     // Single tap should open the item with default action.
     // Select the item, so that MainWindowComponent will execute action of it.
     sm.beginChange();
     sm.unselectAll();
+    const name = this.filesView.getItemLabel(index);
+    this.filesView.a11y.speakA11yMessage(strf('SELECTION_SINGLE_ENTRY', name));
     sm.setIndexSelected(index, true);
     sm.leadIndex = index;
     sm.anchorIndex = index;
@@ -345,15 +386,17 @@
  * @param {!Event} e The browser mouse event.
  * @param {number} index The index that was under the mouse pointer, -1 if
  *     none.
- * @this {cr.ui.ListSelectionController}
+ * @this {cr.ui.ListSelectionController} either FileListSelectionController or
+ *     FileGridSelectionController.
  */
 filelist.handlePointerDownUp = function(e, index) {
   const sm = /**
                 @type {!FileListSelectionModel|!FileListSingleSelectionModel}
                   */
       (this.selectionModel);
+
   const anchorIndex = sm.anchorIndex;
-  const isDown = (e.type == 'mousedown');
+  const isDown = (e.type === 'mousedown');
 
   const isTargetCheckmark = e.target.classList.contains('detail-checkmark') ||
       e.target.classList.contains('checkmark');
@@ -361,12 +404,13 @@
   // modifiers(Ctrl/Shift), the click should toggle the item's selection.
   // (i.e. same behavior as Ctrl+Click)
   const isClickOnCheckmark =
-      (isTargetCheckmark && sm.multiple && index != -1 && !e.shiftKey &&
-       !e.ctrlKey && e.button == 0);
+      (isTargetCheckmark && sm.multiple && index !== -1 && !e.shiftKey &&
+       !e.ctrlKey && e.button === 0);
 
   sm.beginChange();
 
-  if (index == -1) {
+  if (index === -1) {
+    this.filesView.a11y.speakA11yMessage(str('SELECTION_CANCELLATION'));
     sm.leadIndex = sm.anchorIndex = -1;
     sm.unselectAll();
   } else {
@@ -387,24 +431,37 @@
         sm.setCheckSelectMode(true);
 
         // Toggle the current one and make it anchor index.
+        const name = this.filesView.getItemLabel(index);
+        const msgId = sm.getIndexSelected(index) ?
+            'SELECTION_REMOVE_SINGLE_ENTRY' :
+            'SELECTION_ADD_SINGLE_ENTRY';
+        this.filesView.a11y.speakA11yMessage(strf(msgId, name));
         sm.setIndexSelected(index, !sm.getIndexSelected(index));
         sm.leadIndex = index;
         sm.anchorIndex = index;
       }
-    } else if (e.shiftKey && anchorIndex != -1 && anchorIndex != index) {
+    } else if (e.shiftKey && anchorIndex !== -1 && anchorIndex !== index) {
       // Shift is done in mousedown.
       if (isDown) {
         sm.unselectAll();
         sm.leadIndex = index;
         if (sm.multiple) {
           sm.selectRange(anchorIndex, index);
+          const nameStart = this.filesView.getItemLabel(anchorIndex);
+          const nameEnd = this.filesView.getItemLabel(index);
+          const count = Math.abs(index - anchorIndex) + 1;
+          const msg = strf('SELECTION_ADD_RANGE', count, nameStart, nameEnd);
+          this.filesView.a11y.speakA11yMessage(msg);
         } else {
+          const name = this.filesView.getItemLabel(index);
+          this.filesView.a11y.speakA11yMessage(
+              strf('SELECTION_SINGLE_ENTRY', name));
           sm.setIndexSelected(index, true);
         }
       }
     } else {
       // Right click for a context menu needs to not clear the selection.
-      const isRightClick = e.button == 2;
+      const isRightClick = e.button === 2;
 
       // If the index is selected this is handled in mouseup.
       const indexSelected = sm.getIndexSelected(index);
@@ -419,6 +476,13 @@
           sm.unselectAll();
           sm.beginChange();
         }
+        // This event handler is called for mouseup and mousedown, let's
+        // announce the selection only in one of them.
+        if (isDown) {
+          const name = this.filesView.getItemLabel(index);
+          this.filesView.a11y.speakA11yMessage(
+              strf('SELECTION_SINGLE_ENTRY', name));
+        }
         sm.selectedIndex = index;
       }
     }
@@ -437,7 +501,8 @@
  *    check-select mode should be terminated.
  *
  * @param {Event} e The keydown event.
- * @this {cr.ui.ListSelectionController}
+ * @this {cr.ui.ListSelectionController} either FileListSelectionController or
+ *     FileGridSelectionController.
  */
 filelist.handleKeyDown = function(e) {
   const tagName = e.target.tagName;
@@ -445,20 +510,20 @@
   // If focus is in an input field of some kind, only handle navigation keys
   // that aren't likely to conflict with input interaction (e.g., text
   // editing, or changing the value of a checkbox or select).
-  if (tagName == 'INPUT') {
+  if (tagName === 'INPUT') {
     const inputType = e.target.type;
     // Just protect space (for toggling) for checkbox and radio.
-    if (inputType == 'checkbox' || inputType == 'radio') {
-      if (e.key == ' ') {
+    if (inputType === 'checkbox' || inputType === 'radio') {
+      if (e.key === ' ') {
         return;
       }
       // Protect all but the most basic navigation commands in anything else.
-    } else if (e.key != 'ArrowUp' && e.key != 'ArrowDown') {
+    } else if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') {
       return;
     }
   }
   // Similarly, don't interfere with select element handling.
-  if (tagName == 'SELECT') {
+  if (tagName === 'SELECT') {
     return;
   }
 
@@ -470,9 +535,12 @@
   const leadIndex = sm.leadIndex;
   let prevent = true;
 
-  // Ctrl/Meta+A
-  if (sm.multiple && e.keyCode == 65 &&
+  // Ctrl/Meta+A. Use keyCode=65 to use the same shortcut key regardless of
+  // keyboard layout.
+  const pressedKeyA = e.keyCode === 65 || e.key === 'a';
+  if (sm.multiple && pressedKeyA &&
       (cr.isMac && e.metaKey || !cr.isMac && e.ctrlKey)) {
+    this.filesView.a11y.speakA11yMessage(str('SELECTION_ALL_ENTRIES'));
     sm.setCheckSelectMode(true);
     sm.selectAll();
     e.preventDefault();
@@ -481,6 +549,7 @@
 
   // Esc
   if (e.key === 'Escape' && !e.ctrlKey && !e.shiftKey) {
+    this.filesView.a11y.speakA11yMessage(str('SELECTION_CANCELLATION'));
     sm.unselectAll();
     e.preventDefault();
     return;
@@ -488,8 +557,8 @@
 
   // Space: Note ChromeOS and ChromeOS on Linux can generate KeyDown Space
   // events differently the |key| attribute might be set to 'Unidentified'.
-  if (e.code == 'Space' || e.key === ' ') {
-    if (leadIndex != -1) {
+  if (e.code === 'Space' || e.key === ' ') {
+    if (leadIndex !== -1) {
       const selected = sm.getIndexSelected(leadIndex);
       if (e.ctrlKey) {
         sm.beginChange();
@@ -501,9 +570,16 @@
           // It needs to go back/forth to trigger the 'change' event.
           sm.setIndexSelected(leadIndex, false);
           sm.setIndexSelected(leadIndex, true);
+          const name = this.filesView.getItemLabel(leadIndex);
+          this.filesView.a11y.speakA11yMessage(
+              strf('SELECTION_SINGLE_ENTRY', name));
         } else {
           // Toggle the current one and make it anchor index.
           sm.setIndexSelected(leadIndex, !selected);
+          const name = this.filesView.getItemLabel(leadIndex);
+          const msgId = selected ? 'SELECTION_REMOVE_SINGLE_ENTRY' :
+                                   'SELECTION_ADD_SINGLE_ENTRY';
+          this.filesView.a11y.speakA11yMessage(strf(msgId, name));
         }
 
         // Force check-select, FileListSelectionModel.onChangeEvent_ resets it
@@ -527,22 +603,22 @@
       newIndex = this.getLastIndex();
       break;
     case 'ArrowUp':
-      newIndex =
-          leadIndex == -1 ? this.getLastIndex() : this.getIndexAbove(leadIndex);
+      newIndex = leadIndex === -1 ? this.getLastIndex() :
+                                    this.getIndexAbove(leadIndex);
       break;
     case 'ArrowDown':
-      newIndex = leadIndex == -1 ? this.getFirstIndex() :
-                                   this.getIndexBelow(leadIndex);
+      newIndex = leadIndex === -1 ? this.getFirstIndex() :
+                                    this.getIndexBelow(leadIndex);
       break;
     case 'ArrowLeft':
     case 'MediaTrackPrevious':
-      newIndex = leadIndex == -1 ? this.getLastIndex() :
-                                   this.getIndexBefore(leadIndex);
+      newIndex = leadIndex === -1 ? this.getLastIndex() :
+                                    this.getIndexBefore(leadIndex);
       break;
     case 'ArrowRight':
     case 'MediaTrackNext':
-      newIndex = leadIndex == -1 ? this.getFirstIndex() :
-                                   this.getIndexAfter(leadIndex);
+      newIndex = leadIndex === -1 ? this.getFirstIndex() :
+                                    this.getIndexAfter(leadIndex);
       break;
     default:
       prevent = false;
@@ -557,10 +633,18 @@
       if (sm.multiple) {
         sm.unselectAll();
       }
-      if (anchorIndex == -1) {
+      if (anchorIndex === -1) {
+        const name = this.filesView.getItemLabel(newIndex);
+        this.filesView.a11y.speakA11yMessage(
+            strf('SELECTION_SINGLE_ENTRY', name));
         sm.setIndexSelected(newIndex, true);
         sm.anchorIndex = newIndex;
       } else {
+        const nameStart = this.filesView.getItemLabel(anchorIndex);
+        const nameEnd = this.filesView.getItemLabel(newIndex);
+        const count = Math.abs(newIndex - anchorIndex) + 1;
+        const msg = strf('SELECTION_ADD_RANGE', count, nameStart, nameEnd);
+        this.filesView.a11y.speakA11yMessage(msg);
         sm.selectRange(anchorIndex, newIndex);
       }
     } else if (e.ctrlKey) {
@@ -574,6 +658,9 @@
       if (sm.multiple) {
         sm.unselectAll();
       }
+      const name = this.filesView.getItemLabel(newIndex);
+      this.filesView.a11y.speakA11yMessage(
+          strf('SELECTION_SINGLE_ENTRY', name));
       sm.setIndexSelected(newIndex, true);
       sm.anchorIndex = newIndex;
     }
diff --git a/ui/file_manager/file_manager/test/js/strings.js b/ui/file_manager/file_manager/test/js/strings.js
index 7a32af3..2660169 100644
--- a/ui/file_manager/file_manager/test/js/strings.js
+++ b/ui/file_manager/file_manager/test/js/strings.js
@@ -16,6 +16,7 @@
   'GOOGLE_DRIVE_OVERVIEW_URL':
       'https://support.google.com/chromebook/?p=filemanager_drive',
   'HIDE_SPACE_INFO': false,
+  'ARC_USB_STORAGE_UI_ENABLED': true,
   'MY_FILES_VOLUME_ENABLED': true,
   'PLUGIN_VM_ENABLED': true,
   'UI_LOCALE': 'en_US',
diff --git a/ui/file_manager/gallery/js/image_editor/exif_encoder_unittest.js b/ui/file_manager/gallery/js/image_editor/exif_encoder_unittest.js
index ca3df793..0bb1b298 100644
--- a/ui/file_manager/gallery/js/image_editor/exif_encoder_unittest.js
+++ b/ui/file_manager/gallery/js/image_editor/exif_encoder_unittest.js
@@ -5,13 +5,27 @@
 'use strict';
 
 /**
+ * @implements {MetadataParserLogger}
+ * @final
+ */
+class NoLogger {
+  constructor() {
+    this.verbose = false;
+  }
+
+  error() {}
+  log() {}
+  vlog() {}
+}
+
+/**
  * Test case for ordinal exif encoding and decoding.
  */
 function testExifEncodeAndDecode() {
-  var canvas = getSampleCanvas();
-  var data = canvas.toDataURL('image/jpeg');
+  const canvas = getSampleCanvas();
+  const data = canvas.toDataURL('image/jpeg');
 
-  var metadata = /** @type {!MetadataItem} */ ({
+  const metadata = /** @type {!MetadataItem} */ ({
     mediaMimeType: 'image/jpeg',
     modificationTime: new Date(2015, 0, 7, 15, 30, 6),
     ifd: {
@@ -30,8 +44,7 @@
       },
       exif: {
         // Lens model
-        42036:
-            {id: 0xa434, format: 2, componentCount: 10, value: 'LensModel\0'}
+        42036: {id: 0xa434, format: 2, componentCount: 10, value: 'LensModel\0'}
       },
       gps: {
         // GPS latitude ref
@@ -40,38 +53,29 @@
     }
   });
 
-  var encoder = ImageEncoder.encodeMetadata(metadata, canvas, 1);
+  const encoder = ImageEncoder.encodeMetadata(metadata, canvas, 1);
 
   // Assert that ExifEncoder is returned.
   assertTrue(encoder instanceof ExifEncoder);
 
-  var encodedResult = encoder.encode();
+  const encodedResult = encoder.encode();
 
   // Decode encoded exif data.
-  var exifParser = new ExifParser(this);
-
-  // Redirect .log and .vlog to console.log for debugging.
-  exifParser.log = function(arg) {
-    console.log(arg);
-  };
-  exifParser.vlog = function(arg) {
-    console.log(arg);
-  };
-
-  var parsedMetadata = {};
-  var byteReader = new ByteReader(encodedResult);
-  byteReader.readString(2 + 2); // Skip marker and size.
+  const exifParser = new ExifParser(new NoLogger());
+  const parsedMetadata = {};
+  const byteReader = new ByteReader(encodedResult);
+  byteReader.readString(2 + 2);  // Skip marker and size.
   exifParser.parseExifSection(parsedMetadata, encodedResult, byteReader);
 
   // Check ifd.image.
-  assertEquals(1, parsedMetadata.ifd.image[0x112].value); // Orientation
+  assertEquals(1, parsedMetadata.ifd.image[0x112].value);  // Orientation
 
   // Since thumbnail is compressed with JPEG, compression must be 6.
   assertEquals(6, parsedMetadata.ifd.image[0x102].value);
 
   // Check ifd.exif.
-  assertEquals(1920, parsedMetadata.ifd.exif[0xA002].value); // PixelXDimension
-  assertEquals(1080, parsedMetadata.ifd.exif[0xA003].value); // PixelYDimension
+  assertEquals(1920, parsedMetadata.ifd.exif[0xA002].value);  // PixelXDimension
+  assertEquals(1080, parsedMetadata.ifd.exif[0xA003].value);  // PixelYDimension
 
   // These fields should be copied correctly.
   // Manufacture
@@ -84,8 +88,8 @@
   assertEquals('N\0', parsedMetadata.ifd.gps[0x1].value);
 
   // Software should be set as the Gallery app
-  assertEquals('Chrome OS Gallery App\0',
-      parsedMetadata.ifd.image[0x131].value);
+  assertEquals(
+      'Chrome OS Gallery App\0', parsedMetadata.ifd.image[0x131].value);
 
   // Datetime should be updated.
   assertEquals('2015:01:07 15:30:06\0', parsedMetadata.ifd.image[0x132].value);
@@ -100,13 +104,13 @@
  * @return {number} Expected thumbnail size.
  */
 function measureExpectedThumbnailSize_() {
-  var canvas = getSampleCanvas();
-  var metadata = /** @type {!MetadataItem} */ ({
+  const canvas = getSampleCanvas();
+  const metadata = /** @type {!MetadataItem} */ ({
     mediaMimeType: 'image/jpeg',
     modificationTime: new Date(2015, 0, 7, 15, 30, 6)
   });
 
-  var encoder = ImageEncoder.encodeMetadata(metadata, canvas, 1);
+  const encoder = ImageEncoder.encodeMetadata(metadata, canvas, 1);
   /** @suppress {accessControls} */
   function getThumbnailDataUrlForTest() {
     return encoder.thumbnailDataUrl;
@@ -122,13 +126,13 @@
  * @param {boolean} expectThumbnail True if thumbnail is expected to be written.
  */
 function largeExifDataTestHelper_(largeFieldValueSize, expectThumbnail) {
-  var canvas = getSampleCanvas();
+  const canvas = getSampleCanvas();
 
   // Generate a long string.
-  var longString = '0'.repeat(largeFieldValueSize - 1);
+  let longString = '0'.repeat(largeFieldValueSize - 1);
   longString += '\0';
 
-  var metadata = /** @type{!MetadataItem} */ ({
+  const metadata = /** @type{!MetadataItem} */ ({
     mediaMimeType: 'image/jpeg',
     modificationTime: new Date(2015, 0, 7, 15, 30, 6),
     ifd: {
@@ -144,17 +148,16 @@
     }
   });
 
-  var encoder = ImageEncoder.encodeMetadata(metadata, canvas, 1);
+  const encoder = ImageEncoder.encodeMetadata(metadata, canvas, 1);
 
   // For failure case, an error is thrown.
-  var encodedResult = encoder.encode();
+  const encodedResult = encoder.encode();
 
   // Decode encoded exif data and check thumbnail is written or not.
-  var exifParser =
-      new ExifParser(/** @type{MetadataParserLogger} */ ({verbose: false}));
-  var parsedMetadata = {};
-  var byteReader = new ByteReader(encodedResult);
-  byteReader.readString(2 + 2); // Skip marker and size.
+  const exifParser = new ExifParser(new NoLogger());
+  const parsedMetadata = {};
+  const byteReader = new ByteReader(encodedResult);
+  byteReader.readString(2 + 2);  // Skip marker and size.
   exifParser.parseExifSection(parsedMetadata, encodedResult, byteReader);
 
   assertEquals(expectThumbnail, !!parsedMetadata.thumbnailURL);
@@ -167,7 +170,7 @@
   // 158 bytes: other exif data except value of the large field.
   largeExifDataTestHelper_(
       ExifEncoder.MAXIMUM_EXIF_DATA_SIZE - measureExpectedThumbnailSize_() -
-      ExifEncoder.THUMBNAIL_METADATA_SIZE - 158 - 1,
+          ExifEncoder.THUMBNAIL_METADATA_SIZE - 158 - 1,
       true);
 }
 
@@ -177,7 +180,7 @@
 function testLargeExifDataBoundaryCase() {
   largeExifDataTestHelper_(
       ExifEncoder.MAXIMUM_EXIF_DATA_SIZE - measureExpectedThumbnailSize_() -
-      ExifEncoder.THUMBNAIL_METADATA_SIZE - 158,
+          ExifEncoder.THUMBNAIL_METADATA_SIZE - 158,
       true);
 }
 
@@ -187,6 +190,6 @@
 function testLargeExifDataExceedsCase() {
   largeExifDataTestHelper_(
       ExifEncoder.MAXIMUM_EXIF_DATA_SIZE - measureExpectedThumbnailSize_() -
-      ExifEncoder.THUMBNAIL_METADATA_SIZE - 158 + 1,
+          ExifEncoder.THUMBNAIL_METADATA_SIZE - 158 + 1,
       false);
 }
diff --git a/ui/file_manager/integration_tests/file_manager/file_list.js b/ui/file_manager/integration_tests/file_manager/file_list.js
index dcaea20..0d5dc9f 100644
--- a/ui/file_manager/integration_tests/file_manager/file_list.js
+++ b/ui/file_manager/integration_tests/file_manager/file_list.js
@@ -142,4 +142,158 @@
     chrome.test.assertEq(1, selectedRows.length);
     chrome.test.assertEq(2, fileRows.indexOf(selectedRows[0]));
   };
+
+  /**
+   * Verifies the total number of a11y messages and asserts the latest message
+   * is the expected one.
+   *
+   * @param {string} appId
+   * @param {number} expectedCount
+   * @param {string} expectedMessage
+   * @return {string} Latest a11y message.
+   */
+  async function countAndCheckLatestA11yMessage(
+      appId, expectedCount, expectedMessage) {
+    const a11yMessages =
+        await remoteCall.callRemoteTestUtil('getA11yAnnounces', appId, []);
+    chrome.test.assertEq(
+        expectedCount, a11yMessages.length, 'Wrong number of a11y messages');
+    const latestMessage = a11yMessages[a11yMessages.length - 1];
+    chrome.test.assertEq(expectedMessage, latestMessage);
+    return latestMessage;
+  }
+
+  /**
+   * Tests that selecting/de-selecting files with keyboard produces a11y
+   * messages.
+   *
+   * NOTE: Test shared with grid_view.js.
+   * @param {boolean=} isGridView if the test is testing the grid view.
+   */
+  testcase.fileListKeyboardSelectionA11y = async (isGridView) => {
+    const appId = await setupAndWaitUntilReady(
+        RootPath.DOWNLOADS, BASIC_LOCAL_ENTRY_SET, []);
+
+    let a11yMsgCount = 0;
+    const viewSelector = isGridView ? 'grid#file-list' : '#file-list';
+    if (isGridView) {
+      // Click view-button again to switch to detail view.
+      await remoteCall.waitAndClickElement(appId, '#view-button');
+
+      // Clicking #view-button adds 1 a11y message.
+      ++a11yMsgCount;
+    }
+
+    // Keys used for keyboard navigation in the file list.
+    const homeKey = [viewSelector, 'Home', false, false, false];
+    const ctrlDownKey = [viewSelector, 'ArrowDown', true, false, false];
+    const ctrlSpaceKey = [viewSelector, ' ', true, false, false];
+    const shiftEndKey = [viewSelector, 'End', false, true, false];
+    const ctrlAKey = [viewSelector + ' li', 'a', true, false, false];
+    const escKey = [viewSelector, 'Escape', false, false, false];
+
+    // Select first item with Home key.
+    await remoteCall.fakeKeyDown(appId, ...homeKey);
+
+    // Check: Announced "photos" directory selection.
+    await countAndCheckLatestA11yMessage(
+        appId, ++a11yMsgCount, 'Selected photos.');
+
+    // Ctrl+Down & Ctrl+Space to select second item: Beautiful Song.ogg
+    await remoteCall.fakeKeyDown(appId, ...ctrlDownKey);
+    await remoteCall.fakeKeyDown(appId, ...ctrlSpaceKey);
+
+    // Check: Announced "Beautiful Song.add" added to selection.
+    await countAndCheckLatestA11yMessage(
+        appId, ++a11yMsgCount, 'Added Beautiful Song.ogg to selection.');
+
+    // Shift+End to select from 2nd item to the last item.
+    await remoteCall.fakeKeyDown(appId, ...shiftEndKey);
+
+    // Check: Announced range selection from "Beautiful Song.add" to hello.txt.
+    await countAndCheckLatestA11yMessage(
+        appId, ++a11yMsgCount,
+        'Selected a range of 4 entries from Beautiful Song.ogg to hello.txt.');
+
+    // Ctrl+Space to de-select currently focused item (last item).
+    await remoteCall.fakeKeyDown(appId, ...ctrlSpaceKey);
+
+    // Check: Announced de-selecting hello.txt
+    await countAndCheckLatestA11yMessage(
+        appId, ++a11yMsgCount, 'Removed hello.txt from selection.');
+
+    // Ctrl+A to select all items.
+    await remoteCall.fakeKeyDown(appId, ...ctrlAKey);
+
+    // Check: Announced selecting all entries.
+    await countAndCheckLatestA11yMessage(
+        appId, ++a11yMsgCount, 'Selected all entries.');
+
+    // Esc key to deselect all.
+    await remoteCall.fakeKeyDown(appId, ...escKey);
+
+    // Check: Announced deselecting all entries.
+    await countAndCheckLatestA11yMessage(
+        appId, ++a11yMsgCount, 'Removed all entries from selection.');
+  };
+
+  /**
+   * Tests that selecting/de-selecting files with mouse produces a11y messages.
+   *
+   * NOTE: Test shared with grid_view.js.
+   * @param {boolean=} isGridView if the test is testing the grid view.
+   */
+  testcase.fileListMouseSelectionA11y = async (isGridView) => {
+    const appId = await setupAndWaitUntilReady(
+        RootPath.DOWNLOADS, BASIC_LOCAL_ENTRY_SET, []);
+
+    let a11yMsgCount = 0;
+    if (isGridView) {
+      // Click view-button again to switch to detail view.
+      await remoteCall.waitAndClickElement(appId, '#view-button');
+
+      // Clicking #view-button adds 1 a11y message.
+      ++a11yMsgCount;
+    }
+
+    // Click first item.
+    await remoteCall.waitAndClickElement(
+        appId, '#file-list [file-name="photos"]');
+
+    // Check: Announced "photos" directory selection.
+    await countAndCheckLatestA11yMessage(
+        appId, ++a11yMsgCount, 'Selected photos.');
+
+    // Ctrl+Click second item.
+    await remoteCall.waitAndClickElement(
+        appId, '#file-list [file-name="Beautiful Song.ogg"]', {ctrl: true});
+
+    // Check: Announced "Beautiful Song.add" added to selection.
+    await countAndCheckLatestA11yMessage(
+        appId, ++a11yMsgCount, 'Added Beautiful Song.ogg to selection.');
+
+    // Shift+Click last item.
+    await remoteCall.waitAndClickElement(
+        appId, '#file-list [file-name="hello.txt"]', {shift: true});
+
+    // Check: Announced range selection from "Beautiful Song.add" to hello.txt.
+    await countAndCheckLatestA11yMessage(
+        appId, ++a11yMsgCount,
+        'Selected a range of 4 entries from Beautiful Song.ogg to hello.txt.');
+
+    // Ctrl+Click to de-select the last item.
+    await remoteCall.waitAndClickElement(
+        appId, '#file-list [file-name="hello.txt"]', {ctrl: true});
+
+    // Check: Announced de-selecting hello.txt
+    await countAndCheckLatestA11yMessage(
+        appId, ++a11yMsgCount, 'Removed hello.txt from selection.');
+
+    // Click on "Cancel selection" button.
+    await remoteCall.waitAndClickElement(appId, '#cancel-selection-button');
+
+    // Check: Announced deselecting all entries.
+    await countAndCheckLatestA11yMessage(
+        appId, ++a11yMsgCount, 'Removed all entries from selection.');
+  };
 })();
diff --git a/ui/file_manager/integration_tests/file_manager/grid_view.js b/ui/file_manager/integration_tests/file_manager/grid_view.js
index 33bd87a2..0981fac 100644
--- a/ui/file_manager/integration_tests/file_manager/grid_view.js
+++ b/ui/file_manager/integration_tests/file_manager/grid_view.js
@@ -85,3 +85,20 @@
   chrome.test.assertEq(2, a11yMessages.length, 'Missing a11y message');
   chrome.test.assertEq('File list has changed to list view.', a11yMessages[1]);
 };
+
+/**
+ * Tests that selecting/de-selecting files with keyboard produces a11y
+ * messages.
+ */
+testcase.showGridViewKeyboardSelectionA11y = async () => {
+  const isGridView = true;
+  return testcase.fileListKeyboardSelectionA11y(isGridView);
+};
+
+/**
+ * Tests that selecting/de-selecting files with mouse produces a11y messages.
+ */
+testcase.showGridViewMouseSelectionA11y = async () => {
+  const isGridView = true;
+  return testcase.fileListMouseSelectionA11y(isGridView);
+};
diff --git a/ui/file_manager/integration_tests/file_manager/open_image_files.js b/ui/file_manager/integration_tests/file_manager/open_image_files.js
index 1844223..07be56c 100644
--- a/ui/file_manager/integration_tests/file_manager/open_image_files.js
+++ b/ui/file_manager/integration_tests/file_manager/open_image_files.js
@@ -76,8 +76,8 @@
 
     // Check that opening the file was announced to screen reader.
     chrome.test.assertTrue(a11yMessages instanceof Array);
-    chrome.test.assertEq(1, a11yMessages.length);
-    chrome.test.assertEq('Opening file image3.jpg.', a11yMessages[0]);
+    chrome.test.assertEq(3, a11yMessages.length);
+    chrome.test.assertEq('Opening file image3.jpg.', a11yMessages[2]);
 
     // Check: the Gallery window should open.
     const galleryAppId = await galleryApp.waitForWindow('gallery.html');
diff --git a/ui/file_manager/integration_tests/remote_call.js b/ui/file_manager/integration_tests/remote_call.js
index 705e66c..8f7de5d8 100644
--- a/ui/file_manager/integration_tests/remote_call.js
+++ b/ui/file_manager/integration_tests/remote_call.js
@@ -330,12 +330,15 @@
  *     If query is an array, |query[0]| specifies the first
  *     element(s), |query[1]| specifies elements inside the shadow DOM of
  *     the first element, and so on.
+ * @param {{shift: boolean, alt: boolean, ctrl: boolean}=} opt_keyModifiers
+ *     Object
  * @param {Promise} Promise to be fulfilled with the clicked element.
  */
-RemoteCall.prototype.waitAndClickElement = async function(windowId, query) {
+RemoteCall.prototype.waitAndClickElement =
+    async function(windowId, query, opt_keyModifiers) {
   const element = await this.waitForElement(windowId, query);
-  const result =
-      await this.callRemoteTestUtil('fakeMouseClick', windowId, [query]);
+  const result = await this.callRemoteTestUtil(
+      'fakeMouseClick', windowId, [query, opt_keyModifiers]);
   chrome.test.assertTrue(result, 'mouse click failed.');
   return element;
 };
diff --git a/ui/file_manager/integration_tests/test_util.js b/ui/file_manager/integration_tests/test_util.js
index 19db92e0..b173cc2 100644
--- a/ui/file_manager/integration_tests/test_util.js
+++ b/ui/file_manager/integration_tests/test_util.js
@@ -673,6 +673,17 @@
     typeText: 'Plain text',
   }),
 
+  // Note, no MIME type here, this is used for testing file content sniffing.
+  plainText: new TestEntryInfo({
+    type: EntryType.FILE,
+    sourceFileName: 'plaintext',
+    targetPath: 'plaintext',
+    lastModifiedTime: 'Sep 4, 1998, 12:34 PM',
+    nameText: 'plaintext',
+    sizeText: '32 bytes',
+    typeText: 'Plain text',
+  }),
+
   tallHtml: new TestEntryInfo({
     type: EntryType.FILE,
     sourceFileName: 'tall.html',
diff --git a/ui/gfx/font.h b/ui/gfx/font.h
index ebf85a212..72614df 100644
--- a/ui/gfx/font.h
+++ b/ui/gfx/font.h
@@ -124,9 +124,11 @@
   NativeFont GetNativeFont() const;
 #endif
 
+#if defined(OS_WIN)
   // Raw access to the underlying platform font implementation. Can be
   // static_cast to a known implementation type if needed.
   PlatformFont* platform_font() const { return platform_font_.get(); }
+#endif
 
  private:
   // Wrapped platform font implementation.
diff --git a/ui/gfx/font_unittest.cc b/ui/gfx/font_unittest.cc
index 04b28e7..4c75a35 100644
--- a/ui/gfx/font_unittest.cc
+++ b/ui/gfx/font_unittest.cc
@@ -13,7 +13,6 @@
 #include "ui/gfx/font_names_testing.h"
 
 #if defined(OS_WIN)
-#include "ui/gfx/platform_font_win.h"
 #include "ui/gfx/system_fonts_win.h"
 #endif
 
@@ -22,6 +21,19 @@
 
 using FontTest = testing::Test;
 
+TEST_F(FontTest, DefaultFont) {
+  Font cf;
+  EXPECT_EQ(cf.GetStyle(), Font::NORMAL);
+  EXPECT_EQ(cf.GetWeight(), Font::Weight::NORMAL);
+  // Ensures that font metrics are generated. Some fonts backends do not provide
+  // some metrics (e.g. DWrite do not produce average character width).
+  EXPECT_GT(cf.GetFontSize(), 0);
+  EXPECT_GT(cf.GetHeight(), 0);
+  EXPECT_GT(cf.GetBaseline(), 0);
+  EXPECT_GT(cf.GetCapHeight(), 0);
+  EXPECT_GT(cf.GetExpectedTextWidth(1), 0);
+}
+
 TEST_F(FontTest, LoadArial) {
   Font cf(kTestFontName, 16);
 #if defined(OS_MACOSX) || defined(OS_IOS)
diff --git a/ui/gfx/geometry/mojo/BUILD.gn b/ui/gfx/geometry/mojo/BUILD.gn
index 2d0e1efb..cac3d224 100644
--- a/ui/gfx/geometry/mojo/BUILD.gn
+++ b/ui/gfx/geometry/mojo/BUILD.gn
@@ -34,6 +34,7 @@
   deps = [
     ":test_interfaces",
     "//base",
+    "//base/test:test_support",
     "//mojo/public/cpp/bindings",
     "//testing/gtest",
     "//ui/gfx/geometry",
diff --git a/ui/gfx/geometry/mojo/geometry_struct_traits_unittest.cc b/ui/gfx/geometry/mojo/geometry_struct_traits_unittest.cc
index 4dd59db..d0be185 100644
--- a/ui/gfx/geometry/mojo/geometry_struct_traits_unittest.cc
+++ b/ui/gfx/geometry/mojo/geometry_struct_traits_unittest.cc
@@ -4,7 +4,7 @@
 
 #include <utility>
 
-#include "base/message_loop/message_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/mojo/geometry_traits_test_service.mojom.h"
@@ -78,7 +78,7 @@
     std::move(callback).Run(v);
   }
 
-  base::MessageLoop loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   mojo::BindingSet<GeometryTraitsTestService> traits_test_bindings_;
 
   DISALLOW_COPY_AND_ASSIGN(GeometryStructTraitsTest);
diff --git a/ui/gfx/range/mojo/BUILD.gn b/ui/gfx/range/mojo/BUILD.gn
index 106b6b5..403f212 100644
--- a/ui/gfx/range/mojo/BUILD.gn
+++ b/ui/gfx/range/mojo/BUILD.gn
@@ -32,6 +32,7 @@
   deps = [
     ":test_interfaces",
     "//base",
+    "//base/test:test_support",
     "//mojo/public/cpp/bindings",
     "//testing/gtest",
     "//ui/gfx/range",
diff --git a/ui/gfx/range/mojo/range_struct_traits_unittest.cc b/ui/gfx/range/mojo/range_struct_traits_unittest.cc
index 31705ca..d3b243f 100644
--- a/ui/gfx/range/mojo/range_struct_traits_unittest.cc
+++ b/ui/gfx/range/mojo/range_struct_traits_unittest.cc
@@ -4,7 +4,7 @@
 
 #include <utility>
 
-#include "base/message_loop/message_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/range/mojo/range_traits_test_service.mojom.h"
@@ -35,7 +35,7 @@
     std::move(callback).Run(p);
   }
 
-  base::MessageLoop loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   mojo::BindingSet<RangeTraitsTestService> traits_test_bindings_;
 
   DISALLOW_COPY_AND_ASSIGN(RangeStructTraitsTest);
diff --git a/ui/gfx/text_elider.cc b/ui/gfx/text_elider.cc
index 085fc3c..52d0d13 100644
--- a/ui/gfx/text_elider.cc
+++ b/ui/gfx/text_elider.cc
@@ -788,19 +788,6 @@
                               Typesetter::HARFBUZZ, lines);
 }
 
-#if defined(OS_MACOSX)
-int ElideRectangleTextForNativeUi(const base::string16& input,
-                                  const FontList& font_list,
-                                  float available_pixel_width,
-                                  int available_pixel_height,
-                                  WordWrapBehavior wrap_behavior,
-                                  std::vector<base::string16>* lines) {
-  return RectangleText::Elide(input, font_list, available_pixel_width,
-                              available_pixel_height, wrap_behavior,
-                              Typesetter::NATIVE, lines);
-}
-#endif
-
 base::string16 TruncateString(const base::string16& string,
                               size_t length,
                               BreakType break_type) {
diff --git a/ui/gfx/text_elider.h b/ui/gfx/text_elider.h
index 46ab51b..42b14a0 100644
--- a/ui/gfx/text_elider.h
+++ b/ui/gfx/text_elider.h
@@ -141,17 +141,6 @@
                                   WordWrapBehavior wrap_behavior,
                                   std::vector<base::string16>* lines);
 
-#if defined(OS_MACOSX)
-// As above, but uses the native platform typesetter (CoreText on Mac).
-GFX_EXPORT int ElideRectangleTextForNativeUi(
-    const base::string16& input,
-    const FontList& font_list,
-    float available_pixel_width,
-    int available_pixel_height,
-    WordWrapBehavior wrap_behavior,
-    std::vector<base::string16>* lines);
-#endif  // OS_MACOSX
-
 // Truncates |string| to |length| characters. This breaks the string according
 // to the specified |break_type|, which must be either WORD_BREAK or
 // CHARACTER_BREAK, and adds the horizontal ellipsis character (unicode
diff --git a/ui/gl/gl_surface_glx.cc b/ui/gl/gl_surface_glx.cc
index f649dd46..0aa6892 100644
--- a/ui/gl/gl_surface_glx.cc
+++ b/ui/gl/gl_surface_glx.cc
@@ -583,10 +583,10 @@
 
   XSetWindowAttributes swa = {
       .background_pixmap = 0,
-      .bit_gravity = NorthWestGravity,
-      .colormap = g_colormap,
       .background_pixel = 0,  // ARGB(0,0,0,0) for compositing WM
       .border_pixel = 0,
+      .bit_gravity = NorthWestGravity,
+      .colormap = g_colormap,
   };
   auto value_mask = CWBackPixmap | CWBitGravity | CWColormap | CWBorderPixel;
   if (ui::IsCompositingManagerPresent() &&
diff --git a/ui/ozone/common/linux/BUILD.gn b/ui/ozone/common/linux/BUILD.gn
index c808a51..7628646f 100644
--- a/ui/ozone/common/linux/BUILD.gn
+++ b/ui/ozone/common/linux/BUILD.gn
@@ -25,6 +25,8 @@
     "gbm_buffer.h",
     "gbm_device.h",
     "gbm_wrapper.cc",
+    "scoped_gbm_device.cc",
+    "scoped_gbm_device.h",
   ]
 
   deps = [
diff --git a/ui/ozone/common/linux/scoped_gbm_device.cc b/ui/ozone/common/linux/scoped_gbm_device.cc
new file mode 100644
index 0000000..21f00b9
--- /dev/null
+++ b/ui/ozone/common/linux/scoped_gbm_device.cc
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/common/linux/scoped_gbm_device.h"
+
+namespace ui {
+
+void GbmDeviceDeleter::operator()(gbm_device* device) {
+  if (device)
+    gbm_device_destroy(device);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/common/linux/scoped_gbm_device.h b/ui/ozone/common/linux/scoped_gbm_device.h
new file mode 100644
index 0000000..94c97c8
--- /dev/null
+++ b/ui/ozone/common/linux/scoped_gbm_device.h
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_COMMON_LINUX_SCOPED_GBM_DEVICE_H_
+#define UI_OZONE_COMMON_LINUX_SCOPED_GBM_DEVICE_H_
+
+#include <gbm.h>
+
+#include <memory>
+
+namespace ui {
+
+struct GbmDeviceDeleter {
+  void operator()(gbm_device* device);
+};
+
+using ScopedGbmDevice = std::unique_ptr<gbm_device, GbmDeviceDeleter>;
+
+}  // namespace ui
+
+#endif  // UI_OZONE_COMMON_LINUX_SCOPED_GBM_DEVICE_H_
diff --git a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
index 094709e..126be375 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
@@ -18,6 +18,7 @@
 #include "ui/ozone/common/egl_util.h"
 #include "ui/ozone/common/gl_ozone_egl.h"
 #include "ui/ozone/common/linux/drm_util_linux.h"
+#include "ui/ozone/common/linux/scoped_gbm_device.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/drm_thread_proxy.h"
 #include "ui/ozone/platform/drm/gpu/drm_window_proxy.h"
@@ -108,7 +109,7 @@
     if (!dev_path_file.IsValid())
       break;
 
-    gbm_device* device = gbm_create_device(dev_path_file.GetPlatformFile());
+    ScopedGbmDevice device(gbm_create_device(dev_path_file.GetPlatformFile()));
     if (!device) {
       LOG(ERROR) << "Couldn't create Gbm Device at " << dev_path.MaybeAsASCII();
       return supported_buffer_formats;
@@ -119,12 +120,11 @@
       if (base::ContainsValue(supported_buffer_formats, buffer_format))
         continue;
       if (gbm_device_is_format_supported(
-              device, GetFourCCFormatFromBufferFormat(buffer_format),
+              device.get(), GetFourCCFormatFromBufferFormat(buffer_format),
               GBM_BO_USE_TEXTURING)) {
         supported_buffer_formats.push_back(buffer_format);
       }
     }
-    gbm_device_destroy(device);
   }
   return supported_buffer_formats;
 }
diff --git a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
index c37289f9..63bfa30 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
+++ b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
@@ -17,8 +17,7 @@
 namespace ui {
 
 WaylandBufferManagerGpu::WaylandBufferManagerGpu()
-    : associated_binding_(this),
-      gpu_thread_runner_(base::ThreadTaskRunnerHandle::Get()) {}
+    : associated_binding_(this) {}
 
 WaylandBufferManagerGpu::~WaylandBufferManagerGpu() = default;
 
@@ -50,9 +49,9 @@
   // a buffer, and is able to call the OnSubmission for that specific buffer.
   if (surface) {
     // As long as mojo calls rerouted to the IO child thread, we have to reroute
-    // them back to the gpu main thread, where the original commit buffer call
-    // came from.
-    gpu_thread_runner_->PostTask(
+    // them back to the same thread, where the original commit buffer call came
+    // from.
+    commit_thread_runner_->PostTask(
         FROM_HERE,
         base::Bind(&WaylandSurfaceGpu::OnSubmission, base::Unretained(surface),
                    buffer_id, swap_result));
@@ -71,9 +70,9 @@
   // a buffer, and is able to call the OnPresentation for that specific buffer.
   if (surface) {
     // As long as mojo calls rerouted to the IO child thread, we have to reroute
-    // them back to the gpu main thread, where the original commit buffer call
-    // came from.
-    gpu_thread_runner_->PostTask(
+    // them back to the same thread, where the original commit buffer call came
+    // from.
+    commit_thread_runner_->PostTask(
         FROM_HERE, base::Bind(&WaylandSurfaceGpu::OnPresentation,
                               base::Unretained(surface), buffer_id, feedback));
   }
@@ -138,9 +137,11 @@
 void WaylandBufferManagerGpu::CommitBuffer(gfx::AcceleratedWidget widget,
                                            uint32_t buffer_id,
                                            const gfx::Rect& damage_region) {
-  DCHECK(gpu_thread_runner_ && gpu_thread_runner_->BelongsToCurrentThread());
   DCHECK(io_thread_runner_);
 
+  if (!commit_thread_runner_)
+    commit_thread_runner_ = base::ThreadTaskRunnerHandle::Get();
+
   // Do the mojo call on the IO child thread.
   io_thread_runner_->PostTask(
       FROM_HERE,
diff --git a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h
index deeb8d0..8743961 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h
+++ b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h
@@ -158,12 +158,13 @@
 
   std::map<gfx::AcceleratedWidget, WaylandSurfaceGpu*> widget_to_surface_map_;
 
-  // This task runner can be used to pass messages back to the GpuMainThread.
-  // For example, swap requests come from the GpuMainThread, but rerouted to the
-  // IOChildThread and then mojo calls happen. However, when the manager
-  // receives mojo calls, it has to reroute calls back to the same thread
-  // where the calls came from to ensure correct sequence.
-  scoped_refptr<base::SingleThreadTaskRunner> gpu_thread_runner_;
+  // This task runner can be used to pass messages back to the same thread,
+  // where the commit buffer request came from. For example, swap requests come
+  // from the GpuMainThread, but rerouted to the IOChildThread and then mojo
+  // calls happen. However, when the manager receives mojo calls, it has to
+  // reroute calls back to the same thread where the calls came from to ensure
+  // correct sequence.
+  scoped_refptr<base::SingleThreadTaskRunner> commit_thread_runner_;
 
   // A task runner, which is initialized in a multi-process mode. It is used to
   // ensure all the methods of this class are run on IOChildThread. This is
diff --git a/ui/resources/cursors/pan_middle_horizontal.cur b/ui/resources/cursors/pan_middle_horizontal.cur
new file mode 100644
index 0000000..62dd33f
--- /dev/null
+++ b/ui/resources/cursors/pan_middle_horizontal.cur
Binary files differ
diff --git a/ui/resources/cursors/pan_middle_vertical.cur b/ui/resources/cursors/pan_middle_vertical.cur
new file mode 100644
index 0000000..aec07af
--- /dev/null
+++ b/ui/resources/cursors/pan_middle_vertical.cur
Binary files differ
diff --git a/ui/resources/ui_unscaled_resources.grd b/ui/resources/ui_unscaled_resources.grd
index ee0b64b..0243529b 100644
--- a/ui/resources/ui_unscaled_resources.grd
+++ b/ui/resources/ui_unscaled_resources.grd
@@ -18,6 +18,8 @@
         <include name="IDC_HAND_GRABBING" file="cursors/hand_grabbing.cur" type="CURSOR" />
         <include name="IDC_PAN_EAST" file="cursors/pan_east.cur" type="CURSOR" />
         <include name="IDC_PAN_MIDDLE" file="cursors/pan_middle.cur" type="CURSOR" />
+        <include name="IDC_PAN_MIDDLE_HORIZONTAL" file="cursors/pan_middle_horizontal.cur" type="CURSOR" />
+        <include name="IDC_PAN_MIDDLE_VERTICAL" file="cursors/pan_middle_vertical.cur" type="CURSOR" />
         <include name="IDC_PAN_NORTH" file="cursors/pan_north.cur" type="CURSOR" />
         <include name="IDC_PAN_NORTH_EAST" file="cursors/pan_north_east.cur" type="CURSOR" />
         <include name="IDC_PAN_NORTH_WEST" file="cursors/pan_north_west.cur" type="CURSOR" />
diff --git a/ui/views/controls/button/image_button.cc b/ui/views/controls/button/image_button.cc
index 888cd9c..8616381d 100644
--- a/ui/views/controls/button/image_button.cc
+++ b/ui/views/controls/button/image_button.cc
@@ -55,8 +55,9 @@
   if (old_preferred_size != GetPreferredSize())
     PreferredSizeChanged();
 
-  if (state() == for_state)
-    SchedulePaint();
+  // Even if |for_state| isn't the current state this image could be painted;
+  // see |GetImageToPaint()|. So, always repaint.
+  SchedulePaint();
 }
 
 void ImageButton::SetBackgroundImage(SkColor color,
diff --git a/ui/views/controls/menu/menu_controller_unittest.cc b/ui/views/controls/menu/menu_controller_unittest.cc
index 270d7bc..be9e3026 100644
--- a/ui/views/controls/menu/menu_controller_unittest.cc
+++ b/ui/views/controls/menu/menu_controller_unittest.cc
@@ -59,15 +59,6 @@
 #include "ui/base/ui_base_features.h"
 #endif
 
-// TODO(crbug.com/961075): Fix memory leaks in tests and re-enable on LSAN.
-#ifdef LEAK_SANITIZER
-#define MAYBE_SetSelectionIndices_NestedButtons \
-  DISABLED_SetSelectionIndices_NestedButtons
-#else
-#define MAYBE_SetSelectionIndices_NestedButtons \
-  SetSelectionIndices_NestedButtons
-#endif
-
 namespace views {
 namespace test {
 
@@ -2265,13 +2256,7 @@
   EXPECT_EQ(5, data.GetIntAttribute(ax::mojom::IntAttribute::kSetSize));
 }
 
-TEST_F(MenuControllerTest, MAYBE_SetSelectionIndices_NestedButtons) {
-  class DummyButtonListener : public ButtonListener {
-   public:
-    ~DummyButtonListener() override = default;
-    void ButtonPressed(Button* sender, const ui::Event& event) override {}
-  };
-
+TEST_F(MenuControllerTest, SetSelectionIndices_NestedButtons) {
   MenuItemView* const item1 = menu_item()->GetSubmenu()->GetMenuItemAt(0);
   MenuItemView* const item2 = menu_item()->GetSubmenu()->GetMenuItemAt(1);
   MenuItemView* const item3 = menu_item()->GetSubmenu()->GetMenuItemAt(2);
@@ -2286,13 +2271,11 @@
   container_view->AddChildView(new Label());
 
   // Add two focusable buttons (buttons in menus are always focusable).
-  Button* const button1 =
-      new LabelButton(new DummyButtonListener(), base::string16());
+  Button* const button1 = new LabelButton(nullptr, base::string16());
   button1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
   button1->GetViewAccessibility().OverrideRole(ax::mojom::Role::kMenuItem);
   container_view->AddChildView(button1);
-  Button* const button2 =
-      new LabelButton(new DummyButtonListener(), base::string16());
+  Button* const button2 = new LabelButton(nullptr, base::string16());
   button2->GetViewAccessibility().OverrideRole(ax::mojom::Role::kMenuItem);
   button2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
   container_view->AddChildView(button2);
diff --git a/ui/views/controls/tree/tree_view.cc b/ui/views/controls/tree/tree_view.cc
index 8586b005..005ab7f 100644
--- a/ui/views/controls/tree/tree_view.cc
+++ b/ui/views/controls/tree/tree_view.cc
@@ -446,16 +446,16 @@
 
 void TreeView::TreeNodesAdded(TreeModel* model,
                               TreeModelNode* parent,
-                              int start,
-                              int count) {
+                              size_t start,
+                              size_t count) {
   InternalNode* parent_node =
       GetInternalNodeForModelNode(parent, DONT_CREATE_IF_NOT_LOADED);
   if (!parent_node || !parent_node->loaded_children())
     return;
   const auto& children = model_->GetChildren(parent);
-  for (int i = start; i < start + count; ++i) {
+  for (size_t i = start; i < start + count; ++i) {
     auto child = std::make_unique<InternalNode>();
-    ConfigureInternalNode(children[size_t{i}], child.get());
+    ConfigureInternalNode(children[i], child.get());
     parent_node->Add(std::move(child), i);
   }
   if (IsExpanded(parent))
@@ -464,14 +464,14 @@
 
 void TreeView::TreeNodesRemoved(TreeModel* model,
                                 TreeModelNode* parent,
-                                int start,
-                                int count) {
+                                size_t start,
+                                size_t count) {
   InternalNode* parent_node =
       GetInternalNodeForModelNode(parent, DONT_CREATE_IF_NOT_LOADED);
   if (!parent_node || !parent_node->loaded_children())
     return;
   bool reset_selection = false;
-  for (int i = 0; i < count; ++i) {
+  for (size_t i = 0; i < count; ++i) {
     InternalNode* child_removing = parent_node->GetChild(start);
     if (selected_node_ && selected_node_->HasAncestor(child_removing))
       reset_selection = true;
diff --git a/ui/views/controls/tree/tree_view.h b/ui/views/controls/tree/tree_view.h
index 649a244d..382b2b9 100644
--- a/ui/views/controls/tree/tree_view.h
+++ b/ui/views/controls/tree/tree_view.h
@@ -144,12 +144,12 @@
   // TreeModelObserver overrides:
   void TreeNodesAdded(ui::TreeModel* model,
                       ui::TreeModelNode* parent,
-                      int start,
-                      int count) override;
+                      size_t start,
+                      size_t count) override;
   void TreeNodesRemoved(ui::TreeModel* model,
                         ui::TreeModelNode* parent,
-                        int start,
-                        int count) override;
+                        size_t start,
+                        size_t count) override;
   void TreeNodeChanged(ui::TreeModel* model,
                        ui::TreeModelNode* model_node) override;
 
diff --git a/ui/views/controls/tree/tree_view_unittest.cc b/ui/views/controls/tree/tree_view_unittest.cc
index d37796e..aecde93 100644
--- a/ui/views/controls/tree/tree_view_unittest.cc
+++ b/ui/views/controls/tree/tree_view_unittest.cc
@@ -47,9 +47,7 @@
   }
 
  protected:
-  TestNode* Add(TestNode* parent,
-                int index,
-                const std::string& title);
+  TestNode* Add(TestNode* parent, size_t index, const std::string& title);
 
   std::string TreeViewContentsAsString();
 
@@ -77,7 +75,7 @@
 };
 
 TestNode* TreeViewTest::Add(TestNode* parent,
-                            int index,
+                            size_t index,
                             const std::string& title) {
   std::unique_ptr<TestNode> new_node = std::make_unique<TestNode>();
   new_node->SetTitle(ASCIIToUTF16(title));
diff --git a/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.html b/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.html
index f0020d1..7fea684da 100644
--- a/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.html
+++ b/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.html
@@ -79,7 +79,8 @@
             value="{{mountUrl_}}" items="[[discoveredShares_]]"
             placeholder="\\server\share"
             error-message-allowed
-            update-value-on-input autofocus>
+            update-value-on-input autofocus
+            show-loading="[[discoveryActive_]]">
         </cr-searchable-drop-down>
         <cr-input id="name" label="[[i18n('smbShareName')]]"
             value="{{mountName_}}" maxlength="64">
diff --git a/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.js b/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.js
index f23176f..0dc459a9 100644
--- a/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.js
+++ b/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.js
@@ -4,6 +4,9 @@
 
 /**
  * @fileoverview 'add-smb-share-dialog' is a component for adding an SMB Share.
+ *
+ * This component can only be used once to add an SMB share, and must be
+ * destroyed when finished, and re-created when shown again.
  */
 
 cr.define('smb_shares', function() {
@@ -80,6 +83,12 @@
     },
 
     /** @private */
+    discoveryActive_: {
+      type: Boolean,
+      value: true,
+    },
+
+    /** @private */
     isActiveDirectory_: {
       type: Boolean,
       value: function() {
@@ -169,11 +178,14 @@
   },
 
   /**
-   * @param {!Array<string>} shares
+   * @param {!Array<string>} newSharesDiscovered New shares that have been
+   * discovered since the last call.
+   * @param {boolean} done Whether share discovery has finished.
    * @private
    */
-  onSharesFound_: function(shares) {
-    this.discoveredShares_ = this.discoveredShares_.concat(shares);
+  onSharesFound_: function(newSharesDiscovered, done) {
+    this.discoveredShares_ = this.discoveredShares_.concat(newSharesDiscovered);
+    this.discoveryActive_ = !done;
   },
 
   /**